fwrite

fwrite

好好生活
twitter
github
email

PackageManagerService

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 install system 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 FlagDescriptionOther
BOOT_PROGRESS_PMS_STARTInitial stage, obtains various singletons through the Inject inner classDisplayMetrics, Installer, mPermissionManager, mSettings, mPackageDexOptimizer
BOOT_PROGRESS_PMS_SYSTEM_SCAN_STARTScanning the system
BOOT_PROGRESS_PMS_DATA_SCAN_STARTScanning the data partition
BOOT_PROGRESS_PMS_SCAN_ENDScanning ends
BOOT_PROGRESS_PMS_READYPreparation stage

PKMS uses multiple threads, and there are two important locks to note:

Lock NameDescriptionSupplement
mPackages (small lock)Protects all details, states, and updates of parsed Packages in memoryIt 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.

AccessFolder
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).

FilenameDescriptionOther
packages.xmlInstalled app informationPKMS will create this file after scanning the directory files, and it will be updated during system operations such as install, uninstall, update
packages-backup.xmlBackup information of installed appsPrevents unexpected issues during installation when the device shuts down
packages.listInformation 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.xmlInformation of forcibly stopped appsWhen a user forcibly stops an application, the system will record the related information of that application in this file
packages-stopped-backup.xmlBackup 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 NameFunctionSupplement
nameApplication package nameMust have a package name
userIdThe independent UID last assigned to this application on Linux
sharedUserIdThis application does not have an independent UID, sharing UID with other applicationsAfter 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 NameFunctionSupplement
nameDescribes the name of a shared Linux user
userIdDescribes the ID of a shared Linux user
systemDescribes 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 FoldersContentSupplement
appSystem appsIncludes built-in Google and manufacturer apps
frameworkApplication framework jar packagesMainly stores jar packages, vdex
priv-appPrivileged apps
libStores dynamic so files
fontsSystem fontsMostly ttf files
mediaSystem soundsSuch 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 SubdirectoryMeaning
appStores the apps installed by the device itself
dataStores the data of all installed apps, including System APP data
app-privateAPP's private storage space
app-libStores all APP's JNI Lib
systemStores system configuration files
anrUsed 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);
    
    ...
}
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.