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 parses the AndroidManifest.xml
file in the APK during the application installation process (extracting the four main components of Android), and this parsed information is stored in the Android system for easy access.
Main Functions#
- Scan
.apk
files and installsystem applications
andlocal applications
- Parse the AndroidManifest.xml manifest file to analyze the four main components, permissions, etc., and store them in PKMS for easy querying
- Manage local applications, including functions such as
install
,uninstall
,query APK information
, etc. - Allocate UID and GID for applications
Manage permissions through GID (Group ID), allowing certain functions to be used only within a specific group.
Usage#
We can obtain the PKMS service through the Context#getPackageManager()
method. PackageManager is an abstract class, and in fact, it is ApplicationPackageManager.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PackageManager packageManager = this.getBaseContext().getPackageManager();
}
Obtaining PKMS through 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 through 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 Initialization of 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 then 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, perform 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) and complete dex optimization 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 add PKMS 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, obtaining 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 block | |
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) | Used to protect all details, states, and updates of packages parsed in memory | It is safe to obtain mPackages while holding mInstallLock |
mInstallLock (large lock) | Used to protect all locks when installing APKs, usually related to the reloading of application apps (single-threaded, involving heavy IO operations) | This lock should not be obtained when obtaining mPackages |
First Stage - 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: Obtain DisplayMetrics
(resolution), Installer
(installation), PermissionManager
(permission management), mSettings
(stores installation information, clears non-existent applications), PackageDexOptimizer
(Dex optimization) … details can be found in the 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 installing APKs, 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 if there is a common SharedUserId
, data can be shared.
mPackageDexOptimizer
is the Dex optimization tool.- 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 accesses 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 Settings object of PKMS is 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, with the files divided into the following types (highlighting several important ones).
Filename | Description | Other |
---|---|---|
packages.xml | Installed app information | This file will be created after PKMS scans the directory files, and will be updated during system operations such as install, uninstall, update. |
packages-backup.xml | Backup information of installed apps | Prevents accidents during installation when the device is powered off. |
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 records the relevant 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 and packages-backup.xml files, with the following functions related to Linux UID. | ||
“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 ID of the application. |
// Settings.java
boolean readLPw(@NonNull List<UserInfo> users) {
FileInputStream str = null;
// If there is a 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 Assign 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, shares 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 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 independent ID
if (sharedUserAppId > 0) {
// Save 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 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 Assign 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 is correct, 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;
// Attempt 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 relevant information about 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);
}
}
}
Second Stage - BOOT_PROGRESS_PMS_SYSTEM_SCAN_START#
BOOT_PROGRESS_PMS_SYSTEM_SCAN_START: The system scanning stage, including directories such as (system
, vendor
, product
, odm
, oem
, etc.) in the priv-app
, app
, overlay
folders, 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 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");
// Obtain /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 app 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 there is no available analysis data
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 to parse package cache
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 it
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 mPackage
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 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 apps 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 goal 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 updates: Through removePackageLI, the PackageSetting of the system App is removed from PKMS mPackage, and added to the mExpectingBetter list.
System APP is removed: Through removePackageDataLIF, the data of the system APP is removed.
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.
Third Stage - BOOT_PROGRESS_PMS_DATA_SCAN_START#
BOOT_PROGRESS_PMS_DATA_SCAN_START: Mainly scans the /data
folder.
Scanning processes and updating application information in the /data
directory (also timely 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 deleted.
- 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 the following actions will be taken:
- Set scanning parsing parameters according to the directory where the system APP is located.
- Set the PackageName to
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 the Parser stream (the stream for parsing Package xml)
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";
// (In fact, not immediately removed)
} else {
// If pkg is obtained, it indicates that the APP exists in the Data area,
// 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 previously scanned and known to the system]
// but we do not have that Pkg [i.e., an error occurred while scanning it from /data]
// partition], thoroughly delete 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. Traverse 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 according to the directory where the system APP is located
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 PackageName corresponding package setting data (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 */
}
}
// Unzip & install root system applications
// This operation must be ensured to execute last, to ensure all stubs are replaced or disabled
// Stub
installSystemStubPackages(stubSystemApps, scanFlags);
...
}
// Clear the upgrade list
mExpectingBetter.clear();
// Obtain the Storage manager package name
mStorageManagerPackage = getStorageManagerPackageName();
// Resolve protected action filters, allowing only the 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:
Store all users' personal data.
Store 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 |
Fourth Stage - BOOT_PROGRESS_PMS_SCAN_END#
BOOT_PROGRESS_PMS_SCAN_END stage: After OAT upgrade, the first startup, unnecessary cache data, permissions must be cleared, and the package.xml
file must be updated (packages.xml is used to store information of all APPs).
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 is a technology for managing mobile phone systems and applications through mobile networks.
After permission updates are completed, related data is cleared.
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;
}
...
// 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();
...
}
}
}
Fifth Stage - BOOT_PROGRESS_PMS_READY#
BOOT_PROGRESS_PMS_READY: The final stage of PKMS.
PackageInstallerService is used to manage installation sessions, and a SessionId is allocated for each installation process.
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 executed at the end of PM init, so that all packages can coordinate their data directories
// Construct verifiable files and build memory cache
// Using files is expected to be 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);
...
}