banner
fwrite

fwrite

好好生活
twitter
github
email

包管理器服務

PackageManagerServervice 透過 SystemServer 進程啟動,該服務會掃描系統中特定的目錄,尋找裡面的 APK 格式文件,並對這些文件進行解析(得到 AppInfo 相關訊息),最後完成安裝。 PKMS 會在安裝應用的過程中解析 APK 中的AndroidManifest.xml文件(解析出 Android 四大組件),這些解析完的信息會保存在 Android 系統中,方便系統隨時取用。

主要功能#

  • 掃描.apk文件,安裝系統應用本地應用
  • 解析 AndroidManifest.xml 清單文件,分析出四大組件、權限… 等等信息,並儲存在 PKMS 方便查詢
  • 管理本地應用,其中包括安裝卸載查詢APK信息… 等等功能
  • 分配應用程序的 UID、GID

透過 GID(群組 ID)來管理權限管理,在某個群組中才能使用某些功能

使用#

我們可以透過Context#getPackageManager()方法來取得 PKMS 服務,PackageManager 是抽象類,實際上是 ApplicationPackageManager。

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
		  
    PackageManager packageManager = this.getBaseContext().getPackageManager();
}

ServiceManager 取得 PKMS#

ActivityThread 透過 ServiceManager 取得 PKMS 的 IBinder,這裡的IPackageManager 是透過 AIDL 自動產生的類,IPackageManager 透過asInterface來取得 Java 代理類,透過該代理類來與 PKMS 通信

// ActivityThread.java
	  
@UnsupportedAppUsage
public static IPackageManager getPackageManager() {
    if (sPackageManager != null) {
        return sPackageManager;
    }
	          
    // 1.取得ServiceManager的代理
    // 2.透過代理傳入“package”來取得PKMS的句柄(handle)
    // 3.返回PKMS的代理
    final IBinder b = ServiceManager.getService("package");
	          
    // 創建AIDL的代理類
    sPackageManager = IPackageManager.Stub.asInterface(b);
	          
    return sPackageManager;
}

SystemServer 初始化 PKMS#

SystemServer 啟動引導服務(startBootstrapServices):PKMS 就是在這裡啟動

// SystemServer.java

public static void main(String[] args) {
    new SystemServer().run();
}
	  
private void run() {
    ... 省略部分

    try {
        startBootstrapServices(t);
        startCoreServices(t);
        startOtherServices(t);
    } /* 省略 catch */

    ... 省略部分

    // Loop forever.
    Looper.loop();
	  
    throw new RuntimeException("Main thread loop unexpectedly exited");
}
	  
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) 
    ... 

    // 安裝服務
    Installer installer = mSystemServiceManager.startService(Installer.class);

    mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
	  
	... 
	  
    try {
        ... watch dog

        // 呼叫PackageManagerService的靜態main方法
        // @分析main
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
                mOnlyCore);
    } ... watch dog
	  
    ... 
}

startBootstrapServices#

啟動 Installer 服務,之後會用這個服務來安裝所有應用

// SystemServer.java

private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
    ... 省略部分

    Installer installer = mSystemServiceManager.startService(Installer.class);

    ... 省略部分
}

VoldProperties#decrypt判斷手機裝置是否加密(讀取init.rc檔的設定),mOnlyCore = true代表只運行核心程序。

// 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)) {
        // 只運行核心程序
        ... log msg

        mOnlyCore = true;
    } else if (ENCRYPTED_STATE.equals(cryptState)) {
        ... log msg

        mOnlyCore = true;
    }

    ...
}

調用PKMS#main方法,創建 PKMS 對象

// SystemServer.java

private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
    ... 省略部分

    try {
        ...

        PackageManagerService.main(mSystemContext, installer,
                domainVerificationService, 
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
                mOnlyCore);    // 假設傳入false
    } /* 省略finally */
  
    ... 省略部分
}

如果設備沒有加密,就執行 A/B OTA dexopting,OtaDexoptService#main方法

// 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 */
        }
    }

    ...
}

PKMS 在SystemServer#startBootstrapServices函數完成初始化操作後,會在startOtherServices做後續的操作(完成 Dex 優化、systemReady)透過PKMS#performFstrimIfNeeded完成 dex 優化

// SystemServer.java

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {

    ... 省略部分

    try {
        mPackageManagerService.performFstrimIfNeeded();
    } ...

    ... 省略部分
}

PKMS 準備就緒調用systemReady加載預設權限

PKMS 分析#

PKMS main#

透過PackageManagerService#main方法就可以創建 PackageManagerService 物件,並且將 PKMS 透過packagepackage_native添加到 ServiceManager 中

// PackageManagerService.java

public static PackageManagerService main(Context context, Installer installer,
        @NonNull DomainVerificationService domainVerificationService, boolean factoryTest,
        boolean onlyCore) {

    // 檢查package編譯相關系統屬性
    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) -> {
                // 負責apk安裝、卸載
                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);

    // 調用PackageMangerService的構造函數
    PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
            Build.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT,
            Build.VERSION.INCREMENTAL);

    ...

    // 通過package、package_native添加到ServiceManager中
    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 構造方法#

PackageManagerService 的構造函數中可以發現 PKMS 構造有5 個階段(寫在 EventLog 中)

階段標誌說明其他
BOOT_PROGRESS_PMS_START初始階段,透過 Inject 內部類來取得單例的各種服務、對象DisplayMetrics、Installer、mPermissionManager、mSettings、mPackageDexOptimizer
BOOT_PROGRESS_PMS_SYSTEM_SCAN_START掃描系統
BOOT_PROGRESS_PMS_DATA_SCAN_START掃描 data 區塊
BOOT_PROGRESS_PMS_SCAN_END掃描結束
BOOT_PROGRESS_PMS_READY準備階段

PKMS 有使用到多 Thread,而裡面有兩個重要的需要注意

鎖名稱說明補充
mPackages(小鎖)用於保護所有內存中解析 Package 的細節、狀態、更新在持有 mInstallLock 的時候,獲取 mPackages 是安全的
mInstallLock(大鎖)用於保護所有安裝 APK 時的鎖,通常是有關到應用 APP 的重載(單線程、涉及繁重的 IO 操作在獲取 mPackages 時,不該再獲取此鎖

第一階段 - BOOT_PROGRESS_PMS_START#

第一階段的重點是Settings#readLPw 方法,讀取/data/system目錄下的文件

BOOT_PROGRESS_PMS_START:取得DisplayMetrics(解析度)、Installer(安裝)、PermissionManager(權限管理)、mSettings(保存安裝信息、清除不存在的應用)、PackageDexOptimizer(Dex 優化)… 詳細請看註釋

// 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(); // 多用戶管理
    // 權限管理服務
    mPermissionManager = injector.getPermissionManagerServiceInternal();

    // 涉及 'data/system/' packages.xml
    // packages-backup.xml
    // packages.list
    // packages-stopped.xml
    // packages-stopped-backup.xml 文件
    mSettings = injector.getSettings();

    ...

    // 添加system、phone、log、nfc、bluetooth、shell、se、
    // networkstack、uwb這幾個sharedUserLPw到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);

    ...

    // 處理dex優化(DexOpt優化)
    mPackageDexOptimizer = injector.getPackageDexOptimizer();
    mDexManager = injector.getDexManager();
    // Art虛擬機管理
    mArtManagerService = injector.getArtManagerService();

    ...

    // 創建/data/app文件夾
    mAppInstallDir = new File(Environment.getDataDirectory(), "app");

    // 安裝APK時需要的鎖,保護所有對installed的訪問
    // CHECKSTYLE:OFF IndentationCheck
    synchronized (mInstallLock) {
    // writer
    synchronized (mLock) {
        // 啟動PackageManager Thread,負責apk安裝、卸載
        mHandler = injector.getHandler();
        // 進程紀錄Handler
        mProcessLoggingHandler = new ProcessLoggingHandler();
        
        // 監聽PackageManager Thread是否超時10分鐘
        Watchdog.getInstance().addThread(mHandler, 
                                 // WATCHDOG_TIMEOUT => 10分鐘
                                 WATCHDOG_TIMEOUT);
    
        ...
            
        // 讀取安裝相關的SELinux策略
        SELinuxMMAC.readInstallPolicy();
        
        // 讀取並解析/data/system中的xml文件
        mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
        
        t.traceEnd();
    }
    
    }
}

其中mSettings.addSharedUserLPw方法是將 **SharedUserId添加到 Setting 中;一般來講進程間數據不可共享,但有共同SharedUserId就可以共享數據 **

  • mPackageDexOptimizer是 Dex 的優化工具
  • mHandler綁定後台 ServiceThread 的消息隊列,PKMS 透過它來驅動 APK 複製&安裝,並且該 Handler 會由 WatchDog 來監看(防止花費過多時間)

PKMD 會訪問幾個重要的文件夾

訪問資料夾
File(Environment.getDataDirectory(), “app”)/data/app
File(Environment.getDataDirectory(), “app-lib”)/data/app-lib

Settings 創建#

先複習 Settings 對象由來:PKMS 的 Settings 對象會由PKMS#Inject類提供

// PackageManager

public static class Injector {

    private final Singleton<Settings> mSettingsProducer;

    Injector(/* 省略參數 */) {
        ...
        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 類的構造函數會創建所需文件 & 文件夾,並設置它們的權限

// Settings.java

Settings(File dataDir,    //帶入的dataDir是/data
         RuntimePermissionsPersistence runtimePermissionsPersistence,
        LegacyPermissionDataProvider permissionDataProvider,
        @NonNull DomainVerificationManagerInternal domainVerificationManager,
        @NonNull PackageManagerTracedLock lock)  {

    ...

    // 創建/data/system目錄
    mSystemDir = new File(dataDir, "system");//'/data/system'
    mSystemDir.mkdirs(); // 創建/data/system
    // 文件夾權限775
    FileUtils.setPermissions(mSystemDir.toString(),
            FileUtils.S_IRWXU|FileUtils.S_IRWXG
            |FileUtils.S_IROTH|FileUtils.S_IXOTH,
            -1, -1);

    // 創建/data/system/packages.xml
    mSettingsFilename = new File(mSystemDir, "packages.xml");
    // 創建/data/system/packages-backup.xml 
    mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
    // 創建data/system/packages.list
    mPackageListFilename = new File(mSystemDir, "packages.list");

    // 設定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
    // 創建data/system/packages-stopped.list
    mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
    // 創建data/system/packages-stopped-backup.list
    mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");

    ...

}

Settings - readLPw 讀取 package 文件#

mSettings#readLPw () 函數:讀取/data/system目錄下的文件,並保存到 Setting 對象中,而文件又分為以下幾種(提出較為重要的幾個)

文件名說明其他
packages.xml安裝 app 信息PKMS 掃描完目錄文件後會創建該文件,當系統執行安裝、卸載、更新等操作時都會更新這個文件
packages-backup.xml安裝 app 的備份信息避免在安裝時關機,產生意外
packages.list所有安裝的 app 信息(非系統 APP)描述所有非系統 APK 信息,當有第三方 APP 變更時就會修改該文件
packages-stopped.xml強制停止 app 信息強制停止某個應用時(用戶操作),系統會將該應用的相關訊息記錄到該文件中
packages-stopped-backup.xml強制停止 app 的備份信息
**mSettings#readLPw ()** 功能:首先掃描packages.xmlpackages-backup.xml文件,其中與 Linux 使用 UID 有關的函數如下
“package” 標籤:使用readPackageLPw函數,讀取上一次分配的 Linux UID
“shared-user” 標籤:使用readSharedUserLPw函數,讀取上一次應用共享的 shared ID
// Settings.java

boolean readLPw(@NonNull List<UserInfo> users) {
    FileInputStream str = null;

    // 如果有packages-backup.xml的話取得backup IO流
    if (mBackupSettingsFilename.exists()) {
        try {
            str = new FileInputStream(mBackupSettingsFilename);
            ...
        } /* 省略catch */
    }

    try {
        if (str == null) {
            ...

            // 沒有backup就使用packages.xml
            str = new FileInputStream(mSettingsFilename);
        }

        // 解析xml
        final TypedXmlPullParser parser = Xml.resolvePullParser(str);
        int type;
        // 調整xml指標位置
        while ((type = parser.next()) != XmlPullParser.START_TAG
                && type != XmlPullParser.END_DOCUMENT) {
            ;
        }
        // 解析格式,判斷xml格式是否正確
        if (type != XmlPullParser.START_TAG) {
            ... err log
            return false;
        }


        int outerDepth = parser.getDepth();
        // 開始分析xml
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            // 解析節點名稱
            String tagName = parser.getName();
            if (tagName.equals("package")) {
                // 讀取上一次分配的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")) {
                // 讀取上一次應用共享的GID
                readSharedUserLPw(parser, users);

            } ... 省略部分 else if

            else {
                ... log

                XmlUtils.skipCurrentTag(parser);
            }
        }
        str.close();
    } /* 省略catch、finally */ 

    ...

    return true;
}

readPackageLPw 應用分配獨立 Linux ID#

接下來我們主要關注 “package” 標籤中的 3 元素

元素名功能補充
name應用包名必須要有 package 包名
userIdLinux 上次為該應用分配的獨立 UID
sharedUserId該應用沒有獨立 UID,與其它應用共享 UID將該應用添加進mPendingPackages列表之後判斷
// 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) {    // 已分配到獨立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) {    // 未分配獨立ID
            if (sharedUserAppId > 0) {

                // 保存該應用信息
                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);
                ...

                // 添加到`mPendingPackages`列表
                mPendingPackages.add(packageSetting);
                ...
            } else {
                ...
            }
        } else {
            ...
        }
    } /* 省略catch */
}

addPackageLPw函數:在 PKMS 中每個應用都是以PackageSetting對象存在,並將其儲存在以 package name 為 Key 的 HashMap 中

// Setting.java

// 儲存全部應用的信息
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, 
        ... /* 省略部分 */) {

    // 以name來取得PackageSetting
    PackageSetting p = mPackages.get(name);
    if (p != null) {
        // 判斷uid 
        if (p.getAppId() == uid) {
            // 相等代表已分配獨立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, 
            ... /* 省略部分 */);

    // 設定UID給該PackageSetting
    p.setAppId(uid);

    // 註冊該ID至系統
    if (mAppIds.registerExistingAppId(uid, p, name)) {
        mPackages.put(name, p);
        return p;
    }
    return null;
}

registerExistingAppId函數:已分配的應用會從mNonSystemSettings取出 null

// 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) {

    // 大於FIRST_APPLICATION_UID代表是獨立應用
    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++;
        }
        // 已分配會取出null
        if (mNonSystemSettings.get(index) != null) {
            // 重複UID
           
            ...err msg
            return false;
        }
        mNonSystemSettings.set(index, setting);
    } else {
        // 小於FIRST_APPLICATION_UID代表是共享應用
        ...
    }
    return true;
}

readPackageLPw 應用分配共享 Linux ID#

接下來我們主要關注 “shared-user” 標籤中的 3 元素

元素名功能補充
name用來描述一個共享 Linux 使用者的名稱
userId用來描述一個共享 Linux 使用者的 ID
system描述該 ID 是系統型態、使用者型態
// 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函數:使用SharedUserSetting來描述一個共享應用;比對 ID 確認沒問題後,就可以加入mSharedUsers列表

// 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;
    }
    // 創建對應對象
    s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
    // 設定ID
    s.mAppId = uid;
    
    // 嘗試註冊
    if (mAppIds.registerExistingAppId(uid, s, name)) {
        mSharedUsers.put(name, s);
        return s;
    }
    return null;
}

registerExistingAppId函數:已分配的應用會從mNonSystemSettings取出 null

// 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) {

    // 大於FIRST_APPLICATION_UID代表是獨立應用
    if (appId >= Process.FIRST_APPLICATION_UID) {
        ...
    } else {
        // 小於FIRST_APPLICATION_UID代表是共享應用
        if (mSystemSettings.get(appId) != null) {
            ...err

            return false;
        }
        mSystemSettings.put(appId, setting);
    }
    return true;
}

回到readLPw函數:經過上面分析“shared-user”“package”後可以得到共享、獨立應用的相關訊息,接著就是要處理共享應用的 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();
        
        // 共享ID一定大於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 (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);
        // 決定最終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);
        }
    }
}

第二階段 - BOOT_PROGRESS_PMS_SYSTEM_SCAN_START#

BOOT_PROGRESS_PMS_SYSTEM_SCAN_START:掃描系統階段,其中目錄包括(systemvendorproductodmoem … 等等目錄中的priv-appappoverlay)並且該步驟仍在兩個鎖內執行
scanDirTracedLI 方法:該方法掃描 APK 包,會針對傳入的路徑掃描內部的 APK 檔案,之後會再針對 scanDirTracedLI 進行分析

	  // PackageManagerService.java
	  
	  // Key是應用包名,Value是應用包
	  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) {
	  
	          ...
	  
	          // 記錄開始時間
	          long startTime = SystemClock.uptimeMillis();
	          EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
	              startTime);
	  
	          // 從init.rc中獲取環境變量BOOTCLASSPATH、
	          //                        SYSTEMSERVERCLASSPATH
	          final String bootClassPath = 
	              System.getenv("BOOTCLASSPATH");
	  
	          final String systemServerClassPath = 
	              System.getenv("SYSTEMSERVERCLASSPATH");
	  
	  
	          // 獲取/system/framework目錄
	          File frameworkDir = new File(Environment.getRootDirectory(), "framework");
	  
	          // 獲取內部版本
	          final VersionInfo ver = mSettings.getInternalVersion();
	  
	          // 判斷是否需要更新
	          mIsUpgrade =
	              !buildFingerprint.equals(ver.fingerprint);
	  
	          // Android M升級上來的版本,須將系統應用權限從安裝時申請,改為運行時申請
	          mPromoteSystemApps =
	              mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
	  
	          // Android N升級上來的版本,需要像首次啟動一樣處理包提取,因為沒有可用的分析數據
	          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;
	  
	          ...
	  
	          // 準備解析package的快取
	          mCacheDir = preparePackageParserCache(mIsEngBuild);
	  
	          // 設定flag,當掃描安裝文件夾時不改變apk路徑
	          int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
	  
	          // 收集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;
	              }
	              // 掃描overlay…等等文件夾內的apk
	              scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
	                      systemScanFlags | partition.scanFlag, 0,
	                      packageParser, executorService);
	          }
	  
	          // 掃描framework文件夾內的apk
	          scanDirTracedLI(frameworkDir, systemParseFlags,
	              systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
	              packageParser, executorService);
	  
	          // 判斷安裝包是否是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) {
	                  // 掃描priv-app文件夾
	                  scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
	                          systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
	                          packageParser, executorService);
	              }
	              // 掃描/system/app文件夾
	              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());
	                  }
	              }
	  
	              // 倒序掃描所有應用
	              for (int index = packageSettings.size() - 1; index >= 0; index--) {
	                  final PackageSetting ps = packageSettings.valueAt(index);
	  
	                  // 如果包有FLAG_SYSTEM忽略
	                  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) {
	  					//若在disabled packages list,添加進
	  					// via OTA,再將其移除
	                      if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
	                          ...
	                          //將系統App的PackageSetting從PKMS中mPackage移除
	                          removePackageLI(scannedPkg, true);
	  
	                          // 將升級包的路徑添加到mExpectingBetter列表中
	                          mExpectingBetter.put(ps.name, ps.getPath());
	                      }
	                      continue;
	                  }
	  
	                  if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
	                      logCriticalInfo(Log.WARN, "System package " + ps.name
	                              + " no longer exists; it's data will be wiped");
	                      // 系統APP將不存在,移除該APP的數據
	                      removePackageDataLIF(ps, userIds, null, 0, false);
	                  } else {
	                      // we still have a disabled system package, but, it still might have
	                      // been removed. check 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) {
	                          // 該系統APP在isDisabledSystemPackage中,並且沒有發現升級包
	                          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());
	                      }
	                  }
	              }
	          }
	  
	          ...
	  
	      }
	  
	      }
	  }

掃描升級系統 APP - system 文件夾#

目前階段(BOOT_PROGRESS_PMS_SYSTEM_SCAN_START)主要是掃描/system文件夾:這個目錄下文件夾都如何規劃放置
Android 系統架構有分為應用層、應用框架層、系統運行層、硬體抽象層、內核層

system 內文件夾放置內容補充
app系統 app包括 google 內置、手機廠商 app
framework應用框架 jar 包主要放 jar 包、vdex
priv-app特權 app
lib放置動態 so 文件
fonts系統字體多是 ttf 檔
media系統音效像是鈴聲、提示音、系統啟動動畫

第二個目的是掃描系統文件並處理,處理的重點是 ==OTA 升級 ==系統會將可升級的系統應用標記為 DisabledSystemPackage,並且有 3 種狀況
系統 APP 有更新:透過 removePackageLI 將系統 App 的 PackageSetting 從 PKMS 中 mPackage 移除,並添加到 mExpectingBetter 列表
系統 APP 被移除:透過removePackageDataLIF移除系統 APP 數據
沒有升級包:系統應用為 DisabledSystemPackage,但沒有發現升級包,代表代表系統升級包可能被刪除

第三階段 - BOOT_PROGRESS_PMS_DATA_SCAN_START#

BOOT_PROGRESS_PMS_DATA_SCAN_START:主要會掃描/data文件夾
掃描處理、更新/data目錄的應用信息(也會及時移除不需要的數據)
處理上一步遺留的 possiblyDeletedUpdatedSystemApps 列表

  • 如果無法從 mPackage 中取得 Package 則會刪除該 APP
  • 如果可以取得 Package 但不是系統 APP,並且會做以下這些事情
    • 移除 System 權限
    • 移除 Package
    • 重新掃描 Package 路徑的 APK
      掃描 mExpectingBetter 列表
    • 取得系統 APP 升級包的路徑,並且會做以下這些事情
    • 根據系統 APP 所在的目錄設定掃描的解析參數
    • 將 PackageName 設定給mSetting#mPackages中(removeDisabledSystemPackageLPw 方法)
    • 清除 mExpectingBetter 列表
// 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) {

    ...
   
    // 取得/data/app資料夾
    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());
            
            // 掃描/data/app文件夾
            scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
                    packageParser, executorService);
        }
        
        // 關閉Parser流(解析Package xml的流)
        packageParser.close();
        
        // 關閉Executor的全部任務
        List<Runnable> unfinishedTasks = executorService.shutdownNow();
        if (!unfinishedTasks.isEmpty()) {
            throw new IllegalStateException("Not all tasks finished before calling close: "
                    + unfinishedTasks);
        }
        
        
        if (!mOnlyCore) {    
            // 2.掃描上一步所遺留的APP包(倒序)

            for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
                final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
                final AndroidPackage pkg = mPackages.get(packageName);
                final String msg;
                
                // 從禁用系統列表(mDisabledSystemPackage)中移除
                mSettings.removeDisabledSystemPackageLPw(packageName);
                
                if (pkg == null) {
                    // 應該要找到升級包,但沒找到(移除該APP)
                    msg = "Updated system package " + packageName
                            + " no longer exists; removing its data";
                    // (實際上並非馬上移除)
                } else {
                    
                    // 如果有取到pkg,代表該APP存在Data區中,
                    // 不屬於System app,移除系統權限
                    msg = "Updated system package " + packageName
                            + " no longer exists; rescanning package on data";
                    // 以下,刪除並重新掃描數據包
                    removePackageLI(pkg, true);    // 刪除
                    try {
                        final File codePath = new File(pkg.getPath());
                        // 重新掃描
                        scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
                    } catch (PackageManagerException e) {
                        Slog.e(TAG, "Failed to parse updated, ex-system package: "
                                + e.getMessage());
                    }
                }
               
                // 如果我們還有一個包設置[ie.它是以前掃描並為系統所知]
                // 但是,我們沒有該Pkg[即。從/data掃描它時出錯
                // partition],徹底刪除包數據。
                final PackageSetting ps = mSettings.getPackageLPr(packageName);
                if (ps != null && mPackages.get(packageName) == null) {
                    // 移除data內出錯的數據包
                    removePackageDataLIF(ps, userIds, null, 0, false);
                }
                logCriticalInfo(Log.WARN, msg);

            }

            // 3.遍歷mExpectingBetter列表
            for (int i = 0; i < mExpectingBetter.size(); i++) {
                final String packageName = mExpectingBetter.keyAt(i);
                if (!mPackages.containsKey(packageName)) {
                    // 取得系統APP升級包的路徑
                    final File scanFile = mExpectingBetter.valueAt(i);

                    for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
                        final ScanPartition partition = mDirsToScanAsSystem.get(i1);
                        // 根據系統APP所在的目錄設定掃描的解析參數
                        if (partition.containsPrivApp(scanFile)) {
                            reparseFlags = systemParseFlags;
                            // 設定掃描參數
                            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;
                    }

                  //將PackageName對應的包設置數據(PackagesSetting)
                  //添加到mSetting的mPackages中
                    mSettings.enableSystemPackageLPw(packageName);

                    try {
                        // 掃描系統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);
                        }
                    } /* 省略catch */
                }
            }

          //解壓&安裝根系統應用
          //該操做要確保最後執行,來確保所有存根被替代or禁用
          // Stub(存根)
            installSystemStubPackages(stubSystemApps, scanFlags);

            ...
        }
        
        
        // 清除升級列表
        mExpectingBetter.clear();
        
        // 取得Storage manage包名
        mStorageManagerPackage = getStorageManagerPackageName();
        
        // 解決保護action過濾器,只允許setup wizard為這些action有最高權限的過濾
        mSetupWizardPackage = getSetupWizardPackageNameImpl();
        
        ...
        
        // 讀取並更新要保留的package的上次使用時間
        mPackageUsage.read(packageSettings);
        mCompilerStats.read();

    }
}

scanDirTracedLI - 分析 data 目錄內容#

/data文件夾可成為 Data 分區,它個主用公用有兩個
儲存所有用戶的個人數據
儲存所有用戶的配置文件
接著來看 /data 目錄下的子目錄都放那些數據

/data 底下目錄含意
app儲存該收機裝置,自己安裝的 APP 應用
data儲存所有已安裝的 APP 數據,其中也包括 System APP 數據
app-privateAPP 的私有儲存空間
app-lib儲存所有 APP 的 JNI Lib
system存放系統配置文件
anr用於存放 ANR 發生時,系統生成的traces.text文件

第四階段 - BOOT_PROGRESS_PMS_SCAN_END#

BOOT_PROGRESS_PMS_SCAN_END 階段升級後首次啟動,要清除不必要的快取數據、權限,更新package.xml檔案(packages.xml 用來儲存所有 APP 的信息)
SDK 版本更變,重新更新權限
OTA 升級後首次啟動,清除不要要快取
OTA 英文全稱是Over-the-Air Technology,即空間下載技術,是手機透過行動網絡對手機系統,應用程序進行管理的技術
權限更新完成後清理相關數據
透過 mSetting 的內容保存並更新 package.xml 文件,這樣以後 PKMS 創建時就會讀取到新的 Setting

// 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_SCAN_END,
                SystemClock.uptimeMillis());

        // 權限重置
        mPermissionManager.onStorageVolumeMounted(
                StorageManager.UUID_PRIVATE_INTERNAL, mIsUpgrade);
        ver.sdkVersion = mSdkVersion;

        // 如果是第一次啟動或從pre-M升級上來,並且是正常啟動,那我們需要初始化User定義的首選預設應用
        if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) {
            for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
                mSettings.applyDefaultPreferredAppsLPw(user.id);
            }
        }
        
      //在啟動期間確實為系統用戶準備儲存空間
      //因為像是SettingsProvider、SystemUI無法等待使用者啟動
        final int storageFlags;
        if (StorageManager.isFileEncryptedNativeOrEmulated()) {
            storageFlags = StorageManager.FLAG_STORAGE_DE;
        } else {
            storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
        }
        
        ...
        
        // 在OTA升級成功後的首次啟動,不必要的快取,但不清除應用程序的配置文件
        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)) {
                    // 沒有應用會在這時啟動,所以不必凍結
                    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;
        }
        
        // 在Launcher中隱藏他們的icon(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;
        
        // 所有的改變都在掃描中完成
        ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
        
        // 更新package.xml
        t.traceBegin("write settings");
        writeSettingsLPrTEMP();
        t.traceEnd();

        ...

    }
        
    }
}

第五階段 - BOOT_PROGRESS_PMS_READY#

BOOT_PROGRESS_PMS_READY:PKMS 最後階段
PackageInstallerService 用於管理安裝 Session 服務,每次安裝過程都會分配一個 SessionId
要求觸發 GC 回收內存

// 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是一個系統核心,預設同意&管理權限
        mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
        
        ... 省略部分
        
        // 取得安裝器服務
        mInstallerService = mInjector.getPackageInstallerService();
        
        ...
        
      // 讀取&更新dex檔案的用法
      // 在PM init結束時執行這個操作,以便所有包都協調其數據目錄
      // 建構可以驗證的文件並建構內存快取
      // 使用文件預計很小,因此與其它活動相比,加載&驗證它都滿花費時間的
        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);
        }

        // 屬性被重構後,重構live computer
        mLiveComputer = createLiveComputer();

    }
        
    }
    
    ...
    
    // 在打開應用zip後,確保他們都刷新,並進行GC回收
    VMRuntime.getRuntime().requestConcurrentGC();
    
    mInstaller.setWarnIfHeld(mLock);
    
    ...
}
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。