PackageManagerService is started by the SystemServer process, which scans specific directories in the system to look for APK format files, parses these files (to obtain AppInfo related information), and finally completes the installation. PKMS will parse the AndroidManifest.xml
file in the APK during the application installation process (to extract the four main components of Android), and this parsed information will be stored in the Android system for easy access at any time.
Main Functions#
- Scan
.apk
files and installsystem applications
,local applications
- Parse the AndroidManifest.xml manifest file, analyze the four main components, permissions, etc., and store them in PKMS for easy querying
- Manage local applications, including
install
,uninstall
,query APK information
, etc. - Allocate UID and GID for applications
Manage permissions through GID (Group ID), allowing certain functionalities only within a specific group
Usage#
We can obtain the PKMS service through the Context#getPackageManager()
method. PackageManager is an abstract class, and in practice, it is ApplicationPackageManager.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PackageManager packageManager = this.getBaseContext().getPackageManager();
}
Obtaining PKMS via ServiceManager#
ActivityThread obtains the IBinder of PKMS through ServiceManager. Here, IPackageManager is a class automatically generated through AIDL, and IPackageManager obtains the Java proxy class through asInterface
, which communicates with PKMS via this proxy class.
// ActivityThread.java
@UnsupportedAppUsage
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
// 1. Obtain the proxy of ServiceManager
// 2. Use the proxy to pass "package" to obtain the handle of PKMS
// 3. Return the proxy of PKMS
final IBinder b = ServiceManager.getService("package");
// Create AIDL proxy class
sPackageManager = IPackageManager.Stub.asInterface(b);
return sPackageManager;
}
SystemServer Initializes PKMS#
SystemServer starts the bootstrap service (startBootstrapServices): PKMS is started here.
// SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
... omitted parts
try {
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
} /* omitted catch */
... omitted parts
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t)
...
// Installer service
Installer installer = mSystemServiceManager.startService(Installer.class);
mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
...
try {
... watch dog
// Call the static main method of PackageManagerService
// @ Analyze main
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
mOnlyCore);
} ... watch dog
...
}
startBootstrapServices#
Start the Installer service, which will be used to install all applications.
// SystemServer.java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
... omitted parts
Installer installer = mSystemServiceManager.startService(Installer.class);
... omitted parts
}
VoldProperties#decrypt
checks whether the device is encrypted (reads the settings from the init.rc
file), mOnlyCore = true
means only core programs are running.
// SystemServer.java
private static final String ENCRYPTING_STATE = "trigger_restart_min_framework";
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
// Only run "core" apps if we're encrypting the device.
String cryptState = VoldProperties.decrypt().orElse("");
if (ENCRYPTING_STATE.equals(cryptState)) {
// Only run core programs
... log msg
mOnlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
... log msg
mOnlyCore = true;
}
...
}
Call the PKMS#main
method to create the PKMS object.
// SystemServer.java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
... omitted parts
try {
...
PackageManagerService.main(mSystemContext, installer,
domainVerificationService,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
mOnlyCore); // Assume passing false
} /* omitted finally */
... omitted parts
}
If the device is not encrypted, execute A/B OTA dexopting, OtaDexoptService#main
method.
// SystemServer.java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
if (!mOnlyCore) {
boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
false);
if (!disableOtaDexopt) {
... trace log
try {
... watch dog
OtaDexoptService.main(mSystemContext, mPackageManagerService);
} /* catch、finally */
}
}
...
}
After PKMS completes the initialization operation in the SystemServer#startBootstrapServices function
, it will perform subsequent operations in startOtherServices
(complete Dex optimization, systemReady).
Dex optimization is completed through PKMS#performFstrimIfNeeded
.
// SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
... omitted parts
try {
mPackageManagerService.performFstrimIfNeeded();
} ...
... omitted parts
}
PKMS is ready to call systemReady and load preset permissions.
PKMS Analysis#
PKMS main
#
The PackageManagerService#main
method can create a PackageManagerService object, and PKMS is added to ServiceManager through package and package_native.
// PackageManagerService.java
public static PackageManagerService main(Context context, Installer installer,
@NonNull DomainVerificationService domainVerificationService, boolean factoryTest,
boolean onlyCore) {
// Check package compilation related system properties
PackageManagerServiceCompilerMapping.checkProperties();
...
Injector injector = new Injector(
context, lock, installer, installLock,
new PackageAbiHelperImpl(),
backgroundHandler,
SYSTEM_PARTITIONS,
(i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
(i, pm) -> PermissionManagerService.create(context,
i.getSystemConfig().getAvailableFeatures()),
(i, pm) -> new UserManagerService(context, pm,
new UserDataPreparer(installer, installLock, context, onlyCore),
lock),
(i, pm) -> new Settings(Environment.getDataDirectory(),
RuntimePermissionsPersistence.createInstance(),
i.getPermissionManagerServiceInternal(),
domainVerificationService, lock),
(i, pm) -> AppsFilter.create(pm.mPmInternal, i),
(i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),
(i, pm) -> SystemConfig.getInstance(),
(i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(),
i.getContext(), "*dexopt*"),
(i, pm) -> new DexManager(i.getContext(), pm, i.getPackageDexOptimizer(),
i.getInstaller(), i.getInstallLock()),
(i, pm) -> new ArtManagerService(i.getContext(), pm, i.getInstaller(),
i.getInstallLock()),
(i, pm) -> ApexManager.getInstance(),
(i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
(i, pm) -> (IncrementalManager)
i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
(i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),
() -> LocalServices.getService(UserManagerInternal.class)),
(i, pm) -> new DisplayMetrics(),
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
i.getDisplayMetrics(), pm.mCacheDir,
pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */,
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
i.getDisplayMetrics(), null,
pm.mPackageParserCallback) /* scanningPackageParserProducer */,
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, false, i.getDisplayMetrics(),
null, pm.mPackageParserCallback) /* preparingPackageParserProducer */,
// Prepare a supplier of package parser for the staging manager to parse apex file
// during the staging installation.
(i, pm) -> new PackageInstallerService(
i.getContext(), pm, i::getScanningPackageParser),
(i, pm, cn) -> new InstantAppResolverConnection(
i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),
(i, pm) -> new ModuleInfoProvider(i.getContext(), pm),
(i, pm) -> LegacyPermissionManagerService.create(i.getContext()),
(i, pm) -> domainVerificationService,
(i, pm) -> {
// Responsible for apk installation and uninstallation
HandlerThread thread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
thread.start();
return pm.new PackageHandler(thread.getLooper());
},
new DefaultSystemWrapper(),
LocalServices::getService,
context::getSystemService);
// Call the constructor of PackageManagerService
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
Build.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT,
Build.VERSION.INCREMENTAL);
...
// Add to ServiceManager through package and package_native
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
// PackageManagerService.java
public static class Injector {
@VisibleForTesting(visibility = Visibility.PRIVATE)
interface Producer<T> {
/** Produce an instance of type {@link T} */
T produce(Injector injector, PackageManagerService packageManager);
}
...
@VisibleForTesting(visibility = Visibility.PRIVATE)
static class Singleton<T> {
private final Producer<T> mProducer;
private volatile T mInstance = null;
Singleton(Producer<T> producer) {
this.mProducer = producer;
}
T get(Injector injector, PackageManagerService packageManagerService) {
if (mInstance == null) {
mInstance = mProducer.produce(injector, packageManagerService);
}
return mInstance;
}
}
private final Singleton<PermissionManagerServiceInternal> mPermissionManagerServiceProducer;
private final Singleton<UserManagerService> mUserManagerProducer;
private final Singleton<Settings> mSettingsProducer;
private final Singleton<AppsFilter> mAppsFilterProducer;
...
private final Singleton<DisplayMetrics> mDisplayMetricsProducer;
Injector(Context context, PackageManagerTracedLock lock, Installer installer,
Object installLock, PackageAbiHelper abiHelper,
Handler backgroundHandler,
List<ScanPartition> systemPartitions,
...
Producer<PermissionManagerServiceInternal> permissionManagerServiceProducer,
Producer<UserManagerService> userManagerProducer,
Producer<Settings> settingsProducer,
Producer<DisplayMetrics> displayMetricsProducer,
) {
...
mPermissionManagerServiceProducer = new Singleton<>(permissionManagerServiceProducer);
mUserManagerProducer = new Singleton<>(userManagerProducer);
mSettingsProducer = new Singleton<>(settingsProducer);
...
mDisplayMetricsProducer = new Singleton<>(displayMetricsProducer);
}
public PermissionManagerServiceInternal getPermissionManagerServiceInternal() {
return mPermissionManagerServiceProducer.get(this, mPackageManager);
}
public Context getContext() {
return mContext;
}
public Settings getSettings() {
return mSettingsProducer.get(this, mPackageManager);
}
public DisplayMetrics getDisplayMetrics() {
return mDisplayMetricsProducer.get(this, mPackageManager);
}
...
}
PKMS Constructor#
In the constructor of PackageManagerService, it can be found that PKMS is constructed in 5 stages (written in EventLog).
Stage Flag | Description | Other |
---|---|---|
BOOT_PROGRESS_PMS_START | Initial stage, obtains various singletons through the Inject inner class | DisplayMetrics, Installer, mPermissionManager, mSettings, mPackageDexOptimizer |
BOOT_PROGRESS_PMS_SYSTEM_SCAN_START | Scanning the system | |
BOOT_PROGRESS_PMS_DATA_SCAN_START | Scanning the data partition | |
BOOT_PROGRESS_PMS_SCAN_END | Scanning ends | |
BOOT_PROGRESS_PMS_READY | Preparation stage |
PKMS uses multiple threads, and there are two important locks to note:
Lock Name | Description | Supplement |
---|---|---|
mPackages (small lock) | Protects all details, states, and updates of parsed Packages in memory | It is safe to obtain mPackages while holding mInstallLock |
mInstallLock (large lock) | Protects all locks during APK installation, usually related to application APP reloading (single-threaded, involves heavy IO operations) | This lock should not be obtained while acquiring mPackages |
Stage One - BOOT_PROGRESS_PMS_START#
The focus of the first stage is the Settings#readLPw method, which reads files in the
/data/system
directory.
BOOT_PROGRESS_PMS_START: Obtains DisplayMetrics
(resolution), Installer
(installation), PermissionManager
(permission management), mSettings
(stores installation information, clears non-existent applications), PackageDexOptimizer
(Dex optimization)... for details, see comments.
// PackageManagerService.java
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest, final String buildFingerprint, final boolean isEngBuild, final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
...
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
mOnlyCore = onlyCore;
mMetrics = injector.getDisplayMetrics();
mInstaller = injector.getInstaller();
mUserManager = injector.getUserManagerService(); // Multi-user management
// Permission management service
mPermissionManager = injector.getPermissionManagerServiceInternal();
// Involves 'data/system/' packages.xml
// packages-backup.xml
// packages.list
// packages-stopped.xml
// packages-stopped-backup.xml files
mSettings = injector.getSettings();
...
// Add sharedUserLPw for system, phone, log, nfc, bluetooth, shell, se,
// networkstack, uwb to mSettings
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.se", SE_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
...
// Handle dex optimization (DexOpt optimization)
mPackageDexOptimizer = injector.getPackageDexOptimizer();
mDexManager = injector.getDexManager();
// Art virtual machine management
mArtManagerService = injector.getArtManagerService();
...
// Create /data/app folder
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
// Lock needed for APK installation, protecting all access to installed
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
// Start PackageManager Thread, responsible for apk installation and uninstallation
mHandler = injector.getHandler();
// Process logging Handler
mProcessLoggingHandler = new ProcessLoggingHandler();
// Monitor whether PackageManager Thread times out after 10 minutes
Watchdog.getInstance().addThread(mHandler,
// WATCHDOG_TIMEOUT => 10 minutes
WATCHDOG_TIMEOUT);
...
// Read installation-related SELinux policies
SELinuxMMAC.readInstallPolicy();
// Read and parse xml files in /data/system
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
t.traceEnd();
}
}
}
The mSettings.addSharedUserLPw
method adds SharedUserId
to Settings; generally, inter-process data cannot be shared, but having a common SharedUserId
allows data sharing.
mPackageDexOptimizer
is the tool for Dex optimization.- mHandler binds the message queue of the background ServiceThread, and PKMS uses it to drive APK copying & installation, and this Handler will be monitored by WatchDog (to prevent excessive time consumption).
PKMD will access several important folders.
Access | Folder |
---|---|
File(Environment.getDataDirectory(), “app”) | /data/app |
File(Environment.getDataDirectory(), “app-lib”) | /data/app-lib |
Settings Creation#
Let's review the origin of the Settings object: The PKMS's Settings object will be provided by the PKMS#Inject
class.
// PackageManager
public static class Injector {
private final Singleton<Settings> mSettingsProducer;
Injector(/* omitted parameters */) {
...
mSettingsProducer = new Singleton<>(settingsProducer);
}
public Settings getSettings() {
return mSettingsProducer.get(this, mPackageManager);
}
}
public static PackageManagerService main(Context context, Installer installer,
@NonNull DomainVerificationService domainVerificationService, boolean factoryTest,
boolean onlyCore) {
Injector injector = new Injector(
context, lock, installer, installLock, new PackageAbiHelperImpl(),
backgroundHandler,
SYSTEM_PARTITIONS,
...
(i, pm) -> new Settings(Environment.getDataDirectory(),
RuntimePermissionsPersistence.createInstance(),
i.getPermissionManagerServiceInternal(),
domainVerificationService,
lock),
);
}
Settings Class constructor creates the required files & folders and sets their permissions.
// Settings.java
Settings(File dataDir, // The passed dataDir is /data
RuntimePermissionsPersistence runtimePermissionsPersistence,
LegacyPermissionDataProvider permissionDataProvider,
@NonNull DomainVerificationManagerInternal domainVerificationManager,
@NonNull PackageManagerTracedLock lock) {
...
// Create /data/system directory
mSystemDir = new File(dataDir, "system");//'/data/system'
mSystemDir.mkdirs(); // Create /data/system
// Folder permissions 775
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
// Create /data/system/packages.xml
mSettingsFilename = new File(mSystemDir, "packages.xml");
// Create /data/system/packages-backup.xml
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
// Create data/system/packages.list
mPackageListFilename = new File(mSystemDir, "packages.list");
// Set chmod 0640
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
final File kernelDir = new File("/config/sdcardfs");
mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
// Deprecated: Needed for migration
// Create data/system/packages-stopped.list
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
// Create data/system/packages-stopped-backup.list
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
...
}
Settings - readLPw Read Package Files#
mSettings#readLPw() function: Reads files in the /data/system
directory and saves them into the Settings object, and the files are divided into the following types (highlighting several important ones).
Filename | Description | Other |
---|---|---|
packages.xml | Installed app information | PKMS will create this file after scanning the directory files, and it will be updated during system operations such as install, uninstall, update |
packages-backup.xml | Backup information of installed apps | Prevents unexpected issues during installation when the device shuts down |
packages.list | Information of all installed apps (non-system APP) | Describes all non-system APK information, and this file will be modified when there are changes to third-party apps |
packages-stopped.xml | Information of forcibly stopped apps | When a user forcibly stops an application, the system will record the related information of that application in this file |
packages-stopped-backup.xml | Backup information of forcibly stopped apps | |
mSettings#readLPw() function: First scans the packages.xml , packages-backup.xml files, and the functions related to Linux UID are as follows. | ||
“package” tag: Uses the readPackageLPw function to read the last assigned Linux UID. | ||
“shared-user” tag: Uses the readSharedUserLPw function to read the last shared shared ID of the application. |
// Settings.java
boolean readLPw(@NonNull List<UserInfo> users) {
FileInputStream str = null;
// If there is packages-backup.xml, obtain the backup IO stream
if (mBackupSettingsFilename.exists()) {
try {
str = new FileInputStream(mBackupSettingsFilename);
...
} /* omitted catch */
}
try {
if (str == null) {
...
// If no backup, use packages.xml
str = new FileInputStream(mSettingsFilename);
}
// Parse xml
final TypedXmlPullParser parser = Xml.resolvePullParser(str);
int type;
// Adjust xml index position
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
;
}
// Parse format, check if xml format is correct
if (type != XmlPullParser.START_TAG) {
... err log
return false;
}
int outerDepth = parser.getDepth();
// Start analyzing xml
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
// Parse node name
String tagName = parser.getName();
if (tagName.equals("package")) {
// Read the last assigned Linux UID
readPackageLPw(parser, users);
} else if (tagName.equals("permissions")) {
mPermissions.readPermissions(parser);
} else if (tagName.equals("permission-trees")) {
mPermissions.readPermissionTrees(parser);
} else if (tagName.equals("shared-user")) {
// Read the last shared GID of the application
readSharedUserLPw(parser, users);
} ... omitted parts else if
else {
... log
XmlUtils.skipCurrentTag(parser);
}
}
str.close();
} /* omitted catch, finally */
...
return true;
}
readPackageLPw Assigning Independent Linux ID to Applications#
Next, we focus on the three elements in the “package” tag.
Element Name | Function | Supplement |
---|---|---|
name | Application package name | Must have a package name |
userId | The independent UID last assigned to this application on Linux | |
sharedUserId | This application does not have an independent UID, sharing UID with other applications | After adding this application to the mPendingPackages list, it is determined. |
// Setting.java
public static final String ATTR_NAME = "name";
private final WatchedArrayList<PackageSetting> mPendingPackages = new WatchedArrayList<>();
private void readPackageLPw(TypedXmlPullParser parser, List<UserInfo> users,
ArrayMap<String, Long> originalFirstInstallTimes)
throws XmlPullParserException, IOException {
String name = null;
int userId = 0;
int sharedUserAppId = 0;
try {
// App package name
name = parser.getAttributeValue(null, ATTR_NAME);
...
userId = parser.getAttributeInt(null, "userId", 0);
sharedUserAppId = parser.getAttributeInt(null, "sharedUserId", 0);
if (name == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <package> has no name at "
+ parser.getPositionDescription());
} else if (codePathStr == null) {
...
} else if (userId > 0) { // Already assigned an independent ID
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString,
cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags,
null /* usesSdkLibraries */, null /* usesSdkLibraryVersions */,
null /* usesStaticLibraries */, null /* usesStaticLibraryVersions */,
null /* mimeGroups */, domainSetId);
...
} else if (sharedUserAppId != 0) { // Not assigned an independent ID
if (sharedUserAppId > 0) {
// Save the application information
packageSetting = new PackageSetting(name.intern(), realName,
new File(codePathStr), legacyNativeLibraryPathStr,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
versionCode, pkgFlags, pkgPrivateFlags, sharedUserAppId,
null /* usesSdkLibraries */,
null /* usesSdkLibrariesVersions */,
null /* usesStaticLibraries */,
null /* usesStaticLibraryVersions */,
null /* mimeGroups */, domainSetId);
...
// Add to `mPendingPackages` list
mPendingPackages.add(packageSetting);
...
} else {
...
}
} else {
...
}
} /* omitted catch */
}
addPackageLPw
function: In PKMS, each application exists as a PackageSetting
object and is stored in a HashMap with the package name as the key.
// Setting.java
// Store all application information
final WatchedArrayMap<String, PackageSetting> mPackages;
private final AppIdSettingMap mAppIds;
PackageSetting addPackageLPw(String name, String realName, File codePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString, int uid,
... /* omitted parts */) {
// Obtain PackageSetting by name
PackageSetting p = mPackages.get(name);
if (p != null) {
// Check uid
if (p.getAppId() == uid) {
// Equal means already assigned an independent UID
return p;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate package, keeping first: " + name);
return null;
}
p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
... /* omitted parts */);
// Set UID for this PackageSetting
p.setAppId(uid);
// Register this ID to the system
if (mAppIds.registerExistingAppId(uid, p, name)) {
mPackages.put(name, p);
return p;
}
return null;
}
registerExistingAppId function: Already assigned applications will retrieve null from mNonSystemSettings
.
// Process.java
public static final int FIRST_APPLICATION_UID = 10000;
// ------------------------------------------------------------
// AppIdSettingMap.java
private final WatchedArrayList<SettingBase> mNonSystemSettings;
public boolean registerExistingAppId(int appId, SettingBase setting, Object name) {
// Greater than FIRST_APPLICATION_UID indicates an independent application
if (appId >= Process.FIRST_APPLICATION_UID) {
int size = mNonSystemSettings.size();
final int index = appId - Process.FIRST_APPLICATION_UID;
// Fill the array until our index becomes valid
while (index >= size) {
mNonSystemSettings.add(null);
size++;
}
// Already assigned will retrieve null
if (mNonSystemSettings.get(index) != null) {
// Duplicate UID
...err msg
return false;
}
mNonSystemSettings.set(index, setting);
} else {
// Less than FIRST_APPLICATION_UID indicates a shared application
...
}
return true;
}
readPackageLPw Assigning Shared Linux ID to Applications#
Next, we focus on the three elements in the “shared-user” tag.
Element Name | Function | Supplement |
---|---|---|
name | Describes the name of a shared Linux user | |
userId | Describes the ID of a shared Linux user | |
system | Describes whether this ID is system type or user type |
// Setting.java
private void readSharedUserLPw(TypedXmlPullParser parser, List<UserInfo> users)
throws XmlPullParserException, IOException {
String name = null;
int pkgFlags = 0;
int pkgPrivateFlags = 0;
SharedUserSetting su = null;
{
name = parser.getAttributeValue(null, ATTR_NAME);
int userId = parser.getAttributeInt(null, "userId", 0);
if (parser.getAttributeBoolean(null, "system", false)) {
pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
}
if (name == null) {
...
} else if (userId == 0) {
...
} else {
if ((su = addSharedUserLPw(name.intern(), userId, pkgFlags, pkgPrivateFlags))
== null)
...
}
}
}
...
}
Setting#addSharedUserLPw
function: Uses SharedUserSetting
to describe a shared application; after confirming the ID, it can be added to the mSharedUsers
list.
// Setting.java
final WatchedArrayMap<String, SharedUserSetting> mSharedUsers
= new WatchedArrayMap<>();
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
if (s.mAppId == uid) {
return s;
}
...err
return null;
}
// Create corresponding object
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
// Set ID
s.mAppId = uid;
// Try to register
if (mAppIds.registerExistingAppId(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
}
return null;
}
registerExistingAppId function: Already assigned applications will retrieve null from mNonSystemSettings
.
// Process.java
public static final int FIRST_APPLICATION_UID = 10000;
// ------------------------------------------------------------
// AppIdSettingMap.java
private final WatchedArrayList<SettingBase> mNonSystemSettings;
public boolean registerExistingAppId(int appId, SettingBase setting, Object name) {
// Greater than FIRST_APPLICATION_UID indicates an independent application
if (appId >= Process.FIRST_APPLICATION_UID) {
...
} else {
// Less than FIRST_APPLICATION_UID indicates a shared application
if (mSystemSettings.get(appId) != null) {
...err
return false;
}
mSystemSettings.put(appId, setting);
}
return true;
}
Returning to the readLPw
function: After analyzing the “shared-user”
and “package”
, we can obtain the relevant information of shared and independent applications, and then handle the shared application ID.
// Settings.java
private final AppIdSettingMap mAppIds;
boolean readLPw(@NonNull List<UserInfo> users) {
...
final int N = mPendingPackages.size();
for (int i = 0; i < N; i++) {
final PackageSetting p = mPendingPackages.get(i);
final int sharedUserAppId = p.getSharedUserAppId();
// Shared ID must be greater than 0
if (sharedUserAppId <= 0) {
continue;
}
final Object idObj = getSettingLPr(sharedUserAppId);
if (idObj instanceof SharedUserSetting) {
final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
addPackageSettingLPw(p, sharedUser);
} else if (idObj != null) {
...
} else {
...
}
}
mPendingPackages.clear();
return true;
}
public SettingBase getSettingLPr(int appId) {
return mAppIds.getSetting(appId);
}
void addPackageSettingLPw(PackageSetting p, SharedUserSetting sharedUser) {
mPackages.put(p.getPackageName(), p);
// If it is a shared application, further judgment is needed
if (sharedUser != null) {
SharedUserSetting existingSharedUserSetting = getSharedUserSettingLPr(p);
if (existingSharedUserSetting != null && existingSharedUserSetting != sharedUser) {
...err msg
sharedUser.removePackage(p);
} else if (p.getAppId() != sharedUser.mAppId) {
...err msg
}
sharedUser.addPackage(p);
p.setSharedUserAppId(sharedUser.mAppId);
// Determine the final ID
p.setAppId(sharedUser.mAppId);
}
Object userIdPs = getSettingLPr(p.getAppId());
if (sharedUser == null) {
if (userIdPs != null && userIdPs != p) {
mAppIds.replaceSetting(p.getAppId(), p);
}
} else {
if (userIdPs != null && userIdPs != sharedUser) {
mAppIds.replaceSetting(p.getAppId(), sharedUser);
}
}
}
Stage Two - BOOT_PROGRESS_PMS_SYSTEM_SCAN_START#
BOOT_PROGRESS_PMS_SYSTEM_SCAN_START: The system scanning stage, which includes directories such as (system
, vendor
, product
, odm
, oem
, etc.) and this step is still executed within two locks.
scanDirTracedLI method: This method scans APK packages, scanning the internal APK files of the passed path, and then further analyzes through scanDirTracedLI.
// PackageManagerService.java
// Key is application package name, Value is application package
final ArrayMap<String, PackageParser.Package> mPackages =
new ArrayMap<String, PackageParser.Package>();
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
...
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
...
// Record start time
long startTime = SystemClock.uptimeMillis();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);
// Obtain environment variables BOOTCLASSPATH,
// SYSTEMSERVERCLASSPATH from init.rc
final String bootClassPath =
System.getenv("BOOTCLASSPATH");
final String systemServerClassPath =
System.getenv("SYSTEMSERVERCLASSPATH");
// Get /system/framework directory
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
// Obtain internal version
final VersionInfo ver = mSettings.getInternalVersion();
// Check if an update is needed
mIsUpgrade =
!buildFingerprint.equals(ver.fingerprint);
// For Android M upgraded versions, system application permissions must change from installation requests to runtime requests
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
// For Android N upgraded versions, package extraction must be handled as if it were the first startup, as no available analysis data exists
mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;
...
// Prepare the cache for parsing packages
mCacheDir = preparePackageParserCache(mIsEngBuild);
// Set flag to not change apk path when scanning installation folders
int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
// Collect vendor/product/system_ext overlay packages.
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getOverlayFolder() == null) {
continue;
}
// Scan overlay... etc. folders for apk
scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
// Scan apk in the framework folder
scanDirTracedLI(frameworkDir, systemParseFlags,
systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
packageParser, executorService);
// Check if the installation package is android
if (!mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
}
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getPrivAppFolder() != null) {
// Scan priv-app folder
scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
packageParser, executorService);
}
// Scan /system/app folder
scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
...
if (!mOnlyCore) {
// do this first before mucking with mPackages for the "expecting better" case
final int numPackages = mPackages.size();
for (int index = 0; index < numPackages; index++) {
final AndroidPackage pkg = mPackages.valueAt(index);
if (pkg.isStub()) {
stubSystemApps.add(pkg.getPackageName());
}
}
// Reverse scan all applications
for (int index = packageSettings.size() - 1; index >= 0; index--) {
final PackageSetting ps = packageSettings.valueAt(index);
// If the package has FLAG_SYSTEM, ignore
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
/*
* If the package is scanned, it's not erased.
*/
final AndroidPackage scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
// If in disabled packages list, add to
// via OTA, then remove it
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
...
// Remove the PackageSetting of the system App from PKMS
removePackageLI(scannedPkg, true);
// Add the upgrade package path to the mExpectingBetter list
mExpectingBetter.put(ps.name, ps.getPath());
}
continue;
}
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
logCriticalInfo(Log.WARN, "System package " + ps.name
+ " no longer exists; its data will be wiped");
// The system APP will not exist, remove the data of this APP
removePackageDataLIF(ps, userIds, null, 0, false);
} else {
// We still have a disabled system package, but it still might have
// been removed. Check if the code path still exists and check there's
// still a package. The latter can happen if an OTA keeps the same
// code path, but changes the package name.
final PackageSetting disabledPs =
mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
|| disabledPs.pkg == null) {
// The system APP is in the isDisabledSystemPackage, and no upgrade package is found
possiblyDeletedUpdatedSystemApps.add(ps.name);
} else {
// We're expecting that the system app should remain disabled, but add
// it to expecting better to recover in case the data version cannot
// be scanned.
mExpectingBetter.put(disabledPs.name, disabledPs.getPath());
}
}
}
}
...
}
}
}
Scanning Upgraded System APP - System Folder#
At this stage (BOOT_PROGRESS_PMS_SYSTEM_SCAN_START
), the main task is to scan the /system
folder: how the files in this directory are organized.
The Android system architecture is divided into application layer, application framework layer, system runtime layer, hardware abstraction layer, and kernel layer.
System Internal Folders | Content | Supplement |
---|---|---|
app | System apps | Includes built-in Google and manufacturer apps |
framework | Application framework jar packages | Mainly stores jar packages, vdex |
priv-app | Privileged apps | |
lib | Stores dynamic so files | |
fonts | System fonts | Mostly ttf files |
media | System sounds | Such as ringtones, notifications, system boot animations |
The second purpose is to scan system files and process them, with the focus on ==OTA upgrades==. The system will mark upgradable system applications as DisabledSystemPackage, and there are three conditions:
System APP has an update: The system App's PackageSetting is removed from PKMS through removePackageLI and added to the mExpectingBetter list.
System APP is removed: The system APP data is removed through removePackageDataLIF.
No upgrade package: The system application is a DisabledSystemPackage, but no upgrade package is found, indicating that the system upgrade package may have been deleted.
Stage Three - BOOT_PROGRESS_PMS_DATA_SCAN_START#
BOOT_PROGRESS_PMS_DATA_SCAN_START: Mainly scans the /data
folder.
Scanning processes, updating application information in the /data
directory (also promptly removing unnecessary data).
Processing the possiblyDeletedUpdatedSystemApps list left over from the previous step.
- If the Package cannot be obtained from mPackage, the APP will be removed.
- If the Package can be obtained but is not a system APP, the following actions will be taken:
- Remove System permissions
- Remove Package
- Rescan the APK of the Package path.
Scanning the mExpectingBetter list: - Obtain the path of the system APP upgrade package and perform the following actions:
- Set the scanning parsing parameters based on the directory of the system APP.
- Set the PackageName in
mSetting#mPackages
(removeDisabledSystemPackageLPw method). - Clear the mExpectingBetter list.
// PackageManagerService.java
private final File mAppInstallDir;
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
...
// Obtain /data/app folder
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
...
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
// Scan /data/app folder
scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
packageParser, executorService);
}
// Close Parser stream (parsing Package xml stream)
packageParser.close();
// Close all tasks of Executor
List<Runnable> unfinishedTasks = executorService.shutdownNow();
if (!unfinishedTasks.isEmpty()) {
throw new IllegalStateException("Not all tasks finished before calling close: "
+ unfinishedTasks);
}
if (!mOnlyCore) {
// 2. Scan the APP packages left over from the previous step (in reverse order)
for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
final AndroidPackage pkg = mPackages.get(packageName);
final String msg;
// Remove from the disabled system list (mDisabledSystemPackage)
mSettings.removeDisabledSystemPackageLPw(packageName);
if (pkg == null) {
// Should find the upgrade package, but did not find it (remove this APP)
msg = "Updated system package " + packageName
+ " no longer exists; removing its data";
// (Actually not removed immediately)
} else {
// If pkg is obtained, it means this APP exists in Data,
// not a System app, remove system permissions
msg = "Updated system package " + packageName
+ " no longer exists; rescanning package on data";
// Below, delete and rescan the data package
removePackageLI(pkg, true); // Delete
try {
final File codePath = new File(pkg.getPath());
// Rescan
scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ e.getMessage());
}
}
// If we still have a package setting [i.e., it is known from previous scans]
// but we do not have that Pkg [i.e., an error occurred while scanning it from /data]
// thoroughly remove package data.
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null && mPackages.get(packageName) == null) {
// Remove erroneous package data in data
removePackageDataLIF(ps, userIds, null, 0, false);
}
logCriticalInfo(Log.WARN, msg);
}
// 3. Iterate through the mExpectingBetter list
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
if (!mPackages.containsKey(packageName)) {
// Obtain the path of the system APP upgrade package
final File scanFile = mExpectingBetter.valueAt(i);
for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i1);
// Set scanning parsing parameters based on the directory of the system APP
if (partition.containsPrivApp(scanFile)) {
reparseFlags = systemParseFlags;
// Set scanning parameters
rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
| partition.scanFlag;
break;
}
if (partition.containsApp(scanFile)) {
reparseFlags = systemParseFlags;
rescanFlags = systemScanFlags | partition.scanFlag;
break;
}
}
if (rescanFlags == 0) {
Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
continue;
}
// Set the package settings data corresponding to PackageName (PackagesSetting)
// to mSetting's mPackages
mSettings.enableSystemPackageLPw(packageName);
try {
// Scan the upgrade package of the system APP
final AndroidPackage newPkg = scanPackageTracedLI(
scanFile, reparseFlags, rescanFlags, 0, null);
// We rescanned a stub, add it to the list of stubbed system packages
if (newPkg.isStub()) {
stubSystemApps.add(packageName);
}
} /* omitted catch */
}
}
// Decompress & install root system applications
// This operation must ensure to execute last, to ensure all stubs are replaced or disabled
// Stub
installSystemStubPackages(stubSystemApps, scanFlags);
...
}
// Clear the upgrade list
mExpectingBetter.clear();
// Obtain Storage manager package name
mStorageManagerPackage = getStorageManagerPackageName();
// Resolve protected action filters, only allowing setup wizard to have the highest permissions for these actions
mSetupWizardPackage = getSetupWizardPackageNameImpl();
...
// Read and update the last usage time of the packages to be retained
mPackageUsage.read(packageSettings);
mCompilerStats.read();
}
}
scanDirTracedLI - Analyze Data Directory Contents#
The /data
folder can be considered the Data partition, which mainly serves two purposes:
Storing all users' personal data.
Storing all users' configuration files.
Next, let's look at the subdirectories under the /data directory and what data they hold.
/data Subdirectory | Meaning |
---|---|
app | Stores the apps installed by the device itself |
data | Stores the data of all installed apps, including System APP data |
app-private | APP's private storage space |
app-lib | Stores all APP's JNI Lib |
system | Stores system configuration files |
anr | Used to store the traces.text file generated by the system when ANR occurs |
Stage Four - BOOT_PROGRESS_PMS_SCAN_END#
BOOT_PROGRESS_PMS_SCAN_END stage: After the OAT upgrade, the first startup, clears unnecessary cached data, permissions, and updates the package.xml
file (packages.xml is used to store information about all APPs).
When the SDK version changes, permissions are updated again.
After the first startup following an OTA upgrade, unnecessary caches are cleared.
OTA stands for Over-the-Air Technology
, which refers to the technology that manages the mobile phone system and applications through mobile networks.
After permission updates are completed, related data is cleaned up.
The contents of mSetting are saved and the package.xml file is updated so that the new Setting will be read when PKMS is created in the future.
// PackageManagerService.java
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
...
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
...
// Enter the fourth stage
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
// Permission reset
mPermissionManager.onStorageVolumeMounted(
StorageManager.UUID_PRIVATE_INTERNAL, mIsUpgrade);
ver.sdkVersion = mSdkVersion;
// If it is the first startup or upgraded from pre-M, and it is a normal startup, we need to initialize User-defined preferred applications
if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(user.id);
}
}
// Prepare storage space for the system user during startup
// Because services like SettingsProvider and SystemUI cannot wait for user startup
final int storageFlags;
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
storageFlags = StorageManager.FLAG_STORAGE_DE;
} else {
storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
}
...
// On the first startup after a successful OTA upgrade, clear unnecessary caches but do not clear application configuration files
if (mIsUpgrade && !mOnlyCore) {
Slog.i(TAG, "Build fingerprint changed; clearing code caches");
for (int i = 0; i < packageSettings.size(); i++) {
final PackageSetting ps = packageSettings.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
// No applications will start at this time, so freezing is unnecessary
clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY
| Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
}
}
ver.fingerprint = Build.FINGERPRINT;
}
// Hide their icons in the Launcher (non-system applications before Android Q)
if (!mOnlyCore && mIsPreQUpgrade) {
Slog.i(TAG, "Whitelisting all existing apps to hide their icons");
int size = packageSettings.size();
for (int i = 0; i < size; i++) {
final PackageSetting ps = packageSettings.valueAt(i);
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
continue;
}
ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME,
UserHandle.USER_SYSTEM);
}
}
// Clear only after permissions and other defaults have been updated
mPromoteSystemApps = false;
// All changes are completed during scanning
ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
// Update package.xml
t.traceBegin("write settings");
writeSettingsLPrTEMP();
t.traceEnd();
...
}
}
}
Stage Five - BOOT_PROGRESS_PMS_READY#
BOOT_PROGRESS_PMS_READY: The final stage of PKMS.
PackageInstallerService is used to manage installation sessions, each installation process will allocate a SessionId.
Request to trigger GC to reclaim memory.
// PackageManagerService.java
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
...
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
...
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
...
// PermissionController is a system core, preset to agree & manage permissions
mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
... omitted parts
// Obtain the installer service
mInstallerService = mInjector.getPackageInstallerService();
...
// Read & update the usage of dex files
// This operation is performed at the end of PM init, so that all packages coordinate their data directories
// Construct verifiable files and build memory cache
// The expected file is small, so loading & verifying it takes time compared to other activities
final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();
for (int userId : userIds) {
userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());
}
mDexManager.load(userPackages);
if (mIsUpgrade) {
FrameworkStatsLog.write(
FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME,
SystemClock.uptimeMillis() - startTime);
}
// After properties are reconstructed, reconstruct the live computer
mLiveComputer = createLiveComputer();
}
}
...
// After opening application zip, ensure they are refreshed and perform GC reclaim
VMRuntime.getRuntime().requestConcurrentGC();
mInstaller.setWarnIfHeld(mLock);
...
}