Android 10.0 PackageManagerService流程分析

概述

PackageManagerService是android系統(tǒng)核心服務(wù)之一蒙袍,在Android中非常重要骚烧,主要負責的功能如下:

  • 解析AndroidManifest.xml 主要包括AndoridManifest中節(jié)點的信息。

  • 掃描本地文件夸政,主要針對apk元旬,主要是系統(tǒng)應(yīng)用,本地安裝應(yīng)用等守问。

  • 管理本地apk匀归,主要包括安裝和刪除等。

啟動過程

PKMS服務(wù)由SystemServer進行啟動耗帕,在SystemServer中startBootstrapServices()啟動PKMS服務(wù)穆端,在調(diào)用startOtherServices進行dex優(yōu)化,磁盤管理等功能仿便,并讓PKMS進入systemReady狀態(tài)体啰。

SystemServer.java#startBootstrapServices()

startBootstrapServices首先啟動Installer服務(wù),也就是安裝器嗽仪,隨后判斷當前的設(shè)備是否處于加密狀態(tài)荒勇,如果是則只解析核心應(yīng)用,接著調(diào)用PKMS的靜態(tài)方法main來創(chuàng)建pkms對象

 
private void startBootstrapServices() {
    ...
        // Wait for installd to finish starting up so that it has a chance to
        // create critical directories such as /data/user with the appropriate
        // permissions.  We need this to complete before we initialize other services.
    //(1)啟動Installer
    //阻塞等待installd完成啟動钦幔,以便有機會創(chuàng)建具有適當權(quán)限的關(guān)鍵目錄,如/data/user常柄。
    //我們需要在初始化其他服務(wù)之前完成此任務(wù)鲤氢。
    Installer installer = mSystemServiceManager.startService(Installer.class);
    mActivityManagerService.setInstaller(installer);

    ...

  // Only run "core" apps if we're encrypting the device.
    //(2)獲取設(shè)別是否加密(手機設(shè)置密碼),如果設(shè)備加密了西潘,則只解析"core"應(yīng)用卷玉,mOnlyCore = true,后面會頻繁使用該變量進行條件判斷
    String cryptState = VoldProperties.decrypt().orElse("");
    if (ENCRYPTING_STATE.equals(cryptState)) {
        Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
        mOnlyCore = true;
    } else if (ENCRYPTED_STATE.equals(cryptState)) {
        Slog.w(TAG, "Device encrypted - only parsing core apps");
        mOnlyCore = true;
    }
    
    //(3)調(diào)用main方法初始化PackageManagerService喷市。
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
 
    //PKMS是否是第一次啟動
    mFirstBoot = mPackageManagerService.isFirstBoot();
    
    //(4)如果設(shè)備沒有加密相种,操作它。管理A/B OTA dexopting品姓。
    if (!mOnlyCore) {
        boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
                false);
        OtaDexoptService.main(mSystemContext, mPackageManagerService);
    }
    ...
}
startOtherServices
  • 執(zhí)行updatePackagesIfNeeded寝并,完成dex優(yōu)化
  • 執(zhí)行performFstrimIfNeeded 完成磁盤維護
  • 調(diào)用systemReady 準備就緒
 
private void startOtherServices() {
    ...
    if (!mOnlyCore) {
        ...
        //(5)如果設(shè)備沒有加密箫措,執(zhí)行performDexOptUpgrade,完成dex優(yōu)化衬潦;
        mPackageManagerService.updatePackagesIfNeeded();
    }
    ...
    //(6) 最終執(zhí)行performFstrim斤蔓,完成磁盤維護
    mPackageManagerService.performFstrimIfNeeded();
    ...
    //(7)PKMS準備就緒
    mPackageManagerService.systemReady();
    ...
}
PackageManagerService.java#main()

main函數(shù)主要工作:

  • 檢查Package編譯相關(guān)系統(tǒng)屬性
  • 調(diào)用PackageManagerService構(gòu)造方法
  • 啟動部分應(yīng)用服務(wù)于多用戶場景
  • 往ServiceManager中注冊packagepackage_native
public static PackageManagerService main(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    // (1)檢查Package編譯相關(guān)系統(tǒng)屬性
    PackageManagerServiceCompilerMapping.checkProperties();
 
    //(2)調(diào)用PackageManagerService構(gòu)造方法
    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    //(3)啟用部分應(yīng)用服務(wù)于多用戶場景
    m.enableSystemUserPackages();
    
    //(4)往ServiceManager中注冊”package”和”package_native”镀岛。
    ServiceManager.addService("package", m);
    final PackageManagerNative pmn = m.new PackageManagerNative();
    ServiceManager.addService("package_native", pmn);
    return m;
}

PKMS初始化時的核心部分為PackageManagerService()構(gòu)造函數(shù)的內(nèi)容弦牡,我們接下來就來分析該流程。

PKMS構(gòu)造函數(shù)分析

主要有兩個重要的鎖(mInstallLock,mPackages)和五個階段構(gòu)成漂羊。
mPackages:用來解析內(nèi)存中所有apk的package信息以及相關(guān)狀態(tài)驾锰。
五個階段:

  • 階段1:BOOT_PROGRESS_PMS_START

  • 階段2:BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

  • 階段3:BOOT_PROGRESS_PMS_DATA_SCAN_START

  • 階段4:BOOT_PROGRESS_PMS_SCAN_END

  • 階段5:BOOT_PROGRESS_PMS_READY
    PKMS服務(wù)也是通過binder進行通信,IPackageManager.aidl由工具轉(zhuǎn)換后自動生成binder的服務(wù)端IPackageManager.Stub和客戶端IPackageManager.Stub.Proxy走越。

  • Binder服務(wù)端:PackageManagerService繼承于IPackageManager.Stub

  • Binder客戶端:ApplicationPackageManager的成員變量mPM繼承于IPackageManager.Stub.Proxy;本身APM繼承于PackageManager對象椭豫。

PackageManagerService.java

IPacakgeManager.Stub 是IPackageManager.aidl自動生成的,正好也說明PKMS是service端买喧,通過Binder交互

public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender
PackageManagerService()構(gòu)造函數(shù)
public PackageManagerService(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
        ...
        //階段1:BOOT_PROGRESS_PMS_START
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                SystemClock.uptimeMillis());
 
        //階段2:BOOT_PROGRESS_PMS_SYSTEM_SCAN_START 
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);
        ...
        
        //階段3:BOOT_PROGRESS_PMS_DATA_SCAN_START 
        if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
        }
        ...
        //階段4:BOOT_PROGRESS_PMS_SCAN_END 
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                    SystemClock.uptimeMillis());
        ...
        //階段5:BOOT_PROGRESS_PMS_READY
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                    SystemClock.uptimeMillis());
}
階段1:BOOT_PROGRESS_PMS_START

主要工作:

  • 構(gòu)造DisplayMetrics捻悯,保存分辨率等相關(guān)信息

  • 創(chuàng)建Installer對象 與InstallId交互

  • 創(chuàng)建mPermissionManager對象,進行權(quán)限管理

  • 構(gòu)造Settings類淤毛,保存安裝包信息今缚,清除路徑不存在的孤立應(yīng)用,主要涉及/data/system目錄下的packages.xml,package-backup.xml,packages.list,packages-stopped.xml,packages-stopped-backup.xml等文件低淡。

  • 構(gòu)造PackageDexOptimizer及DexManager類姓言,處理dex優(yōu)化;

  • 創(chuàng)建SystemConfig實例蔗蹋,獲取系統(tǒng)配置信息何荚,配置共享lib庫

  • 創(chuàng)建PackageManager的handler線程,循環(huán)處理外部安裝相關(guān)消息猪杭。

public PackageManagerService(...) {
    LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                SystemClock.uptimeMillis());
    mContext = context;
 
    mFactoryTest = factoryTest; // 一般為false,即非工廠生產(chǎn)模式
    mOnlyCore = onlyCore; //標記是否只加載核心服務(wù)
    mMetrics = new DisplayMetrics(); // 分辨率配置
    mInstaller = installer; //保存installer對象
 
// Create sub-components that provide services / data. Order here is important.
    //創(chuàng)建提供服務(wù)/數(shù)據(jù)的子組件皂吮。這里的順序很重要,使用到了兩個重要的同步鎖:mInstallLock戒傻、mPackages
    synchronized (mInstallLock) {
    synchronized (mPackages) {
    // Expose private service for system components to use.
        // 公開系統(tǒng)組件使用的私有服務(wù)
        // 本地服務(wù)
        LocalServices.addService(
                PackageManagerInternal.class, new PackageManagerInternalImpl());
        // 多用戶管理服務(wù)
        sUserManager = new UserManagerService(context, this,
                new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
        mComponentResolver = new ComponentResolver(sUserManager,
                LocalServices.getService(PackageManagerInternal.class),
                mPackages);
        // 權(quán)限管理服務(wù)
        mPermissionManager = PermissionManagerService.create(context,
                mPackages /*externalLock*/);
        mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
        
        //創(chuàng)建Settings對象
        mSettings = new Settings(Environment.getDataDirectory(),
                mPermissionManager.getPermissionSettings(), mPackages);
    }
    }
    
    // 添加system, phone, log, nfc, bluetooth, shell,se口蝠,networkstack 這8種shareUserId到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);
    ...
    // DexOpt優(yōu)化
    mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
                "*dexopt*");
    mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock);
    // ART虛擬機管理服務(wù)
    mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
    mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
 
    mViewCompiler = new ViewCompiler(mInstallLock, mInstaller);
    // 權(quán)限變化監(jiān)聽器
    mOnPermissionChangeListeners = new OnPermissionChangeListeners(
            FgThread.get().getLooper());
    mProtectedPackages = new ProtectedPackages(mContext);
    mApexManager = new ApexManager(context);
    
    // 獲取默認分辨率
    getDefaultDisplayMetrics(context, mMetrics);
    //拿到SystemConfig()的對象俱箱,其中會調(diào)用SystemConfig的readPermissions()完成權(quán)限的讀取
    SystemConfig systemConfig = SystemConfig.getInstance();
    synchronized (mInstallLock) {
            // writer
            synchronized (mPackages) {
                // 啟動"PackageManager"線程,負責apk的安裝灭必、卸載
                mHandlerThread = new ServiceThread(TAG,
                        Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
                mHandlerThread.start();
                // 應(yīng)用handler
                mHandler = new PackageHandler(mHandlerThread.getLooper());
                // 進程記錄handler
                mProcessLoggingHandler = new ProcessLoggingHandler();
                // Watchdog監(jiān)聽ServiceThread是否超時:10分鐘
                Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
                // Instant應(yīng)用注冊
                mInstantAppRegistry = new InstantAppRegistry(this);
                 // 共享lib庫配置
                ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig
                        = systemConfig.getSharedLibraries();
                final int builtInLibCount = libConfig.size();
                for (int i = 0; i < builtInLibCount; i++) {
                    String name = libConfig.keyAt(i);
                    SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
                    addBuiltInSharedLibraryLocked(entry.filename, name);
                }
                ...
                // 讀取安裝相關(guān)SELinux策略
                SELinuxMMAC.readInstallPolicy();
 
                // 返回棧加載
                FallbackCategoryProvider.loadFallbacks();
                //讀取并解析/data/system下的XML文件
                mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
 
                // 清理代碼路徑不存在的孤立軟件包
                final int packageSettingCount = mSettings.mPackages.size();
                for (int i = packageSettingCount - 1; i >= 0; i--) {
                    PackageSetting ps = mSettings.mPackages.valueAt(i);
                    if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())
                            && mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
                        mSettings.mPackages.removeAt(i);
                        mSettings.enableSystemPackageLPw(ps.name);
                    }
                }
 
                // 如果不是首次啟動狞谱,也不是CORE應(yīng)用,則拷貝預(yù)編譯的DEX文件
                if (!mOnlyCore && mFirstBoot) {
                    requestCopyPreoptedFiles();
                }
                ...
            } // synchronized (mPackages)
        }
}

readLPW()會掃描下面5個文件

  1. /data/system/packages.xml
  2. /data/system/packages-backup.xml
  3. /data/system/packages.list
  4. /data/system/packages-stopped.xml
  5. /data/system/packages-stopped-backup.xml

文件共分為三組禁漓,簡單的作用描述如下:

  • packages.xml:PKMS 掃描完目標文件夾后會創(chuàng)建該文件跟衅。當系統(tǒng)進行程序安裝、卸載和更新等操作時播歼,均會更新該文件伶跷。該文件保存了系統(tǒng)中與 package 相關(guān)的一些信息。

  • packages.list:描述系統(tǒng)中存在的所有非系統(tǒng)自帶的 APK 的信息秘狞。當這些程序有變動時叭莫,PKMS 就會更新該文件。

  • packages-stopped.xml:從系統(tǒng)自帶的設(shè)置程序中進入應(yīng)用程序頁面烁试,然后在選擇強制停止(ForceStop)某個應(yīng)用時雇初,系統(tǒng)會將該應(yīng)用的相關(guān)信息記錄到此文件中。也就是該文件保存系統(tǒng)中被用戶強制停止的 Package 的信息减响。

這些目錄的指向靖诗,都在Settings中的構(gòu)造函數(shù)完成, 如下所示支示,得到目錄后調(diào)用readLPw()進行掃描

Settings(File dataDir, PermissionSettings permission,
        Object lock) {
    mLock = lock;
    mPermissions = permission;
    mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
 
    mSystemDir = new File(dataDir, "system");  //mSystemDir指向目錄"/data/system"
    mSystemDir.mkdirs();  //創(chuàng)建 "/data/system"
    //設(shè)置權(quán)限
    FileUtils.setPermissions(mSystemDir.toString(),
            FileUtils.S_IRWXU|FileUtils.S_IRWXG
            |FileUtils.S_IROTH|FileUtils.S_IXOTH,
            -1, -1);
 
    //(1)指向目錄"/data/system/packages.xml"
    mSettingsFilename = new File(mSystemDir, "packages.xml");
    //(2)指向目錄"/data/system/packages-backup.xml"
    mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
    //(3)指向目錄"/data/system/packages.list"
    mPackageListFilename = new File(mSystemDir, "packages.list");
    FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
    //(4)指向目錄"/data/system/packages-stopped.xml"
    mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
    //(5)指向目錄"/data/system/packages-stopped-backup.xml"
    mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}

解析上面這幾個xml的文件內(nèi)容刊橘,建立對應(yīng)的數(shù)據(jù)結(jié)構(gòu)

[Settings.java]
boolean readLPw(@NonNull List<UserInfo> users) {
    FileInputStream str = null;
    ...
    if (str == null) {
        str = new FileInputStream(mSettingsFilename);
    }
    //解析"/data/system/packages.xml"
    XmlPullParser parser = Xml.newPullParser();
    parser.setInput(str, StandardCharsets.UTF_8.name());
 
    int type;
    while ((type = parser.next()) != XmlPullParser.START_TAG
            && type != XmlPullParser.END_DOCUMENT) {
        ;
    }
    int outerDepth = parser.getDepth();
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
        if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
            continue;
        }
        //根據(jù)XML的各個節(jié)點進行各種操作,例如讀取權(quán)限颂鸿、shared-user等
        String tagName = parser.getName();
        if (tagName.equals("package")) {
            readPackageLPw(parser);
        } else if (tagName.equals("permissions")) {
            mPermissions.readPermissions(parser);
        } else if (tagName.equals("permission-trees")) {
            mPermissions.readPermissionTrees(parser);
        } else if (tagName.equals("shared-user")) {
            readSharedUserLPw(parser);
        }...
    }
    str.close();
    ...
    return true;
}

創(chuàng)建SharedUserSetting對象并添加到Settings的成員變量mSharedUsers中促绵,在Android系統(tǒng)中,多個package通過設(shè)置shareUserid屬性可以運行在同一進程嘴纺,共享同一個UID败晴。

mSettings.addSharedUserLPw("android.uid.system", //字符串
                           Process.SYSTEM_UID, //系統(tǒng)進程使用的用戶id,值為1000
                           ApplicationInfo.FLAG_SYSTEM, //標志系統(tǒng) Package
                           ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); //特權(quán)系統(tǒng)應(yīng)用
 
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
    //mSharedUsers 是一個 HashMap颖医,key 為字符串位衩,值為 SharedUserSetting 對象
    SharedUserSetting s = mSharedUsers.get(name);
    if (s != null) {
        if (s.userId == uid) {
            return s;
        }
        PackageManagerService.reportSettingsProblem(Log.ERROR,
                "Adding duplicate shared user, keeping first: " + name);
        return null;
    }
    //創(chuàng)建一個新的 SharedUserSetting 對象裆蒸,并設(shè)置的 userId 為 uid
    s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
    s.userId = uid;
    if (registerExistingAppIdLPw(uid, s, name)) {
        mSharedUsers.put(name, s);//將name與s鍵值對添加到mSharedUsers中保存
        return s;
    }
    return null;
}
階段2:BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

主要工作:

  • 從init.rc中獲取環(huán)境變量BOOTCLASSPATH和SYSTEMSERVERCLASSPATH熔萧;
  • 對于舊版本升級的情況,將安裝時獲取權(quán)限變更為運行時申請權(quán)限
  • 掃描system/vendor/product/odem/oem 等目錄的priv-app ,app ,overlay包
  • 清除安裝時臨時文件以及其他不必要的信息。
public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
    synchronized (mInstallLock) {
        synchronized (mPackages) {
            // 記錄掃描開始時間
            long startTime = SystemClock.uptimeMillis();
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);
            
            //獲取環(huán)境變量佛致,init.rc
            final String bootClassPath = System.getenv("BOOTCLASSPATH");
            final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
            ...
            // 獲取system/framework目錄
            File frameworkDir = new File(Environment.getRootDirectory(), "framework");
            // 獲取內(nèi)部版本
            final VersionInfo ver = mSettings.getInternalVersion();
            // 判斷fingerprint是否有更新
            mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
            ...
            // 對于Android M之前版本升級上來的情況贮缕,需將系統(tǒng)應(yīng)用程序權(quán)限從安裝升級到運行時
            mPromoteSystemApps =
                    mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
            // 對于Android N之前版本升級上來的情況,需像首次啟動一樣處理package
            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;
            // 在掃描之前保存預(yù)先存在的系統(tǒng)package的名稱俺榆,不希望自動為新系統(tǒng)應(yīng)用授予運行時權(quán)限
            if (mPromoteSystemApps) {
                Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
                while (pkgSettingIter.hasNext()) {
                    PackageSetting ps = pkgSettingIter.next();
                    if (isSystemApp(ps)) {
                        mExistingSystemPackages.add(ps.name);
                    }
                }
            }
            // 準備解析package的緩存
            mCacheDir = preparePackageParserCache();
            // 設(shè)置flag感昼,而不在掃描安裝時更改文件路徑
            int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
            ...
            //掃描以下路徑:
            /vendor/overlay、/product/overlay罐脊、/product_services/overlay定嗓、/odm/overlay、/oem/overlay萍桌、/system/framework
            /system/priv-app宵溅、/system/app、/vendor/priv-app上炎、/vendor/app恃逻、/odm/priv-app、/odm/app藕施、/oem/app寇损、/oem/priv-app、
            /product/priv-app裳食、/product/app矛市、/product_services/priv-app、/product_services/app胞谈、/product_services/priv-app
    
            scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),...);
            scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),...);
            scanDirTracedLI(new File(PRODUCT_SERVICES_OVERLAY_DIR),...);
            scanDirTracedLI(new File(ODM_OVERLAY_DIR),...);
            scanDirTracedLI(new File(OEM_OVERLAY_DIR),...);
            ...
            final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
            final List<String> stubSystemApps = new ArrayList<>();
            // 刪掉不存在的package
            if (!mOnlyCore) {
                final Iterator<PackageParser.Package> pkgIterator = mPackages.values().iterator();
                while (pkgIterator.hasNext()) {
                    final PackageParser.Package pkg = pkgIterator.next();
                    if (pkg.isStub) {
                        stubSystemApps.add(pkg.packageName);
                    }
                }
                final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
                while (psit.hasNext()) {
                    PackageSetting ps = psit.next();
                    // 如果不是系統(tǒng)應(yīng)用尘盼,則不被允許disable
                    if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
                        continue;
                    }
                    
                    // 如果應(yīng)用被掃描,則不允許被擦除
                    final PackageParser.Package scannedPkg = mPackages.get(ps.name);
                    if (scannedPkg != null) {
                        // 如果系統(tǒng)應(yīng)用被掃描且存在disable應(yīng)用列表中烦绳,則只能通過OTA升級添加
                        if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
                            ...
                            removePackageLI(scannedPkg, true);
                            mExpectingBetter.put(ps.name, ps.codePath);
                        }
                        continue;
                    }
                    ...
                }
            }
            // 刪除臨時文件
            deleteTempPackageFiles();
            // 刪除沒有關(guān)聯(lián)應(yīng)用的共享UID標識
            mSettings.pruneSharedUsersLPw();
            ...
        }
        ...
    }
    ...
}

說明下:[system/app/與system/priv-app/的區(qū)別] 在system/priv-app目錄主要是存放手機廠商定制的系統(tǒng)的系統(tǒng)級應(yīng)用卿捎,比如phone app,settings app,systemui app等径密,這些應(yīng)用需要系統(tǒng)及權(quán)限午阵,而又不能被用戶卸載掉。這個目錄是在Android KitKat新增加的分區(qū)享扔。在KitKat之前版本在系統(tǒng)分區(qū)的所有apks都可以使用系統(tǒng)權(quán)限底桂,這個更改使手機廠商能夠更好的控制捆綁軟件對敏感權(quán)限的訪問。手機廠商在定制一些系統(tǒng)軟件的時候軟件也會需要專門給priv-app添加selinux policy惧眠。當然應(yīng)用需要獲取系統(tǒng)權(quán)限還有其他的辦法籽懦,在AndroidManifest.xml文件中添加 android:sharedUserId="android.uid.sysytem",同時給該apk添加系統(tǒng)簽名,比如小米手機就需要給apk添加小米的系統(tǒng)權(quán)限氛魁。

階段3:BOOT_PROGRESS_PMS_DATA_SCAN_START

主要工作:解析核心應(yīng)用暮顺,處理data目錄下的應(yīng)用信息,更新捶码,刪除不必要的數(shù)據(jù)惫恼。

public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
    synchronized (mInstallLock) {
        synchronized (mPackages) {
            ...
            if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                //掃描/data/app 目錄
                scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
                ...
                 // Remove disable package settings for updated system apps that were
                // removed via an OTA. If the update is no longer present, remove the
                // app completely. Otherwise, revoke their system privileges.
                // 移除通過OTA刪除的更新系統(tǒng)應(yīng)用程序的禁用package設(shè)置
                // 如果更新不再存在档押,則完全刪除該應(yīng)用祈纯。否則,撤消其系統(tǒng)權(quán)限
                for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
                    final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
                    final PackageParser.Package pkg = mPackages.get(packageName);
                    final String msg;
 
                     // remove from the disabled system list; do this first so any future
                    // scans of this package are performed without this state
                      mSettings.removeDisabledSystemPackageLPw(packageName);
                    ...
                }
      

/*   Make sure all system apps that we expected to appear on the userdata partition actually showed up. If they never
                  appeared, crawl back and revive the system version.
                 */
                // 確保期望在userdata分區(qū)上顯示的所有系統(tǒng)應(yīng)用程序?qū)嶋H顯示
                // 如果從未出現(xiàn)過腕窥,需要回滾以恢復(fù)系統(tǒng)版本
                for (int i = 0; i < mExpectingBetter.size(); i++) {
                    final String packageName = mExpectingBetter.keyAt(i);
                    if (!mPackages.containsKey(packageName)) {
                        final File scanFile = mExpectingBetter.valueAt(i);
                        ...
                        mSettings.enableSystemPackageLPw(packageName);
                        try {
                              //掃描APK
                            scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
                        } catch (PackageManagerException e) {
                            Slog.e(TAG, "Failed to parse original system package: "
                                    + e.getMessage());
                        }
                    }
                }
                 // Uncompress and install any stubbed system applications.
                // This must be done last to ensure all stubs are replaced or disabled.
                // 解壓縮并安裝任何存根系統(tǒng)應(yīng)用程序掀淘。必須最后執(zhí)行此操作以確保替換或禁用所有存根
                installSystemStubPackages(stubSystemApps, scanFlags);
                ...
                // 獲取storage manager包名
                mStorageManagerPackage = getStorageManagerPackageName();

               // Resolve protected action filters. Only the setup wizard is allowed to
            // have a high priority filter for these actions.
                // 解決受保護的action過濾器革娄。只允許setup wizard(開機向?qū)В檫@些action設(shè)置高優(yōu)先級過濾器
                mSetupWizardPackage = getSetupWizardPackageName();
                ...

             // Now that we know all of the shared libraries, update all clients to have
            // the correct library paths.
                // 更新客戶端以確保持有正確的共享庫路徑
                updateAllSharedLibrariesLocked(null, Collections.unmodifiableMap(mPackages));
                ...
                // 讀取并更新要保留的package的上次使用時間
                mPackageUsage.read(mPackages);
                mCompilerStats.read();
            }
        }
    }
}
階段四:BOOT_PROGRESS_PMS_SCAN_END

主要工作:

  • sdk版本變更拦惋,更新權(quán)限
  • OTA升級后首次啟動安寺,清除不必要的緩存數(shù)據(jù)
  • 權(quán)限等默認更新后挑庶,清理相關(guān)數(shù)據(jù)
  • 更新package.xml
 
public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
    synchronized (mInstallLock) {
        synchronized (mPackages) {
            ...
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                    SystemClock.uptimeMillis());
            // If the platform SDK has changed since the last time we booted,
            // we need to re-grant app permission to catch any new ones that
            // appear.  This is really a hack, and means that apps can in some
            // cases get permissions that the user didn't initially explicitly
            // allow...  it would be nice to have some better way to handle
            // this situation.
            // 如果自上次啟動以來迎捺,平臺SDK已改變凳枝,則需要重新授予應(yīng)用程序權(quán)限以捕獲出現(xiàn)的任何新權(quán)限
            final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
            mPermissionManager.updateAllPermissions(
                    StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, mPackages.values(),
                    mPermissionCallback);
            ...

            // If this is the first boot or an update from pre-M, and it is a normal
            // boot, then we need to initialize the default preferred apps across
            // all defined users.
            // 如果這是第一次啟動或來自Android M之前的版本的升級岖瑰,并且它是正常啟動,那需要在所有已定義的用戶中初始化默認的首選應(yīng)用程序
            if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
                for (UserInfo user : sUserManager.getUsers(true)) {
                    mSettings.applyDefaultPreferredAppsLPw(user.id);
                    primeDomainVerificationsLPw(user.id);
                }
            }


            // Prepare storage for system user really early during boot,
            // since core system apps like SettingsProvider and SystemUI
            // can't wait for user to start
            // 在啟動期間為系統(tǒng)用戶準備存儲率挣,因為像SettingsProvider和SystemUI這樣的核心系統(tǒng)應(yīng)用程序無法等待用戶啟動
            final int storageFlags;
            if (StorageManager.isFileEncryptedNativeOrEmulated()) {
                storageFlags = StorageManager.FLAG_STORAGE_DE;
            } else {
                storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
            }
            ...


             // If this is first boot after an OTA, and a normal boot, then
            // we need to clear code cache directories.
            // Note that we do *not* clear the application profiles. These remain valid
            // across OTAs and are used to drive profile verification (post OTA) and
            // profile compilation (without waiting to collect a fresh set of profiles).
            // 如果是在OTA之后首次啟動娃圆,并且正常啟動蛾茉,那需要清除代碼緩存目錄谦炬,但不清除應(yīng)用程序配置文件
            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)) {
                        // No apps are running this early, so no need to freeze
                        clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
                                FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
                                        | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                    }
                }
                ver.fingerprint = Build.FINGERPRINT;
            }
            
             // Grandfather existing (installed before Q) non-system apps to hide
            // their icons in launcher.
            //安裝Android-Q前的非系統(tǒng)應(yīng)用程序在Launcher中隱藏他們的圖標
            if (!onlyCore && mIsPreQUpgrade) {
                Slog.i(TAG, "Whitelisting all existing apps to hide their icons");
                int size = mSettings.mPackages.size();
                for (int i = 0; i < size; i++) {
                    final PackageSetting ps = mSettings.mPackages.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
            // 僅在權(quán)限或其它默認配置更新后清除
            mExistingSystemPackages.clear();
            mPromoteSystemApps = false;
            ...
          
             // All the changes are done during package scanning.
             // 所有變更均在掃描過程中完成
            ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;

              // can downgrade to reader
            //降級去讀取
            mSettings.writeLPr();
        }
    }
}
階段五:BOOT_PROGRESS_PMS_READY

主要工作有:

  • 創(chuàng)建PackageInstallerService對
  • GC回收內(nèi)存
 
public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
    synchronized (mInstallLock) {
        synchronized (mPackages) {
            ...
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                    SystemClock.uptimeMillis());
            ...

            // PermissionController hosts default permission granting and role management, so it's a critical part of the core system.

            //PermissionController 持有默認的權(quán)限授予和角色管理,所以這是核心系統(tǒng)的一個關(guān)鍵部分叫搁。
            mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
            ...
            updateInstantAppInstallerLocked(null);

            // Read and update the usage of dex files.
            // Do this at the end of PM init so that all the packages have their data directory reconciled.
            // At this point we know the code paths of the packages, so we can validate
            // the disk file and build the internal cache.
            // The usage file is expected to be small so loading and verifying it should take a fairly small time compare to the other activities (e.g. package scanning).
            // 閱讀并更新dex文件的用法
            // 在PM init結(jié)束時執(zhí)行此操作渴逻,以便所有程序包都已協(xié)調(diào)其數(shù)據(jù)目錄
            // 此時知道了包的代碼路徑惨奕,因此可以驗證磁盤文件并構(gòu)建內(nèi)部緩存
            // 使用文件預(yù)計很小梨撞,因此與其他活動(例如包掃描)相比,加載和驗證它應(yīng)該花費相當小的時間
            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) {
                MetricsLogger.histogram(null, "ota_package_manager_init_time",
                        (int) (SystemClock.uptimeMillis() - startTime));
            }
        }
    }
    ...

     // Now after opening every single application zip, make sure they are all flushed.  Not really needed, but keeps things nice and  tidy.
    // 打開應(yīng)用之后,及時回收處理
    Runtime.getRuntime().gc();

          // The initial scanning above does many calls into installd while
        // holding the mPackages lock, but we're mostly interested in yelling
        // once we have a booted system.
        // 上面的初始掃描在持有mPackage鎖的同時對installd進行了多次調(diào)用
    mInstaller.setWarnIfHeld(mPackages);
    ...
}

DEX優(yōu)化

檢查是否需要去更新Packages并進行dex優(yōu)化嗜侮,如果沒有OTA升級啥容,沒有大版本升級咪惠,沒有清除dalvik虛擬機緩存,可以去更新packages覆醇。最終調(diào)用的是Installer的dexopt進行優(yōu)化

PKMS.java#updatePackagesIfNeeded()

更新packages的優(yōu)先級:core app> system app > other app,調(diào)用performDexOptUpgrade()進行系統(tǒng)更新

public void updatePackagesIfNeeded() {
     //只有system或是root用戶才有權(quán)限請求 package update
    enforceSystemOrRoot("Only the system can request package update");
 
    //(1)判斷有沒有進行OTA升級永脓,我們需要在OTA后重新提取常摧。
    boolean causeUpgrade = isDeviceUpgrading();
 
    //(2)是否是第一次啟動或是系統(tǒng)大版本升級
    boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;
 
    // We need to re-extract after a pruned cache, as AoT-ed files will be out of date.
    //(3)判斷是否有清除過dalvik虛擬機的緩存
    boolean causePrunedCache = VMRuntime.didPruneDalvikCache();
 
    //(4)如果上面的三個都沒有落午,那么就不進行任何操作
    if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
        return;
    }
 
    List<PackageParser.Package> pkgs;
    synchronized (mPackages) {
        //(5)按照package的優(yōu)先級進行排序,core app >system app > other app
        pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
    }
 
    final long startTime = System.nanoTime(); //記錄開始時間
    
    //進行dex優(yōu)化界拦,
    final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
                causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
                false /* bootComplete */);
    ...
}
PackageManagerService.java#performDexOptUpgrade()

判斷是否需要對package進行更新享甸,如果需要那么更新會按照優(yōu)先級完成dex優(yōu)化枪萄,最終調(diào)用Install的dexopt進行dex優(yōu)化猫妙。

 
private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
        final int compilationReason, boolean bootComplete) {
    ...
    //循環(huán)取出packages割坠,進行優(yōu)化
    for (PackageParser.Package pkg : pkgs) {
        numberOfPackagesVisited++;
 
        boolean useProfileForDexopt = false;
 
        //第一次啟動或是ota升級之后并且是系統(tǒng)應(yīng)用才會進行odex
        if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) {
            ...
        }
        ...
        //最終是調(diào)用 mInstaller.dexopt 完成優(yōu)化的彼哼,installd守護進程敢朱,installer安裝器和Installd通信
        int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
                pkg.packageName,
                pkgCompilationReason,
                dexoptFlags));
 
        switch (primaryDexOptStaus) {
            case PackageDexOptimizer.DEX_OPT_PERFORMED:
                numberOfPackagesOptimized++; //odex優(yōu)化完成的應(yīng)用
                break;
            case PackageDexOptimizer.DEX_OPT_SKIPPED:
                numberOfPackagesSkipped++; //跳過的應(yīng)用
                break;
            case PackageDexOptimizer.DEX_OPT_FAILED:
                numberOfPackagesFailed++; //失敗的應(yīng)用
                break;
            default:
                Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus);
                break;
        }
    }
    return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped,
            numberOfPackagesFailed };
}

磁盤維護

磁盤維護最終調(diào)用的是vold進行的fstrim進行清理操作

PackageManagerService.java#performFstrimIfNeeded()

主要執(zhí)行磁盤清理工作拴签,釋放磁盤空間

public void performFstrimIfNeeded() {
   //只有system或是root用戶才有權(quán)限請求fstrim
   enforceSystemOrRoot("Only the system can request fstrim");
 
// Before everything else, see whether we need to fstrim.
   //在其他事情之前蚓哩,看看我們是否需要fstrim
   try {
        //獲取StorageManager對象
       IStorageManager sm = PackageHelper.getStorageManager();
       if (sm != null) {
           boolean doTrim = false;
           //獲取執(zhí)行FTRIM間隔,默認是3天喜颁,可以通過setting provider更改這個時間
           long interval = android.provider.Settings.Global.getLong(
                   mContext.getContentResolver(),
                   android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,
                   DEFAULT_MANDATORY_FSTRIM_INTERVAL);
 
           if (interval > 0) {
               final long timeSinceLast = System.currentTimeMillis() - sm.lastMaintenance();
               if (timeSinceLast > interval) {
                   doTrim = true; //如果超過了三天就進行磁盤清理
                   Slog.w(TAG, "No disk maintenance in " + timeSinceLast
                           + "; running immediately");
               }
           }
           if (doTrim) {
               final boolean dexOptDialogShown;
               synchronized (mPackages) {
                   dexOptDialogShown = mDexOptDialogShown;
               }
               //如果不是第一次啟動半开,顯示一個提示框
               if (!isFirstBoot() && dexOptDialogShown) {
                   try {
                       ActivityManager.getService().showBootMessage(
                               mContext.getResources().getString(
                                       R.string.android_upgrading_fstrim), true);
                   } catch (RemoteException e) {
                   }
               }
               // 這里的sm是 StorageManagerService寂拆,發(fā)送消息H_FSTRIM給handler漓库,然后再向vold發(fā)送fstrim命
               sm.runMaintenance();
           }
       } else {
           Slog.e(TAG, "storageManager service unavailable!");
       }
   } catch (RemoteException e) {
       // Can't happen; StorageManagerService is local
   }
}

PKMS準備就緒

PackageManagerService.java#systemReady()

systemReady主要完成默認授權(quán)和更新package的信息渺蒿,通知等待pms的一些組件彪薛。

 
public void systemReady() {
    //只有system或是root用戶才有權(quán)限 聲稱system已經(jīng)ready
    enforceSystemOrRoot("Only the system can claim the system is ready");
 
    ContentObserver co = new ContentObserver(mHandler) {
        ...
    };
    co.onChange(true); //注冊一個監(jiān)聽
    ...
    synchronized (mPackages) {
 
        ArrayList<PreferredActivity> removed = new ArrayList<>();
        for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
            PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
            removed.clear();
            for (PreferredActivity pa : pir.filterSet()) {
                if (!mComponentResolver.isActivityDefined(pa.mPref.mComponent)) {
                    removed.add(pa);
                }
            }//移除不存在的先前用戶設(shè)置保存的優(yōu)先選擇的activity組件
            if (removed.size() > 0) {
                for (int r=0; r<removed.size(); r++) {
                    PreferredActivity pa = removed.get(r);
                    Slog.w(TAG, "Removing dangling preferred activity: "
                            + pa.mPref.mComponent);
                    pir.removeFilter(pa);
                }
                mSettings.writePackageRestrictionsLPr(
                        mSettings.mPreferredActivities.keyAt(i)); //寫入到文件中
            }
        }
 
        for (int userId : UserManagerService.getInstance().getUserIds()) {
            if (!mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) {
                grantPermissionsUserIds = ArrayUtils.appendInt(
                        grantPermissionsUserIds, userId);
            }
        }
    }
 
    sUserManager.systemReady(); //多用戶服務(wù) systemReady
    //升級所有已獲取的默認權(quán)限
    for (int userId : grantPermissionsUserIds) {
        mDefaultPermissionPolicy.grantDefaultPermissions(userId); //默認授權(quán)操作
    }
    ...
 
    // Now that we've scanned all packages, and granted any default
    // permissions, ensure permissions are updated. Beware of dragons if you
    // try optimizing this.
    synchronized (mPackages) {
        mPermissionManager.updateAllPermissions(
                StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
                mPermissionCallback); //更新權(quán)限信息
        ...
        });
    }
 
    //注意隨時間變化的外部存儲設(shè)備
    final StorageManager storage = mContext.getSystemService(StorageManager.class);
    storage.registerListener(mStorageListener);
 
    mInstallerService.systemReady();
    mApexManager.systemReady();
    mPackageDexOptimizer.systemReady();
    ...

 // Now that we're mostly running, clean up stale users and apps
    //清除過期的userid和app
    sUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
    reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
 
    mPermissionManager.systemReady();
 
    if (mInstantAppResolverConnection != null) {
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                mInstantAppResolverConnection.optimisticBind();
                mContext.unregisterReceiver(this);
            }
        }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
    }
 
    if (!isFirstBoot()) {
        Slog.i(TAG, "enablePackageStoppedPolicy");
        synchronized (mPackages) {
            PackageManagerServiceUtilsZTE.enablePackageStoppedPolicy(mPackages.values(), this);
        }
    }
 
    mModuleInfoProvider.systemReady();
 
    // Installer service might attempt to install some packages that have been staged for
    // installation on reboot. Make sure this is the last component to be call since the
    // installation might require other components to be ready.
    mInstallerService.restoreAndApplyStagedSessionIfNeeded();
 
    //update partner app
    new Thread(() -> {
        updatePartnerAppInFota();
    }).start();
}

大概流程就是這樣,完彼妻。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末侨歉,一起剝皮案震驚了整個濱河市幽邓,隨后出現(xiàn)的幾起案子殖熟,更是在濱河造成了極大的恐慌倦挂,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件重斑,死亡現(xiàn)場離奇詭異窥浪,居然都是意外死亡,警方通過查閱死者的電腦和手機假颇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門笨鸡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來形耗,“玉大人辙浑,你說我怎么就攤上這事判呕。” “怎么了辱挥?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵晤碘,是天一觀的道長哼蛆。 經(jīng)常有香客問我霞赫,道長端衰,這世上最難降的妖魔是什么旅东? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任抵代,我火速辦了婚禮,結(jié)果婚禮上案腺,老公的妹妹穿的比我還像新娘劈榨。我一直安慰自己,他們只是感情好拷姿,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布响巢。 她就那樣靜靜地躺著踪古,像睡著了一般靶衍。 火紅的嫁衣襯著肌膚如雪颅眶。 梳的紋絲不亂的頭發(fā)上涛酗,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天商叹,我揣著相機與錄音只泼,去河邊找鬼请唱。 笑死十绑,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的扳躬。 我是一名探鬼主播贷币,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼片择,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了啰挪?” 一聲冷哼從身側(cè)響起亡呵,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤锰什,失蹤者是張志新(化名)和其女友劉穎汁胆,沒想到半個月后霜幼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體罪既,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡琢感,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年驹针,在試婚紗的時候發(fā)現(xiàn)自己被綠了柬甥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖撤防,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情无牵,我是刑警寧澤茎毁,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布七蜘,位于F島的核電站墙懂,受9級特大地震影響损搬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嵌灰,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一沽瞭、第九天 我趴在偏房一處隱蔽的房頂上張望秕脓。 院中可真熱鬧儒搭,春花似錦搂鲫、人聲如沸魂仍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至园骆,卻和暖如春锌唾,著一層夾襖步出監(jiān)牢的瞬間夺英,已是汗流浹背痛悯。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工灸蟆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留炒考,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓帘靡,卻偏偏與公主長得像瓤帚,于是被迫代替她去往敵國和親戈次。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345