Android包管理機(jī)制(四)PMS的創(chuàng)建過(guò)程

成為一名優(yōu)秀的Android開(kāi)發(fā)劣像,需要一份完備的知識(shí)體系,在這里晾虑,讓我們一起成長(zhǎng)為自己所想的那樣~挥唠。

前言

PMS的創(chuàng)建過(guò)程分為兩個(gè)部分進(jìn)行講解够掠,分別是SyetemServer處理部分和PMS構(gòu)造方法忠聚。其中SyetemServer處理部分和AMS和WMS的創(chuàng)建過(guò)程是類似的,可以將它們進(jìn)行對(duì)比柳弄,這樣可以更好的理解和記憶這一知識(shí)點(diǎn)舶胀。

1. SyetemServer處理部分

PMS是在SyetemServer進(jìn)程中被創(chuàng)建的,SyetemServer進(jìn)程用來(lái)創(chuàng)建系統(tǒng)服務(wù)碧注,不了解它的可以查看Android系統(tǒng)啟動(dòng)流程(三)解析SyetemServer進(jìn)程啟動(dòng)過(guò)程這篇文章嚣伐。 從SyetemServer的入口方法main方法開(kāi)始講起,如下所示萍丐。 frameworks/base/services/java/com/android/server/SystemServer.java

 public static void main(String[] args) {
        new SystemServer().run();
    }
復(fù)制代碼

main方法中只調(diào)用了SystemServer的run方法轩端,如下所示。 frameworks/base/services/java/com/android/server/SystemServer.java

private void run() {
    try {
        ...
        //創(chuàng)建消息Looper
         Looper.prepareMainLooper();
        //加載了動(dòng)態(tài)庫(kù)libandroid_servers.so
        System.loadLibrary("android_servers");//1
        performPendingShutdown();
        // 創(chuàng)建系統(tǒng)的Context
        createSystemContext();
        // 創(chuàng)建SystemServiceManager
        mSystemServiceManager = new SystemServiceManager(mSystemContext);//2
        mSystemServiceManager.setRuntimeRestarted(mRuntimeRestart);
        LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
        SystemServerInitThreadPool.get();
    } finally {
        traceEnd(); 
    }
    try {
        traceBeginAndSlog("StartServices");
        //啟動(dòng)引導(dǎo)服務(wù)
        startBootstrapServices();//3
        //啟動(dòng)核心服務(wù)
        startCoreServices();//4
        //啟動(dòng)其他服務(wù)
        startOtherServices();//5
        SystemServerInitThreadPool.shutdown();
    } catch (Throwable ex) {
        Slog.e("System", "******************************************");
        Slog.e("System", "************ Failure starting system services", ex);
        throw ex;
    } finally {
        traceEnd();
    }
    ...
}
復(fù)制代碼

在注釋1處加載了動(dòng)態(tài)庫(kù)libandroid_servers.so逝变。接下來(lái)在注釋2處創(chuàng)建SystemServiceManager基茵,它會(huì)對(duì)系統(tǒng)的服務(wù)進(jìn)行創(chuàng)建、啟動(dòng)和生命周期管理壳影。在注釋3中的startBootstrapServices方法中用SystemServiceManager啟動(dòng)了ActivityManagerService拱层、PowerManagerService、PackageManagerService等服務(wù)宴咧。在注釋4處的startCoreServices方法中則啟動(dòng)了DropBoxManagerService根灯、BatteryService、UsageStatsService和WebViewUpdateService悠汽。注釋5處的startOtherServices方法中啟動(dòng)了CameraService、AlarmManagerService芥驳、VrManagerService等服務(wù)柿冲。這些服務(wù)的父類均為SystemService。從注釋3兆旬、4假抄、5的方法可以看出,官方把系統(tǒng)服務(wù)分為了三種類型丽猬,分別是引導(dǎo)服務(wù)宿饱、核心服務(wù)和其他服務(wù),其中其他服務(wù)是一些非緊要和一些不需要立即啟動(dòng)的服務(wù)脚祟。這些系統(tǒng)服務(wù)總共有100多個(gè)谬以,我們熟知的AMS屬于引導(dǎo)服務(wù),WMS屬于其他服務(wù)由桌, 本文要講的PMS屬于引導(dǎo)服務(wù)为黎,因此這里列出引導(dǎo)服務(wù)以及它們的作用邮丰,見(jiàn)下表。

引導(dǎo)服務(wù) 作用
Installer 系統(tǒng)安裝apk時(shí)的一個(gè)服務(wù)類铭乾,啟動(dòng)完成Installer服務(wù)之后才能啟動(dòng)其他的系統(tǒng)服務(wù)
ActivityManagerService 負(fù)責(zé)四大組件的啟動(dòng)剪廉、切換、調(diào)度炕檩。
PowerManagerService 計(jì)算系統(tǒng)中和Power相關(guān)的計(jì)算斗蒋,然后決策系統(tǒng)應(yīng)該如何反應(yīng)
LightsService 管理和顯示背光LED
DisplayManagerService 用來(lái)管理所有顯示設(shè)備
UserManagerService 多用戶模式管理
SensorService 為系統(tǒng)提供各種感應(yīng)器服務(wù)
PackageManagerService 用來(lái)對(duì)apk進(jìn)行安裝、解析笛质、刪除泉沾、卸載等等操作

查看啟動(dòng)引導(dǎo)服務(wù)的注釋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();
    ...
}
復(fù)制代碼

注釋1處的PMS的main方法主要用來(lái)創(chuàng)建PMS经瓷,其中最后一個(gè)參數(shù)mOnlyCore代表是否只掃描系統(tǒng)的目錄爆哑,它在本篇文章中會(huì)出現(xiàn)多次,一般情況下它的值為false舆吮。注釋2處獲取boolean類型的變量mFirstBoot揭朝,它用于表示PMS是否首次被啟動(dòng)。mFirstBoot是后續(xù)WMS創(chuàng)建時(shí)所需要的參數(shù)色冀,從這里就可以看出系統(tǒng)服務(wù)之間是有依賴關(guān)系的潭袱,它們的啟動(dòng)順序不能隨意被更改。

2. PMS構(gòu)造方法

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;
    }
復(fù)制代碼

main方法主要做了兩件事屯换,一個(gè)是創(chuàng)建PMS對(duì)象,另一個(gè)是將PMS注冊(cè)到ServiceManager中与学。 PMS的構(gòu)造方法大概有600多行彤悔,分為5個(gè)階段,每個(gè)階段會(huì)打印出相應(yīng)的EventLog索守,EventLog用于打印Android系統(tǒng)的事件日志晕窑。

  1. BOOT_PROGRESS_PMS_START(開(kāi)始階段)
  2. BOOT_PROGRESS_PMS_SYSTEM_SCAN_START(掃描系統(tǒng)階段)
  3. BOOT_PROGRESS_PMS_DATA_SCAN_START(掃描Data分區(qū)階段)
  4. BOOT_PROGRESS_PMS_SCAN_END(掃描結(jié)束階段)
  5. BOOT_PROGRESS_PMS_READY(準(zhǔn)備階段)

2.1 開(kāi)始階段

PMS的構(gòu)造方法中會(huì)獲取一些包管理需要屬性,如下所示卵佛。 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");
        //打印開(kāi)始階段日志
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                SystemClock.uptimeMillis())
        ...
        //用于存儲(chǔ)屏幕的相關(guān)信息
        mMetrics = new DisplayMetrics();
        //Settings用于保存所有包的動(dòng)態(tài)設(shè)置
        mSettings = new Settings(mPackages);
        //在Settings中添加多個(gè)默認(rèn)的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;
        //創(chuàng)建Dex優(yōu)化工具類
        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");
        //得到全局系統(tǒng)配置信息杨赤。
        SystemConfig systemConfig = SystemConfig.getInstance();
        //獲取全局的groupId 
        mGlobalGids = systemConfig.getGlobalGids();
        //獲取系統(tǒng)權(quán)限
        mSystemPermissions = systemConfig.getSystemPermissions();
        mAvailableFeatures = systemConfig.getAvailableFeatures();
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        mProtectedPackages = new ProtectedPackages(mContext);
        //安裝APK時(shí)需要的鎖,保護(hù)所有對(duì)installd的訪問(wèn)截汪。
        synchronized (mInstallLock) {//1
        //更新APK時(shí)需要的鎖疾牲,保護(hù)內(nèi)存中已經(jīng)解析的包信息等內(nèi)容
        synchronized (mPackages) {//2
            //創(chuàng)建后臺(tái)線程ServiceThread
            mHandlerThread = new ServiceThread(TAG,
                    Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
            mHandlerThread.start();
            //創(chuàng)建PackageHandler綁定到ServiceThread的消息隊(duì)列
            mHandler = new PackageHandler(mHandlerThread.getLooper());//3
            mProcessLoggingHandler = new ProcessLoggingHandler();
            //將PackageHandler添加到Watchdog的檢測(cè)集中
            Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);//4

            mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
            mInstantAppRegistry = new InstantAppRegistry(this);
            //在Data分區(qū)創(chuàng)建一些目錄
            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");
            //創(chuàng)建多用戶管理服務(wù)
            sUserManager = new UserManagerService(context, this,
                    new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
             ...
               mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false))//6
          ...     
}
復(fù)制代碼

在開(kāi)始階段中創(chuàng)建了很多PMS中的關(guān)鍵對(duì)象并賦值給PMS中的成員變量,下面簡(jiǎn)單介紹這些成員變量衙解。

  • mSettings :用于保存所有包的動(dòng)態(tài)設(shè)置阳柔。注釋1處將系統(tǒng)進(jìn)程的sharedUserId添加到Settings中,sharedUserId用于進(jìn)程間共享數(shù)據(jù)蚓峦,比如兩個(gè)App的之間的數(shù)據(jù)是不共享的盔沫,如果它們有了共同的sharedUserId医咨,就可以運(yùn)行在同一個(gè)進(jìn)程中共享數(shù)據(jù)。
  • mInstaller :Installer繼承自SystemService架诞,和PMS拟淮、AMS一樣是系統(tǒng)的服務(wù)(雖然名稱不像是服務(wù)),PMS很多的操作都是由Installer來(lái)完成的谴忧,比如APK的安裝和卸載很泊。在Installer內(nèi)部,通過(guò)IInstalld和installd進(jìn)行Binder通信沾谓,由位于nativie層的installd來(lái)完成具體的操作委造。
  • systemConfig:用于得到全局系統(tǒng)配置信息。比如系統(tǒng)的權(quán)限就可以通過(guò)SystemConfig來(lái)獲取均驶。
  • mPackageDexOptimizer : Dex優(yōu)化的工具類昏兆。
  • mHandler(PackageHandler類型) :PackageHandler繼承自Handler,在注釋3處它綁定了后臺(tái)線程ServiceThread的消息隊(duì)列妇穴。PMS通過(guò)PackageHandler驅(qū)動(dòng)APK的復(fù)制和安裝工作爬虱,具體的請(qǐng)看在Android包管理機(jī)制(三)PMS處理APK的安裝這篇文章。 PackageHandler處理的消息隊(duì)列如果過(guò)于繁忙腾它,有可能導(dǎo)致系統(tǒng)卡住跑筝, 因此在注釋4處將它添加到Watchdog的監(jiān)測(cè)集中。 Watchdog主要有兩個(gè)用途瞒滴,一個(gè)是定時(shí)檢測(cè)系統(tǒng)關(guān)鍵服務(wù)(AMS和WMS等)是否可能發(fā)生死鎖曲梗,還有一個(gè)是定時(shí)檢測(cè)線程的消息隊(duì)列是否長(zhǎng)時(shí)間處于工作狀態(tài)(可能阻塞等待了很長(zhǎng)時(shí)間)。如果出現(xiàn)上述問(wèn)題妓忍,Watchdog會(huì)將日志保存起來(lái)虏两,必要時(shí)還會(huì)殺掉自己所在的進(jìn)程,也就是SystemServer進(jìn)程世剖。
  • sUserManager(UserManagerService類型) :多用戶管理服務(wù)定罢。

除了創(chuàng)建這些關(guān)鍵對(duì)象,在開(kāi)始階段還有一些關(guān)鍵代碼需要去講解:

  • 注釋1處和注釋2處加了兩個(gè)鎖搁廓,其中mInstallLock是安裝APK時(shí)需要的鎖引颈,保護(hù)所有對(duì)installd的訪問(wèn)耕皮;mPackages是更新APK時(shí)需要的鎖境蜕,保護(hù)內(nèi)存中已經(jīng)解析的包信息等內(nèi)容。
  • 注釋5處后的代碼創(chuàng)建了一些Data分區(qū)中的子目錄凌停,比如/data/app粱年。
  • 注釋6處會(huì)解析packages.xml等文件的信息,保存到Settings的對(duì)應(yīng)字段中罚拟。packages.xml中記錄系統(tǒng)中所有安裝的應(yīng)用信息台诗,包括基本信息完箩、簽名和權(quán)限。如果packages.xml有安裝的應(yīng)用信息拉队,那么注釋6處Settings的readLPw方法會(huì)返回true弊知,mFirstBoot的值為false,說(shuō)明PMS不是首次被啟動(dòng)粱快。

2.2 掃描系統(tǒng)階段

...
public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
...
            //打印掃描系統(tǒng)階段日志
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);
            ...
            //在/system中創(chuàng)建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);

            //這個(gè)列表代表有可能有升級(jí)包的系統(tǒng)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
                           ...
                            //將這個(gè)系統(tǒng)App的PackageSetting從PMS的mPackages中移除
                            removePackageLI(scannedPkg, true);
                            //將升級(jí)包的路徑添加到mExpectingBetter列表中
                            mExpectingBetter.put(ps.name, ps.codePath);
                        }
                        continue;
                    }

                    if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
                       ...   
                    } else {
                        final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
                        //這個(gè)系統(tǒng)App升級(jí)包信息在mDisabledSysPackages中,但是沒(méi)有發(fā)現(xiàn)這個(gè)升級(jí)包存在
                        if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {//5
                            possiblyDeletedUpdatedSystemApps.add(ps.name);//
                        }
                    }
                }
            }
            ...        
}
復(fù)制代碼

/system可以稱作為System分區(qū),里面主要存儲(chǔ)谷歌和其他廠商提供的Android系統(tǒng)相關(guān)文件和框架事哭。Android系統(tǒng)架構(gòu)分為應(yīng)用層漫雷、應(yīng)用框架層、系統(tǒng)運(yùn)行庫(kù)層(Native 層)鳍咱、硬件抽象層(HAL層)和Linux內(nèi)核層降盹,除了Linux內(nèi)核層在Boot分區(qū),其他層的代碼都在System分區(qū)谤辜。下面列出 System分區(qū)的部分子目錄蓄坏。

目錄 含義
app 存放系統(tǒng)App,包括了谷歌內(nèi)置的App也有廠商或者運(yùn)營(yíng)商提供的App
framework 存放應(yīng)用框架層的jar包
priv-app 存放特權(quán)App
lib 存放so文件
fonts 存放系統(tǒng)字體文件
media 存放系統(tǒng)的各種聲音每辟,比如鈴聲剑辫、提示音,以及系統(tǒng)啟動(dòng)播放的動(dòng)畫

上面的代碼還涉及到/vendor 目錄渠欺,它用來(lái)存儲(chǔ)廠商對(duì)Android系統(tǒng)的定制部分妹蔽。

系統(tǒng)掃描階段的主要工作有以下3點(diǎn):

  1. 創(chuàng)建/system的子目錄,比如/system/framework挠将、/system/priv-app和/system/app等等
  2. 掃描系統(tǒng)文件胳岂,比如/vendor/overlay、/system/framework舔稀、/system/app等等目錄下的文件乳丰。
  3. 對(duì)掃描到的系統(tǒng)文件做后續(xù)處理。

主要來(lái)說(shuō)第3點(diǎn)内贮,一次OTA升級(jí)對(duì)于一個(gè)系統(tǒng)App會(huì)有三種情況:

  • 這個(gè)系統(tǒng)APP無(wú)更新产园。
  • 這個(gè)系統(tǒng)APP有更新。
  • 新的OTA版本中夜郁,這個(gè)系統(tǒng)APP已經(jīng)被刪除什燕。

當(dāng)系統(tǒng)App升級(jí),PMS會(huì)將該系統(tǒng)App的升級(jí)包設(shè)置數(shù)據(jù)(PackageSetting)存儲(chǔ)到Settings的mDisabledSysPackages列表中(具體見(jiàn)PMS的replaceSystemPackageLIF方法)竞端,mDisabledSysPackages的類型為ArrayMap<String, PackageSetting>屎即。mDisabledSysPackages中的信息會(huì)被PMS保存到packages.xml中的<updated-package>標(biāo)簽下(具體見(jiàn)Settings的writeDisabledSysPackageLPr方法)。 注釋2處說(shuō)明這個(gè)系統(tǒng)App有升級(jí)包,那么就將該系統(tǒng)App的PackageSetting從mDisabledSysPackages列表中移除技俐,并將系統(tǒng)App的升級(jí)包的路徑添加到mExpectingBetter列表中乘陪,mExpectingBetter的類型為ArrayMap<String, File>等待后續(xù)處理。 注釋5處如果這個(gè)系統(tǒng)App的升級(jí)包信息存儲(chǔ)在mDisabledSysPackages列表中雕擂,但是沒(méi)有發(fā)現(xiàn)這個(gè)升級(jí)包存在啡邑,則將它加入到possiblyDeletedUpdatedSystemApps列表中,意為“系統(tǒng)App的升級(jí)包可能被刪除”井赌,之所以是“可能”谣拣,是因?yàn)橄到y(tǒng)還沒(méi)有掃描Data分區(qū),只能暫放到possiblyDeletedUpdatedSystemApps列表中族展,等到掃描完Data分區(qū)后再做處理森缠。

2.3 掃描Data分區(qū)階段

public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
    ...        
    mSettings.pruneSharedUsersLPw();
    //如果不是只掃描系統(tǒng)的目錄,那么就開(kāi)始掃描Data分區(qū)仪缸。
    if (!mOnlyCore) {
        //打印掃描Data分區(qū)階段日志
        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分區(qū)后贵涵,處理possiblyDeletedUpdatedSystemApps列表
        for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
            PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
            // 從mSettings.mDisabledSysPackages變量中移除去此應(yīng)用
            mSettings.removeDisabledSystemPackageLPw(deletedAppName);
            String msg;
          //1:如果這個(gè)系統(tǒng)App的包信息不在PMS的變量mPackages中,說(shuō)明是殘留的App信息恰画,后續(xù)會(huì)刪除它的數(shù)據(jù)宾茂。
            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:如果這個(gè)系統(tǒng)App在mPackages中,說(shuō)明是存在于Data分區(qū)拴还,不屬于系統(tǒng)App跨晴,那么移除其系統(tǒng)權(quán)限。
                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)) {
                //得到系統(tǒng)App的升級(jí)包路徑
                final File scanFile = mExpectingBetter.valueAt(i);
                logCriticalInfo(Log.WARN, "Expected better " + packageName
                        + " but never showed up; reverting to system");
                int reparseFlags = mDefParseFlags;
                //3:根據(jù)系統(tǒng)App所在的目錄設(shè)置掃描的解析參數(shù)
                if (FileUtils.contains(privilegedAppDir, scanFile)) {
                    reparseFlags = PackageParser.PARSE_IS_SYSTEM
                            | PackageParser.PARSE_IS_SYSTEM_DIR
                            | PackageParser.PARSE_IS_PRIVILEGED;
                } 
                ...
                //將packageName對(duì)應(yīng)的包設(shè)置數(shù)據(jù)(PackageSetting)添加到mSettings的mPackages中
                mSettings.enableSystemPackageLPw(packageName);//4
                try {
                    //掃描系統(tǒng)App的升級(jí)包
                    scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);//5
                } catch (PackageManagerException e) {
                    Slog.e(TAG, "Failed to parse original system package: "
                            + e.getMessage());
                }
            }
        }
    }
   //清除mExpectingBetter列表
    mExpectingBetter.clear();
...
}
復(fù)制代碼

/data可以稱為Data分區(qū)片林,它用來(lái)存儲(chǔ)所有用戶的個(gè)人數(shù)據(jù)和配置文件端盆。下面列出Data分區(qū)部分子目錄:

目錄 含義
app 存儲(chǔ)用戶自己安裝的App
data 存儲(chǔ)所有已安裝的App數(shù)據(jù)的目錄,每個(gè)App都有自己?jiǎn)为?dú)的子目錄
app-private App的私有存儲(chǔ)空間
app-lib 存儲(chǔ)所有App的Jni庫(kù)
system 存放系統(tǒng)配置文件
anr 用于存儲(chǔ)ANR發(fā)生時(shí)系統(tǒng)生成的traces.txt文件

掃描Data分區(qū)階段主要做了以下幾件事:

  1. 掃描/data/app和/data/app-private目錄下的文件。
  2. 遍歷possiblyDeletedUpdatedSystemApps列表,注釋1處如果這個(gè)系統(tǒng)App的包信息不在PMS的變量mPackages中一膨,說(shuō)明是殘留的App信息,后續(xù)會(huì)刪除它的數(shù)據(jù)盖呼。注釋2處如果這個(gè)系統(tǒng)App的包信息在mPackages中,說(shuō)明是存在于Data分區(qū),不屬于系統(tǒng)App,那么移除其系統(tǒng)權(quán)限末患。
  3. 遍歷mExpectingBetter列表,注釋3處根據(jù)系統(tǒng)App所在的目錄設(shè)置掃描的解析參數(shù)锤窑,注釋4處的方法內(nèi)部會(huì)將packageName對(duì)應(yīng)的包設(shè)置數(shù)據(jù)(PackageSetting)添加到mSettings的mPackages中璧针。注釋5處掃描系統(tǒng)App的升級(jí)包,最后清除mExpectingBetter列表果复。

2.4 掃描結(jié)束階段

  //打印掃描結(jié)束階段日志
  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;
            // 如果當(dāng)前平臺(tái)SDK版本和上次啟動(dòng)時(shí)的SDK版本不同陈莽,重新更新APK的授權(quán)
            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;
           //如果是第一次啟動(dòng)或者是Android M升級(jí)后的第一次啟動(dòng)渤昌,需要初始化所有用戶定義的默認(rèn)首選App
            if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
                for (UserInfo user : sUserManager.getUsers(true)) {
                    mSettings.applyDefaultPreferredAppsLPw(this, user.id);
                    applyFactoryDefaultBrowserLPw(user.id);
                    primeDomainVerificationsLPw(user.id);
                }
            }
           ...
            //OTA后的第一次啟動(dòng)虽抄,會(huì)清除代碼緩存目錄走搁。
            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的內(nèi)容保存到packages.xml中
            mSettings.writeLPr();
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
復(fù)制代碼

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

  1. 如果當(dāng)前平臺(tái)SDK版本和上次啟動(dòng)時(shí)的SDK版本不同,重新更新APK的授權(quán)迈窟。
  2. 如果是第一次啟動(dòng)或者是Android M升級(jí)后的第一次啟動(dòng)私植,需要初始化所有用戶定義的默認(rèn)首選App。
  3. OTA升級(jí)后的第一次啟動(dòng)车酣,會(huì)清除代碼緩存目錄曲稼。
  4. 把Settings的內(nèi)容保存到packages.xml中,這樣此后PMS再次創(chuàng)建時(shí)會(huì)讀到此前保存的Settings的內(nèi)容湖员。

2.5 準(zhǔn)備階段

 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);
}
復(fù)制代碼

注釋1處創(chuàng)建PackageInstallerService贫悄,PackageInstallerService是用于管理安裝會(huì)話的服務(wù),它會(huì)為每次安裝過(guò)程分配一個(gè)SessionId娘摔,在Android包管理機(jī)制(二)PackageInstaller安裝APK這篇文章中提到過(guò)PackageInstallerService窄坦。 注釋2處進(jìn)行一次垃圾收集。注釋3處將PackageManagerInternalImpl(PackageManager的本地服務(wù))添加到LocalServices中凳寺, LocalServices用于存儲(chǔ)運(yùn)行在當(dāng)前的進(jìn)程中的本地服務(wù)鸭津。

3. 總結(jié)

本篇文章介紹了PMS的創(chuàng)建過(guò)程,分為兩個(gè)部分肠缨,分別是SyetemServer處理部分和PMS構(gòu)造方法逆趋,PMS構(gòu)造方法又分為5個(gè)部分,分別是開(kāi)始階段晒奕、掃描系統(tǒng)階段闻书、掃描Data分區(qū)階段、掃描結(jié)束階段和準(zhǔn)備階段脑慧。

作者:劉望舒
鏈接:https://juejin.cn/post/6844903649408188429
來(lái)源:掘金
著作權(quán)歸作者所有惠窄。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處漾橙。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末杆融,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子霜运,更是在濱河造成了極大的恐慌脾歇,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淘捡,死亡現(xiàn)場(chǎng)離奇詭異藕各,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)焦除,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門激况,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事乌逐〗呋洌” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵浙踢,是天一觀的道長(zhǎng)绢慢。 經(jīng)常有香客問(wèn)我,道長(zhǎng)洛波,這世上最難降的妖魔是什么胰舆? 我笑而不...
    開(kāi)封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蹬挤,結(jié)果婚禮上缚窿,老公的妹妹穿的比我還像新娘。我一直安慰自己焰扳,他們只是感情好滨攻,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蓝翰,像睡著了一般光绕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上畜份,一...
    開(kāi)封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天诞帐,我揣著相機(jī)與錄音,去河邊找鬼爆雹。 笑死停蕉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钙态。 我是一名探鬼主播慧起,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼册倒!你這毒婦竟也來(lái)了蚓挤?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤驻子,失蹤者是張志新(化名)和其女友劉穎灿意,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體崇呵,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缤剧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了域慷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荒辕。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡汗销,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抵窒,到底是詐尸還是另有隱情弛针,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布估脆,位于F島的核電站,受9級(jí)特大地震影響座云,放射性物質(zhì)發(fā)生泄漏疙赠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一朦拖、第九天 我趴在偏房一處隱蔽的房頂上張望圃阳。 院中可真熱鬧,春花似錦璧帝、人聲如沸捍岳。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锣夹。三九已至,卻和暖如春苏潜,著一層夾襖步出監(jiān)牢的瞬間银萍,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工恤左, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贴唇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓飞袋,卻偏偏與公主長(zhǎng)得像戳气,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子巧鸭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容