Android包管理機制(四)PMS的建立過程

劉望舒發表於2019-03-02

相關文章
Android包管理機制系列

前言

PMS的建立過程分為兩個部分進行講解,分別是SyetemServer處理部分和PMS構造方法。其中SyetemServer處理部分和AMS和WMS的建立過程是類似的,可以將它們進行對比,這樣可以更好的理解和記憶這一知識點。

1. SyetemServer處理部分

PMS是在SyetemServer程式中被建立的,SyetemServer程式用來建立系統服務,不瞭解它的可以檢視Android系統啟動流程(三)解析SyetemServer程式啟動過程這篇文章。
從SyetemServer的入口方法main方法開始講起,如下所示。
frameworks/base/services/java/com/android/server/SystemServer.java

 public static void main(String[] args) {
        new SystemServer().run();
    }
複製程式碼

main方法中只呼叫了SystemServer的run方法,如下所示。
frameworks/base/services/java/com/android/server/SystemServer.java

private void run() {
    try {
        ...
        //建立訊息Looper
         Looper.prepareMainLooper();
        //載入了動態庫libandroid_servers.so
        System.loadLibrary("android_servers");//1
        performPendingShutdown();
        // 建立系統的Context
        createSystemContext();
        // 建立SystemServiceManager
        mSystemServiceManager = new SystemServiceManager(mSystemContext);//2
        mSystemServiceManager.setRuntimeRestarted(mRuntimeRestart);
        LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
        SystemServerInitThreadPool.get();
    } finally {
        traceEnd(); 
    }
    try {
        traceBeginAndSlog("StartServices");
        //啟動引導服務
        startBootstrapServices();//3
        //啟動核心服務
        startCoreServices();//4
        //啟動其他服務
        startOtherServices();//5
        SystemServerInitThreadPool.shutdown();
    } catch (Throwable ex) {
        Slog.e("System", "******************************************");
        Slog.e("System", "************ Failure starting system services", ex);
        throw ex;
    } finally {
        traceEnd();
    }
    ...
}
複製程式碼

在註釋1處載入了動態庫libandroid_servers.so。接下來在註釋2處建立SystemServiceManager,它會對系統的服務進行建立、啟動和生命週期管理。在註釋3中的startBootstrapServices方法中用SystemServiceManager啟動了ActivityManagerService、PowerManagerService、PackageManagerService等服務。在註釋4處的startCoreServices方法中則啟動了DropBoxManagerService、BatteryService、UsageStatsService和WebViewUpdateService。註釋5處的startOtherServices方法中啟動了CameraService、AlarmManagerService、VrManagerService等服務。這些服務的父類均為SystemService。從註釋3、4、5的方法可以看出,官方把系統服務分為了三種型別,分別是引導服務、核心服務和其他服務,其中其他服務是一些非緊要和一些不需要立即啟動的服務。這些系統服務總共有100多個,我們熟知的AMS屬於引導服務,WMS屬於其他服務,
本文要講的PMS屬於引導服務,因此這裡列出引導服務以及它們的作用,見下表。

引導服務 作用
Installer 系統安裝apk時的一個服務類,啟動完成Installer服務之後才能啟動其他的系統服務
ActivityManagerService 負責四大元件的啟動、切換、排程。
PowerManagerService 計算系統中和Power相關的計算,然後決策系統應該如何反應
LightsService 管理和顯示背光LED
DisplayManagerService 用來管理所有顯示裝置
UserManagerService 多使用者模式管理
SensorService 為系統提供各種感應器服務
PackageManagerService 用來對apk進行安裝、解析、刪除、解除安裝等等操作

檢視啟動引導服務的註釋3處的startBootstrapServices方法。
frameworks/base/services/java/com/android/server/SystemServer.java

private void startBootstrapServices() {
    ...
    traceBeginAndSlog("StartPackageManagerService");
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);//1
    mFirstBoot = mPackageManagerService.isFirstBoot();//2
    mPackageManager = mSystemContext.getPackageManager();
    traceEnd();
    ...
}
複製程式碼

註釋1處的PMS的main方法主要用來建立PMS,其中最後一個引數mOnlyCore代表是否只掃描系統的目錄,它在本篇文章中會出現多次,一般情況下它的值為false。註釋2處獲取boolean型別的變數mFirstBoot,它用於表示PMS是否首次被啟動。mFirstBoot是後續WMS建立時所需要的引數,從這裡就可以看出系統服務之間是有依賴關係的,它們的啟動順序不能隨意被更改。

2. PMS構造方法

PMS的main方法如下所示。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

   public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        PackageManagerServiceCompilerMapping.checkProperties();
        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        m.enableSystemUserPackages();
        ServiceManager.addService("package", m);
        return m;
    }
複製程式碼

main方法主要做了兩件事,一個是建立PMS物件,另一個是將PMS註冊到ServiceManager中。
PMS的構造方法大概有600多行,分為5個階段,每個階段會列印出相應的EventLog,EventLog用於列印Android系統的事件日誌。

  1. BOOT_PROGRESS_PMS_START(開始階段)
  2. BOOT_PROGRESS_PMS_SYSTEM_SCAN_START(掃描系統階段)
  3. BOOT_PROGRESS_PMS_DATA_SCAN_START(掃描Data分割槽階段)
  4. BOOT_PROGRESS_PMS_SCAN_END(掃描結束階段)
  5. BOOT_PROGRESS_PMS_READY(準備階段)

2.1 開始階段

PMS的構造方法中會獲取一些包管理需要屬性,如下所示。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager");
        //列印開始階段日誌
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                SystemClock.uptimeMillis())
        ...
        //用於儲存螢幕的相關資訊
        mMetrics = new DisplayMetrics();
        //Settings用於儲存所有包的動態設定
        mSettings = new Settings(mPackages);
	    //在Settings中新增多個預設的sharedUserId
        mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);//1
        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);
        ...
        mInstaller = installer;
        //建立Dex優化工具類
        mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
                "*dexopt*");
        mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
        mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
        mOnPermissionChangeListeners = new OnPermissionChangeListeners(
                FgThread.get().getLooper());
        getDefaultDisplayMetrics(context, mMetrics);
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");
        //得到全域性系統配置資訊。
        SystemConfig systemConfig = SystemConfig.getInstance();
        //獲取全域性的groupId 
        mGlobalGids = systemConfig.getGlobalGids();
        //獲取系統許可權
        mSystemPermissions = systemConfig.getSystemPermissions();
        mAvailableFeatures = systemConfig.getAvailableFeatures();
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        mProtectedPackages = new ProtectedPackages(mContext);
        //安裝APK時需要的鎖,保護所有對installd的訪問。
        synchronized (mInstallLock) {//1
        //更新APK時需要的鎖,保護記憶體中已經解析的包資訊等內容
        synchronized (mPackages) {//2
            //建立後臺執行緒ServiceThread
            mHandlerThread = new ServiceThread(TAG,
                    Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
            mHandlerThread.start();
            //建立PackageHandler繫結到ServiceThread的訊息佇列
            mHandler = new PackageHandler(mHandlerThread.getLooper());//3
            mProcessLoggingHandler = new ProcessLoggingHandler();
            //將PackageHandler新增到Watchdog的檢測集中
            Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);//4

            mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
            mInstantAppRegistry = new InstantAppRegistry(this);
            //在Data分割槽建立一些目錄
            File dataDir = Environment.getDataDirectory();//5
            mAppInstallDir = new File(dataDir, "app");
            mAppLib32InstallDir = new File(dataDir, "app-lib");
            mAsecInternalPath = new File(dataDir, "app-asec").getPath();
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
            //建立多使用者管理服務
            sUserManager = new UserManagerService(context, this,
                    new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
             ...
               mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false))//6
          ...     
}
複製程式碼

在開始階段中建立了很多PMS中的關鍵物件並賦值給PMS中的成員變數,下面簡單介紹這些成員變數。

  • mSettings :用於儲存所有包的動態設定。註釋1處將系統程式的sharedUserId新增到Settings中,sharedUserId用於程式間共享資料,比如兩個App的之間的資料是不共享的,如果它們有了共同的sharedUserId,就可以執行在同一個程式中共享資料。
  • mInstaller :Installer繼承自SystemService,和PMS、AMS一樣是系統的服務(雖然名稱不像是服務),PMS很多的操作都是由Installer來完成的,比如APK的安裝和解除安裝。在Installer內部,通過IInstalld和installd進行Binder通訊,由位於nativie層的installd來完成具體的操作。
  • systemConfig:用於得到全域性系統配置資訊。比如系統的許可權就可以通過SystemConfig來獲取。
  • mPackageDexOptimizer : Dex優化的工具類。
  • mHandler(PackageHandler型別) :PackageHandler繼承自Handler,在註釋3處它繫結了後臺執行緒ServiceThread的訊息佇列。PMS通過PackageHandler驅動APK的複製和安裝工作,具體的請看在Android包管理機制(三)PMS處理APK的安裝這篇文章。
    PackageHandler處理的訊息佇列如果過於繁忙,有可能導致系統卡住, 因此在註釋4處將它新增到Watchdog的監測集中。
    Watchdog主要有兩個用途,一個是定時檢測系統關鍵服務(AMS和WMS等)是否可能發生死鎖,還有一個是定時檢測執行緒的訊息佇列是否長時間處於工作狀態(可能阻塞等待了很長時間)。如果出現上述問題,Watchdog會將日誌儲存起來,必要時還會殺掉自己所在的程式,也就是SystemServer程式。
  • sUserManager(UserManagerService型別) :多使用者管理服務。

除了建立這些關鍵物件,在開始階段還有一些關鍵程式碼需要去講解:

  • 註釋1處和註釋2處加了兩個鎖,其中mInstallLock是安裝APK時需要的鎖,保護所有對installd的訪問;mPackages是更新APK時需要的鎖,保護記憶體中已經解析的包資訊等內容。
  • 註釋5處後的程式碼建立了一些Data分割槽中的子目錄,比如/data/app。
  • 註釋6處會解析packages.xml等檔案的資訊,儲存到Settings的對應欄位中。packages.xml中記錄系統中所有安裝的應用資訊,包括基本資訊、簽名和許可權。如果packages.xml有安裝的應用資訊,那麼註釋6處Settings的readLPw方法會返回true,mFirstBoot的值為false,說明PMS不是首次被啟動。

2.2 掃描系統階段

...
public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
...
            //列印掃描系統階段日誌
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);
            ...
            //在/system中建立framework目錄
            File frameworkDir = new File(Environment.getRootDirectory(), "framework");
            ...
            //掃描/vendor/overlay目錄下的檔案
            scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR
                    | PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
            mParallelPackageParserCallback.findStaticOverlayPackages();
            //掃描/system/framework 目錄下的檔案
            scanDirTracedLI(frameworkDir, mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR
                    | PackageParser.PARSE_IS_PRIVILEGED,
                    scanFlags | SCAN_NO_DEX, 0);
            final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
            //掃描 /system/priv-app 目錄下的檔案
            scanDirTracedLI(privilegedAppDir, mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR
                    | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
            final File systemAppDir = new File(Environment.getRootDirectory(), "app");
            //掃描/system/app 目錄下的檔案
            scanDirTracedLI(systemAppDir, mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
            File vendorAppDir = new File("/vendor/app");
            try {
                vendorAppDir = vendorAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            //掃描 /vendor/app 目錄下的檔案
            scanDirTracedLI(vendorAppDir, mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

           //掃描/oem/app 目錄下的檔案
            final File oemAppDir = new File(Environment.getOemDirectory(), "app");
            scanDirTracedLI(oemAppDir, mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

            //這個列表代表有可能有升級包的系統App
            final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();//1
            if (!mOnlyCore) {
                Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
                while (psit.hasNext()) {
                    PackageSetting ps = psit.next();                 
                    if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
                        continue;
                    }
                    //這裡的mPackages的是PMS的成員變數,代表scanDirTracedLI方法掃描上面那些目錄得到的 
                    final PackageParser.Package scannedPkg = mPackages.get(ps.name);
                    if (scannedPkg != null) {           
                        if (mSettings.isDisabledSystemPackageLPr(ps.name)) {//2
                           ...
                            //將這個系統App的PackageSetting從PMS的mPackages中移除
                            removePackageLI(scannedPkg, true);
                            //將升級包的路徑新增到mExpectingBetter列表中
                            mExpectingBetter.put(ps.name, ps.codePath);
                        }
                        continue;
                    }
                   
                    if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
                       ...   
                    } else {
                        final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
                        //這個系統App升級包資訊在mDisabledSysPackages中,但是沒有發現這個升級包存在
                        if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {//5
                            possiblyDeletedUpdatedSystemApps.add(ps.name);//
                        }
                    }
                }
            }
            ...        
}
複製程式碼

/system可以稱作為System分割槽,裡面主要儲存谷歌和其他廠商提供的Android系統相關檔案和框架。Android系統架構分為應用層、應用框架層、系統執行庫層(Native 層)、硬體抽象層(HAL層)和Linux核心層,除了Linux核心層在Boot分割槽,其他層的程式碼都在System分割槽。下面列出 System分割槽的部分子目錄。

目錄 含義
app 存放系統App,包括了谷歌內建的App也有廠商或者運營商提供的App
framework 存放應用框架層的jar包
priv-app 存放特權App
lib 存放so檔案
fonts 存放系統字型檔案
media 存放系統的各種聲音,比如鈴聲、提示音,以及系統啟動播放的動畫

上面的程式碼還涉及到/vendor 目錄,它用來儲存廠商對Android系統的定製部分。

系統掃描階段的主要工作有以下3點:

  1. 建立/system的子目錄,比如/system/framework、/system/priv-app和/system/app等等
  2. 掃描系統檔案,比如/vendor/overlay、/system/framework、/system/app等等目錄下的檔案。
  3. 對掃描到的系統檔案做後續處理。

主要來說第3點,一次OTA升級對於一個系統App會有三種情況:

  • 這個系統APP無更新。
  • 這個系統APP有更新。
  • 新的OTA版本中,這個系統APP已經被刪除。

當系統App升級,PMS會將該系統App的升級包設定資料(PackageSetting)儲存到Settings的mDisabledSysPackages列表中(具體見PMS的replaceSystemPackageLIF方法),mDisabledSysPackages的型別為ArrayMap<String, PackageSetting>。mDisabledSysPackages中的資訊會被PMS儲存到packages.xml中的<updated-package>標籤下(具體見Settings的writeDisabledSysPackageLPr方法)。
註釋2處說明這個系統App有升級包,那麼就將該系統App的PackageSetting從mDisabledSysPackages列表中移除,並將系統App的升級包的路徑新增到mExpectingBetter列表中,mExpectingBetter的型別為ArrayMap<String, File>等待後續處理。
註釋5處如果這個系統App的升級包資訊儲存在mDisabledSysPackages列表中,但是沒有發現這個升級包存在,則將它加入到possiblyDeletedUpdatedSystemApps列表中,意為“系統App的升級包可能被刪除”,之所以是“可能”,是因為系統還沒有掃描Data分割槽,只能暫放到possiblyDeletedUpdatedSystemApps列表中,等到掃描完Data分割槽後再做處理。

2.3 掃描Data分割槽階段

public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
    ...        
    mSettings.pruneSharedUsersLPw();
    //如果不是隻掃描系統的目錄,那麼就開始掃描Data分割槽。
    if (!mOnlyCore) {
        //列印掃描Data分割槽階段日誌
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                SystemClock.uptimeMillis());
        //掃描/data/app目錄下的檔案       
        scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
        //掃描/data/app-private目錄下的檔案   
        scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
                | PackageParser.PARSE_FORWARD_LOCK,
                scanFlags | SCAN_REQUIRE_KNOWN, 0);
        //掃描完Data分割槽後,處理possiblyDeletedUpdatedSystemApps列表
        for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
            PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
            // 從mSettings.mDisabledSysPackages變數中移除去此應用
            mSettings.removeDisabledSystemPackageLPw(deletedAppName);
            String msg;
          //1:如果這個系統App的包資訊不在PMS的變數mPackages中,說明是殘留的App資訊,後續會刪除它的資料。
            if (deletedPkg == null) {
                msg = "Updated system package " + deletedAppName
                        + " no longer exists; it`s data will be wiped";
                // Actual deletion of code and data will be handled by later
                // reconciliation step
            } else {
            //2:如果這個系統App在mPackages中,說明是存在於Data分割槽,不屬於系統App,那麼移除其系統許可權。
                msg = "Updated system app + " + deletedAppName
                        + " no longer present; removing system privileges for "
                        + deletedAppName;
                deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
                PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
                deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
            }
            logCriticalInfo(Log.WARN, msg);
        }
         //遍歷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);
                logCriticalInfo(Log.WARN, "Expected better " + packageName
                        + " but never showed up; reverting to system");
                int reparseFlags = mDefParseFlags;
                //3:根據系統App所在的目錄設定掃描的解析引數
                if (FileUtils.contains(privilegedAppDir, scanFile)) {
                    reparseFlags = PackageParser.PARSE_IS_SYSTEM
                            | PackageParser.PARSE_IS_SYSTEM_DIR
                            | PackageParser.PARSE_IS_PRIVILEGED;
                } 
                ...
                //將packageName對應的包設定資料(PackageSetting)新增到mSettings的mPackages中
                mSettings.enableSystemPackageLPw(packageName);//4
                try {
                    //掃描系統App的升級包
                    scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);//5
                } catch (PackageManagerException e) {
                    Slog.e(TAG, "Failed to parse original system package: "
                            + e.getMessage());
                }
            }
        }
    }
   //清除mExpectingBetter列表
    mExpectingBetter.clear();
...
}
複製程式碼

/data可以稱為Data分割槽,它用來儲存所有使用者的個人資料和配置檔案。下面列出Data分割槽部分子目錄:

目錄 含義
app 儲存使用者自己安裝的App
data 儲存所有已安裝的App資料的目錄,每個App都有自己單獨的子目錄
app-private App的私有儲存空間
app-lib 儲存所有App的Jni庫
system 存放系統配置檔案
anr 用於儲存ANR發生時系統生成的traces.txt檔案

掃描Data分割槽階段主要做了以下幾件事:

  1. 掃描/data/app和/data/app-private目錄下的檔案。
  2. 遍歷possiblyDeletedUpdatedSystemApps列表,註釋1處如果這個系統App的包資訊不在PMS的變數mPackages中,說明是殘留的App資訊,後續會刪除它的資料。註釋2處如果這個系統App的包資訊在mPackages中,說明是存在於Data分割槽,不屬於系統App,那麼移除其系統許可權。
  3. 遍歷mExpectingBetter列表,註釋3處根據系統App所在的目錄設定掃描的解析引數,註釋4處的方法內部會將packageName對應的包設定資料(PackageSetting)新增到mSettings的mPackages中。註釋5處掃描系統App的升級包,最後清除mExpectingBetter列表。

2.4 掃描結束階段

  //列印掃描結束階段日誌
  EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                    SystemClock.uptimeMillis());
            Slog.i(TAG, "Time to scan packages: "
                    + ((SystemClock.uptimeMillis()-startTime)/1000f)
                    + " seconds");
            int updateFlags = UPDATE_PERMISSIONS_ALL;
            // 如果當前平臺SDK版本和上次啟動時的SDK版本不同,重新更新APK的授權
            if (ver.sdkVersion != mSdkVersion) {
                Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to "
                        + mSdkVersion + "; regranting permissions for internal storage");
                updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
            }
            updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
            ver.sdkVersion = mSdkVersion;
           //如果是第一次啟動或者是Android M升級後的第一次啟動,需要初始化所有使用者定義的預設首選App
            if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
                for (UserInfo user : sUserManager.getUsers(true)) {
                    mSettings.applyDefaultPreferredAppsLPw(this, user.id);
                    applyFactoryDefaultBrowserLPw(user.id);
                    primeDomainVerificationsLPw(user.id);
                }
            }
           ...
            //OTA後的第一次啟動,會清除程式碼快取目錄。
            if (mIsUpgrade && !onlyCore) {
                Slog.i(TAG, "Build fingerprint changed; clearing code caches");
                for (int i = 0; i < mSettings.mPackages.size(); i++) {
                    final PackageSetting ps = mSettings.mPackages.valueAt(i);
                    if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
                        clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
                                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
                                        | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                    }
                }
                ver.fingerprint = Build.FINGERPRINT;
            }
            ...
           // 把Settings的內容儲存到packages.xml中
            mSettings.writeLPr();
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
複製程式碼

掃描結束結束階段主要做了以下幾件事:

  1. 如果當前平臺SDK版本和上次啟動時的SDK版本不同,重新更新APK的授權。
  2. 如果是第一次啟動或者是Android M升級後的第一次啟動,需要初始化所有使用者定義的預設首選App。
  3. OTA升級後的第一次啟動,會清除程式碼快取目錄。
  4. 把Settings的內容儲存到packages.xml中,這樣此後PMS再次建立時會讀到此前儲存的Settings的內容。

2.5 準備階段

 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                SystemClock.uptimeMillis());
    ... 
    mInstallerService = new PackageInstallerService(context, this);//1
    ...
    Runtime.getRuntime().gc();//2
    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "loadFallbacks");
    FallbackCategoryProvider.loadFallbacks();
    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    mInstaller.setWarnIfHeld(mPackages);
    LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());//3
    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
複製程式碼

註釋1處建立PackageInstallerService,PackageInstallerService是用於管理安裝會話的服務,它會為每次安裝過程分配一個SessionId,在Android包管理機制(二)PackageInstaller安裝APK這篇文章中提到過PackageInstallerService。
註釋2處進行一次垃圾收集。註釋3處將PackageManagerInternalImpl(PackageManager的本地服務)新增到LocalServices中,
LocalServices用於儲存執行在當前的程式中的本地服務。

3. 總結

本篇文章介紹了PMS的建立過程,分為兩個部分,分別是SyetemServer處理部分和PMS構造方法,PMS構造方法又分為5個部分,分別是開始階段、掃描系統階段、掃描Data分割槽階段、掃描結束階段和準備階段。

參考資料
Android包管理機制
原始碼分析 — PackageManagerService(一)之啟動流程
Android-6.0之PMS解析下篇
APK安裝流程詳解6——PackageManagerService啟動前奏


這裡不僅分享Android、Java和移動前端相關技術,還有行業動態、技術資訊、面經和個人感悟。

Android包管理機制(四)PMS的建立過程

相關文章