深刻Android系統(十)PMS-1-服務初始化

導讀

PMS筆記大小寫到106kb時,就知大事不妙,導讀叒叒叒來了java

不得已,將本來計劃一篇結束的文章分爲了以下三部分:android

前兩篇主要梳理了PMS初始化的流程和初始化的一些細節;最後一篇對應用的安裝過程進行了簡單梳理。shell

有沒有注意梳理字眼,關於想要深刻全面學習PMS的同窗只能很是抱歉,本系列文章也僅僅是起到的主要流程的梳理做用。緩存

咱們先從總體上掌握,等後面遇到相關需求再來仔細研究吧(這波安慰很及時。。。。)markdown

整個模塊學習下來,簡單對PackageManagerService吐槽一下:app

  • PMS中涉及的類比較多,真正掌握須要一番時間和精力
    • 相關的類像:Package相關、Session相關、Settings等等
  • 閱讀PMS的方法有點拆俄羅斯套娃的感受,一層又一層。。。。
  • 業務邏輯複雜,幾乎每一個方法的執行會夾雜着各類權限用戶的邏輯判斷

有了上面的預期,歡迎來到PackageManagerService的世界ide

瞭解PackageManagerService

PackageManagerService代碼行數在24000行。。。。。函數

Android的應用管理主要是經過PackageManagerService來完成的。PackageManagerService負責各類APK包的安裝、卸載、優化和查詢。oop

Android中的應用能夠簡單分爲兩大類:系統應用普通應用post

  • 系統應用是指位於/system/app/system/priv-app目錄下的應用
    • /system/priv-app是從Android 4.4開始出現的目錄,存放的是一些系統底層的應用,如:SettingsSystemUI
    • /system/app存放的則是一些系統級的應用,如CalendarContacts
  • 普通應用是用戶安裝的應用,位於目錄/data/app

PackageManagerService在啓動時會掃描全部APK文件和Jar包,而後把它們的信息讀取到內存中,這樣系統在運行時就能迅速找到各類應用和組件信息。

  • 掃描過程若是遇到沒有優化的文件,還要執行優化操做。

    Android 5.0開始引入了ARTART 使用預先 (AOT) 編譯,而且從 Android 7.0開始結合使用 AOT即時 (JIT) 編譯配置文件引導型編譯來優化應用的啓動和執行效率

不着急直接分析源代碼,咱們先從使用的角度簡單瞭解下

在應用中,若是要使用PackageManagerService服務,一般是調用ContextgetPackageManager()方法,內容以下:

public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }
        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }

        return null;
    }
複製代碼

調用到了ActivityThread.getPackageManager()的方法:

public static IPackageManager getPackageManager() {
        if (sPackageManager != null) {
            return sPackageManager;
        }
        IBinder b = ServiceManager.getService("package");
        sPackageManager = IPackageManager.Stub.asInterface(b);
        return sPackageManager;
    }
複製代碼

從這兩個getPackageManager()方法的代碼中能夠看到:

  • 返回的是ApplicationPackageManager對象,這個對象建立時使用了IPackageManager對象做爲參數

    • IPackageManager對象是PackageManagerServiceBinder代理對象
  • ApplicationPackageManager繼承自PackageManager,在PackageManager中定義了應用能夠訪問PackageManagerService的全部接口

PackageManager關係圖以下:

image

而對於PackageManagerService類來講,有兩個重要成員變量:

  • mInstallerService:類PackageInstallerService的實例對象。Android經過PackageInstallerService來管理應用的安裝過程。

    • PackageInstallerService也是一個Binder服務,對應的代理對象是PackageInstaller
  • mInstaller:類Installer的實例對象。類結構相對簡單,有一個IInstalld mInstalld變量,經過Binder調用來和installd進程通訊

    • 實際上系統中進行apk文件格式轉換、創建數據目錄等工做最後都是由installd進程來完成的

上述這幾個對象之間的關係圖以下:

image

Settings類和packages.xml

在開始分析PackageManagerService前,咱們要先看下Settings類,這個類用來保存和PackageManagerService相關的一些設置,它保存的內容在解析應用時會用到。

先看看com.android.server.pm.Settings類的構造方法:

Settings(File dataDir, PermissionSettings permission, Object lock) {
        // 省略權限相關的處理
        ......
        // 在data目錄下建立system目錄
        mSystemDir = new File(dataDir, "system");
        mSystemDir.mkdirs();
        // 設置目錄的屬性爲0775
        FileUtils.setPermissions(mSystemDir.toString(),
                FileUtils.S_IRWXU|FileUtils.S_IRWXG
                |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                -1, -1);
        mSettingsFilename = new File(mSystemDir, "packages.xml");
        mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
        mPackageListFilename = new File(mSystemDir, "packages.list");
        FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
        ......
        // Deprecated: Needed for migration
        mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
        mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
    }
複製代碼

Settings類的構造方法會在/data目錄下建立system目錄用來保存一些系統配置文件。建立了5個位於目錄/data/systemFile對象:

  • packages.xml:記錄系統中全部安裝的應用信息,包括基本信息、簽名和權限。
  • packages-backup.xmlpackages.xml文件的備份
  • packages.list:保存普通應用的數據目錄和uid等信息
  • packages-stopped.xml:記錄系統中被強制中止運行的應用信息。系統在強制中止某個應用時,會將應用的信息記錄到文件中
  • packages-stopped-backup.xmlpackages-stopped.xml文件的備份

packages-backup.xmlpackages-stopped-backup.xml是備份文件。關於備份文件的邏輯是:

  • 當Android對文件packages.xmlpackages-stopped.xml寫以前,會把他們備份
  • 若是文件寫成功了,再把備份文件刪除掉
  • 若是寫的時候系統出了問題,重啓後再須要讀取這兩個文件時,若是發現備份文件存在,會使用備份文件的內容,由於原文件可能已經損壞了。

packages.xmlPackageManagerService啓動時須要用到的文件,咱們先看看文件的格式:

<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="28" sharedUserId="1001" isOrphaned="true">
        <sigs count="1" schemeVersion="3">
            <cert index="0" />
        </sigs>
        <perms>
            <item name="android.permission.USE_RESERVED_DISK" granted="true" flags="0" />
            <item name="android.permission.INTERACT_ACROSS_USERS_FULL" granted="true" flags="0" />
            <item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" />
            <item name="android.permission.MODIFY_PHONE_STATE" granted="true" flags="0" />
        </perms>
        <proper-signing-keyset identifier="1" />
    </package>
複製代碼

上面是文件中的一個<package/>標籤,表示一個應用的基本信息、簽名和聲明的權限,從內容來看基本都是解析AndroidManifest.xml的內容。

咱們看下主要屬性和標籤:

  • name表示應用的包名
  • codePath表示apk文件的位置
  • nativeLibraryPath表示應用的native庫的儲存路徑
  • it表示應用安裝時間
  • ut表示最後一次修改的時間
  • version表示應用的版本號
  • sharedUserId表示應用用來共享的用戶ID
  • userId表示應用所屬的用戶ID
  • <sign/>表示應用的簽名
    • 屬性count表示標籤中包含有多少個證書
    • 標籤<cert/>表示具體證書的值
  • <perms/>表示應用聲明使用的權限
    • 每一個子標籤<item/>表明一項權限

除了<package/>標籤,packages.xml中還可能存在一下標籤:

  • <updated-package/>:記錄系統應用升級的狀況。當安裝了一個包名相同、版本號更高的應用後,Android 會經過此標籤記錄被覆蓋的系統應用的信息。
  • <renamed-package/>:記錄變動應用包名的狀況。
    • 應用的包名一般是在AndroidManifest.xml中使用屬性package來指定,同時在AndroidManifest.xml中還能夠用<original-package>來指定原始包名,對於這種狀況:
      • 當設備上存在低版本且包名與原始包名相同的應用時,會升級覆蓋原始應用。
      • 最後運行的是新安裝的應用,可是運行時應用的包名仍是原始的包名。Android會經過<renamed-package/>標籤來記錄這種更名的狀況。
  • <cleaning-package/>:用來記錄那些已經刪除,可是數據目錄還暫時保留的應用的信息

對於<package/><updated-package/>標籤,解析後的數據將保存在PackageSetting的對象中。PackageSetting類的繼承關係圖以下:

image

  • PackageSetting繼承了PackageSettingBase類,PackageSettingBase又繼承了SettingBase
    • 應用的基本信息保存在PackageSettingBase類的成員變量中
    • 申明的權限保存在SettingBasemPermissionState
  • 簽名信息保存在SharedUserSetting類的成員變量signatures
  • SharedUserSetting用來描述具備相同sharedUserId的應用信息
    • 它的成員變量packages是用來保存全部具備相同sharedUserId應用對象的集合
    • 這些應用的簽名是相同的,所以只須要經過一個成員變量signatures保存便可

表明標籤<package/>PackageSetting對象都會保存在Settings的成員變量mPackages中,定義以下:

final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();
複製代碼

表明標籤<updated-package/>PackageSetting對象都會保存在Settings的成員變量mDisabledSysPackages中,定義以下:

private final ArrayMap<String, PackageSetting> mDisabledSysPackages = new ArrayMap<String, PackageSetting>();
複製代碼

表明標籤<cleaning-package/>PackageSetting對象都會保存在Settings的成員變量mPackagesToBeCleaned中,定義以下:

final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
複製代碼

表明標籤<renamed-package/>PackageSetting對象都會保存在Settings的成員變量mRenamedPackages中,定義以下:

private final ArrayMap<String, String> mRenamedPackages = new ArrayMap<String, String>();
複製代碼

這四個對象在PackageManagerService中會常常遇到,爲了方便後面的理解,請記一下哈

PackageManagerService的初始化

有了前面的鋪墊,咱們來看下PackageManagerService的初始化過程。

PackageManagerService是在SystemServer中初始化的,分別在startBootstrapServices()startOtherServices()中都進行了一些處理。相關代碼以下:

private void startBootstrapServices() {
    ......
    // 啓動 installd,PMS依賴此服務
    Installer installer = mSystemServiceManager.startService(Installer.class);
    ......
    // 判斷vold.decrypt是否被設定爲加密狀態
    // mOnlyCore=true表示加密狀態,只能處理系統應用
    // 通常狀況mOnlyCore爲false
    String cryptState = SystemProperties.get("vold.decrypt");
    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;
    }
    // 調用main()初始化PMS
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    mFirstBoot = mPackageManagerService.isFirstBoot();
    ......
    // 若是設備沒有加密,嘗試啓動 OtaDexoptService
    // OtaDexoptService 是用於A/B更新的一個服務,會用到PMS
    if (!mOnlyCore) {
        boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
                    false);
        if (!disableOtaDexopt) {
                OtaDexoptService.main(mSystemContext, mPackageManagerService);
        }
    }
    ......
}
private void startOtherServices() {
    if (!mOnlyCore) {
        ......
        // 進行dex優化
        mPackageManagerService.updatePackagesIfNeeded();
    }
    ......
    // 進行磁盤維護
    mPackageManagerService.performFstrimIfNeeded();
    ......
    // PMS 準備就緒
    mPackageManagerService.systemReady();
}
複製代碼

上面就是SystemServer中和PackageManagerService啓動相關的操做, 咱們先看下PackageManagerServicemain()方法

PackageManagerServicemain()方法

main()方法內容以下:

public static PackageManagerService main(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
        // Self-check for initial settings.
        PackageManagerServiceCompilerMapping.checkProperties();
        // 建立 PackageManagerService 對象
        PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore);
        // 啓用部分系統應用
        m.enableSystemUserPackages();
        // 將服務添加到ServiceManager
        // 包括package和package_native
        ServiceManager.addService("package", m);
        final PackageManagerNative pmn = m.new PackageManagerNative();
        ServiceManager.addService("package_native", pmn);
        return m;
    }
複製代碼

main()方法比較簡單,主要進行了:

  • 建立PackageManagerService對象
    • 核心對象,須要詳細分析
  • 建立PackageManagerNative對象,只是簡單提供了三個功能接口
    • String[] getNamesForUids
    • String getInstallerForPackage
    • long getVersionCodeForPackage
  • 而後將兩個對象添加到ServiceManager

咱們來重點看下PackageManagerService的建立過程,也就是構造方法。

PackageManagerService的構造方法

PackageManagerService的構造方法比較長,根據構造方法中的EventLogTags咱們能夠劃分爲5個階段:

public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
        ......
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,SystemClock.uptimeMillis());
        ......
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, startTime);
        ......
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis());
        ......
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis());
        ......
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis());
        ......
    }
複製代碼

BOOT_PROGRESS_PMS_START

BOOT_PROGRESS_PMS_START階段主要進行了:

  • 建立DisplayMetrics對象、設置Installer引用
  • 建立PermissionManager對象
  • 根據permission信息建立Settings對象,並添加部分uid信息
  • 建立DexManager對象跟蹤dex使用狀況
  • 建立SystemConfig對象用來獲取系統配置信息,如:共享庫列表、權限等
  • 建立PackageHandler對象,實際上是一個HandlerThread類,並添加到Watchdog
  • 對於首次啓動,將預編譯文件拷貝到data分區

具體代碼以下:

// ###### BOOT_PROGRESS_PMS_START 階段 ###### 
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,SystemClock.uptimeMillis());
    // 檢測SDK版本
    if (mSdkVersion <= 0) {
        Slog.w(TAG, "**** ro.build.version.sdk not set!");
    }
    mContext = context;
    // 是否爲工廠模式,工廠模式主要用於系統功能檢測
    // 能夠進行一些按鍵轉義等操做,正式產品通常都爲false
    mFactoryTest = factoryTest;
    // 是否只處理系統應用,通常爲false
    mOnlyCore = onlyCore;
    // 建立DisplayMetrics對象儲存屏幕顯示信息
    mMetrics = new DisplayMetrics();
    // 設置installer
    mInstaller = installer;
    // Create sub-components that provide services / data. Order here is important.
    synchronized (mInstallLock) {
    synchronized (mPackages) {
        // Expose private service for system components to use.
        // 添加PackageManagerInternal到本地服務
        LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
        // 初始化多用戶管理服務
        sUserManager = new UserManagerService(......);
        // 初始化權限管理服務
        mPermissionManager = PermissionManagerService.create(......);
        mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
        // 建立Settings對象
        mSettings = new Settings(mPermissionManager.getPermissionSettings(), mPackages);
    }
    }
    // 經過addSharedUserLPw方法添加SharedUserSetting對象到Settings中
    // sharedUserId 屬性相同的包能夠運行在同一個進程,或者能夠相互讀取資源
    // 這裏添加了一共是7種系統的 uid:system、phone、log、nfc、bluetooth、shell、se
    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);
    ......
    // 建立dexopt命令的幫助類
    mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
            "*dexopt*");
    DexManager.Listener dexManagerListener = DexLogger.getListener(this,
            installer, mInstallLock);
    // 建立 DexManager 用來跟蹤dex文件的使用狀況
    mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock, dexManagerListener);
    // 建立ART管理服務
    mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
    mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
    ......
    // 獲取默認屏幕參數
    getDefaultDisplayMetrics(context, mMetrics);
    // 建立 SystemConfig 對象,用來保存系統全局配置信息
    SystemConfig systemConfig = SystemConfig.getInstance();
    mAvailableFeatures = systemConfig.getAvailableFeatures();
    ......
    synchronized (mInstallLock) {
    // writer
    synchronized (mPackages) {
        // 建立用來處理消息的線程,並添加到Watchdog中監控
        mHandlerThread = new ServiceThread(TAG,
                Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
        mHandlerThread.start();
        mHandler = new PackageHandler(mHandlerThread.getLooper());
        ......
        Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
        // 建立即時應用管理模塊
        mInstantAppRegistry = new InstantAppRegistry(this);
        // 經過 systemConfig 獲取系統中的共享庫列表
        ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
        final int builtInLibCount = libConfig.size();
        for (int i = 0; i < builtInLibCount; i++) {
            String name = libConfig.keyAt(i);
            String path = libConfig.valueAt(i);
            addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
                    SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
        }
        // 打開 SELinux 的 Policy 文件:
        // 1. /security/mac_permissions.xml
        // 2. /etc/security/mac_permissions.xml
        SELinuxMMAC.readInstallPolicy();
        ......
        // 讀取packages.xml文件,解析後將數據存放到mSettings中
        mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

        // Clean up orphaned packages for which the code path doesn't exist
        ......
        if (mFirstBoot) {
            // 若是是首次啓動
            // 向Init進程請求(經過setProp的方式)
            // 將預編譯dex拷貝到data分區
            requestCopyPreoptedFiles();
        }
    }
    }
複製代碼

BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

BOOT_PROGRESS_PMS_SYSTEM_SCAN_START階段主要進行:

  • 獲取環境變量BOOTCLASSPATHSYSTEMSERVERCLASSPATH以及系統版本信息
  • 根據版本信息確認升級策略
    • Android M以前版本需調整邏輯爲運行時獲取
    • Android N以前版本須要調整爲首次啓動
  • 掃描vendor,productoverlay目錄,收集文件信息
  • 掃描vendor,product,system,oem等分區下的apppriv-app目錄,收集應用信息,並將蒐集到的信息保存到mPackages
  • PMS中的mPackages集合與mSettings中的mPackages集合進行比對,過濾掉無效的應用
  • 刪除臨時文件和未使用到的SharedUserSetting對象

相關代碼以下:

// ###### BOOT_PROGRESS_PMS_SYSTEM_SCAN_START 階段 ###### 
        long startTime = SystemClock.uptimeMillis();
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, startTime);

        final String bootClassPath = System.getenv("BOOTCLASSPATH");
        final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
        ......
        File frameworkDir = new File(Environment.getRootDirectory(), "framework");
        final VersionInfo ver = mSettings.getInternalVersion();
        mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
        ......
        // 對於Android M以前版本升級上來的狀況,需將系統應用程序權限從安裝升級到運行時
        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;
        // 掃描前保存已經存在的系統 packages,系統升級後需從新申請權限
        // save off the names of pre-existing system packages prior to scanning; we don't
        // want to automatically grant runtime permissions for new system apps
        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(mIsUpgrade);
        ......
        // 經過scanDirTracedLI掃描指定目錄的應用信息,包括:
        // vendor/overlay、/product/overlay
        // system/framework、system/priv-app、/system/app
        // vendor/priv-app、/vendor/app
        // odm/priv-app、/odm/app、
        // product/priv-app、/product/app
        // 並將相關的應用信息保存到mPackages中
        scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),...);
        scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),...);
        .....
        // Prune any system packages that no longer exist.
        final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
        // Stub packages must either be replaced with full versions in the /data
        // partition or be disabled.
        final List<String> stubSystemApps = new ArrayList<>();
        if (!mOnlyCore) {
            // do this first before mucking with mPackages for the "expecting better" case
            final Iterator<PackageParser.Package> pkgIterator = mPackages.values().iterator();
            while (pkgIterator.hasNext()) {
                final PackageParser.Package pkg = pkgIterator.next();
                if (pkg.isStub) {
                    stubSystemApps.add(pkg.packageName);
                }
            }
            // 循環處理mSettings.mPackages中的應用信息
            final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
            while (psit.hasNext()) {
                PackageSetting ps = psit.next();
                if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
                    // 忽略普通應用
                    continue;
                }
                // 請注意,此處的mPackages是在PMS中的成員變量
                // 裏面存放的是scanDir掃描目錄後獲得的應用信息
                final PackageParser.Package scannedPkg = mPackages.get(ps.name);
                if (scannedPkg != null) {
                    // 若是mPackages的系統應用是待升級包的,把它從mPackages中移除
                    // disable說明是<update-package/>標籤表示的應用
                    if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
                        // 從掃描表mPackages中移除
                        removePackageLI(scannedPkg, true);
                        // 將其添加到mExpectingBetter集合中
                        // 在下一階段會處理
                        mExpectingBetter.put(ps.name, ps.codePath);
                    }
                    continue;
                }
                // 執行到此處說明mPackages中沒有ps應用信息
                // 也就是系統中不存在該應用
                // 檢查是否屬於<update-package/>標籤
                if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
                    // 不屬於<update-package/>標籤,說明是殘留在packages.xml中的
                    // 移除,後面還會進行數據清理
                    psit.remove();
                } else {
                    // 應用屬於<update-package/>標籤
                    // 添加到 possiblyDeletedUpdatedSystemApps 集合中
                    ......
                    possiblyDeletedUpdatedSystemApps.add(ps.name);
                }
            }
        }
        //delete tmp files
        deleteTempPackageFiles();
        final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get();
        // 刪除mSettings中沒有關聯任何應用的SharedUserSetting對象
        mSettings.pruneSharedUsersLPw();
        final long systemScanTime = SystemClock.uptimeMillis() - startTime;
        ......
複製代碼

BOOT_PROGRESS_PMS_DATA_SCAN_START

BOOT_PROGRESS_PMS_DATA_SCAN_START主要進行了:

  • 掃描用戶目錄/data/app/data/app-private下的應用信息,並添加到mPackages
  • 遍歷possiblyDeletedUpdatedSystemApps集合,並逐一將其從Settings的中disabledSystemPackage集合中刪除
    • possiblyDeletedUpdatedSystemApps集合中存放的是在packages.xml文件中被標記成了<update-package/>,可是應用文件還未找到的應用
    • 若是在用戶目錄下存在該應用升級文件,調整應用的權限爲普通應用
    • 若是不存在,清除應用相關數據
  • 遍歷mExpectingBetter集合。
    • mExpectingBetter集合中存放的是在packages.xml文件中被標記成了<update-package/>,資料數據齊全,不能直接啓動,須要配合用戶目錄下的完整版應用才能啓動
    • 此時根據集合中的應用名稱,從/data/vendor/system等目錄查找,找到後將其掃描到mPackages集合中
  • 讀取StorageManagerSetupWizard包名,更新全部應用的動態庫路徑

具體代碼以下:

if (!mOnlyCore) {
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis());
        // 掃描 /data/app 目錄,收集應用信息
        scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
        // 掃描 /data/app-private 目錄,收集應用信息
        scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
                | PackageParser.PARSE_FORWARD_LOCK,
                scanFlags | SCAN_REQUIRE_KNOWN, 0);
        // possiblyDeletedUpdatedSystemApps中存放的應用是在packages.xml文件中被標記成了已升級的系統應用,可是文件卻不在了
        // 所以這裏檢查用戶目錄下的升級文件是否存在,而後進行處理
        for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
            PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
            // 先將應用從<update-package/>集合中移除
            mSettings.removeDisabledSystemPackageLPw(deletedAppName);
            final String msg;
            if (deletedPkg == null) {
                // 用戶安裝目錄也不存在該升級文件
                msg = "Updated system package " + deletedAppName
                        + " no longer exists; removing its data";
            } else {
                // 用戶空間找到了升級文件,說明系統目錄下的文件可能被刪除了
                // 所以,把應用的系統屬性去掉,以普通應用的方式運行
                msg = "Updated system package + " + deletedAppName
                        + " no longer exists; revoking system privileges";
                final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
                deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
                deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
            }
            ......
        }
        // 放到 mExpectingBetter 列表中的應用是帶有升級包的
        // 在上一階段解析時把它們從 mPackages 列表中已經移除並放到了 mExpectingBetter 列表中
        for (int i = 0; i < mExpectingBetter.size(); i++) {
            final String packageName = mExpectingBetter.keyAt(i);
            if (!mPackages.containsKey(packageName)) {
                final File scanFile = mExpectingBetter.valueAt(i);
                final @ParseFlags int reparseFlags;
                final @ScanFlags int rescanFlags;
                // 從下面路徑中查找應用
                if (FileUtils.contains(privilegedAppDir, scanFile)) {
                    .....
                } else if (FileUtils.contains(systemAppDir, scanFile)) {
                    ......
                } else if (FileUtils.contains(privilegedVendorAppDir, scanFile)
                        || FileUtils.contains(privilegedOdmAppDir, scanFile)) {
                    ......
                } else if (FileUtils.contains(vendorAppDir, scanFile)
                        || FileUtils.contains(odmAppDir, scanFile)) {
                    ......
                } else if (FileUtils.contains(oemAppDir, scanFile)) {
                    ......
                } else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {
                    ......
                } else if (FileUtils.contains(productAppDir, scanFile)) {
                    ......
                } else {
                    // 若是沒找到,不作任何處理
                    continue;
                }
                mSettings.enableSystemPackageLPw(packageName);
                try {
                    // 掃描並處理該應用
                    scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
                } catch (PackageManagerException e) {
                    Slog.e(TAG, "Failed to parse original system package: " + e.getMessage());
                }
            }
        }
        // 此時用戶空間的掃描基本都已經完成
        // 解壓安裝 stub system app
        decompressSystemApplications(stubSystemApps, scanFlags);
        ......
    }
    mExpectingBetter.clear();
    // 獲取StorageManager包名
    mStorageManagerPackage = getStorageManagerPackageName();
    // 獲取SetupWizard 包名(開機引導)
    mSetupWizardPackage = getSetupWizardPackageName();
    ......
    // 更新全部應用的動態庫路徑
    updateAllSharedLibrariesLPw(null);
    ......
    // Now that we know all the packages we are keeping,
    // read and update their last usage times.
    mPackageUsage.read(mPackages);
    mCompilerStats.read();
複製代碼

BOOT_PROGRESS_PMS_SCAN_END

BOOT_PROGRESS_PMS_SCAN_END階段的主要進行了:

  • 檢查SDK版本,更新應用權限
  • 檢查啓動條件(首次正常啓動、基於Android M升級後正常啓動)設置默認首選應用
  • 獲取UUID_DEFAULT捲上的核心繫統應用包名,並準備相關的應用數據
  • 檢查是否爲OTA後首次啓動,是:進行cache清理
  • 最後,將mSetting中的數據更新到packages.xml

主要代碼以下:

EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis());
    // 若是平臺的SDK版本和上次啓動時相比發生了變化,可能permission的定義也改變了
    // 所以須要從新賦予應用權限
    final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
    mPermissionManager.updateAllPermissions(
            StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, mPackages.values(),
            mPermissionCallback);
    ver.sdkVersion = mSdkVersion;
    // 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.
    // 根據條件設定默認的首選應用
    if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
        for (UserInfo user : sUserManager.getUsers(true)) {
            mSettings.applyDefaultPreferredAppsLPw(this, user.id);
            applyFactoryDefaultBrowserLPw(user.id);
            primeDomainVerificationsLPw(user.id);
        }
    }
    ......
    // 獲取 UUID_DEFAULT 上的核心繫統應用包名集合
    List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,  UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */, true /* onlyCoreApps */);
    // 爲deferPackages集合中的應用準備應用數據
    mPrepareAppDataFuture = SystemServerInitThreadPool.get().submit(...);
    ......
    // 若是是執行OTA後的首次啓動,須要清除cache
    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(...);
            }
        }
        ver.fingerprint = Build.FINGERPRINT;
    }
    ......
    // 把 mSettings 內容保存到 packages.xml
    mSettings.writeLPr();
複製代碼

BOOT_PROGRESS_PMS_READY

BOOT_PROGRESS_PMS_READY階段內容比較簡單,方法內容以下:

EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis());
        ......
        // 建立 PackageInstallerService 
        mInstallerService = new PackageInstallerService(context, this);
        ......
        // 獲取每一個user對應的已安裝應用信息,填充到userPackages中
        final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();
        final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
        for (int userId : currentUserIds) {
            userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());
        }
        // 將userPackages添加到 mDexManager 中用來跟蹤dex使用信息
        mDexManager.load(userPackages);
        ......
        // 觸發垃圾回收
        Runtime.getRuntime().gc();
        ......
複製代碼

PMS構造方法總結

PackageManagerService構造方法的執行過程就是:

  • 先讀取packages.xml文件內容,解析並保存在成員變量mSettings中。
    • 這至關於加載設備上次運行時的應用狀態信息
  • 而後掃描設備中的幾個分區目錄下的應用文件,把掃描結果保存到PMSmPackages中。
    • 這記錄的是當前系統中的應用狀態信息
  • 後面就是對兩次的應用狀態信息進行:比對、從新調整、掃描特定目錄。
  • 最後將應用信息從新寫回packages.xml文件

調用時序圖以下(爲了簡潔,移除了部分方法):

image

startOtherServices()中的PMS操做

前面已經介紹,在startOtherServices()中,PMS還進行了以下三個操做:

  • mPackageManagerService.updatePackagesIfNeeded():檢查是否須要更新package
  • mPackageManagerService.performFstrimIfNeeded():執行磁盤清理,釋放空間
  • mPackageManagerService.systemReady():執行一些默認權限的檢查和package信息更新操做以及進行一些相關服務的狀態通知(*.systemReady())

咱們簡單來看下

updatePackagesIfNeeded()

updatePackagesIfNeeded()用來檢查是否須要去更新packages並進行dex優化

方法內容以下,註釋比較詳細:

public void updatePackagesIfNeeded() {
        enforceSystemOrRoot("Only the system can request package update");

        // We need to re-extract after an OTA.
        boolean causeUpgrade = isUpgrade();

        // First boot or factory reset.
        boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;

        // We need to re-extract after a pruned cache, as AoT-ed files will be out of date.
        boolean causePrunedCache = VMRuntime.didPruneDalvikCache();
        
        // 若是不是:OTA、首次啓動、升級到Android N、虛擬機緩存調整
        // 直接返回
        if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
            return;
        }
        List<PackageParser.Package> pkgs;
        synchronized (mPackages) {
            // 獲取安裝的Package集合
            pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
        }
        final long startTime = System.nanoTime();
        // 執行升級操做
        final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
                    causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
                    false /* bootComplete */);
        ......
    }
複製代碼

真正執行升級的方法是performDexOptUpgrade(),咱們看下:

private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, ...) {
        ......
        final int numberOfPackagesToDexopt = pkgs.size();
        for (PackageParser.Package pkg : pkgs) {
            numberOfPackagesVisited++;
            boolean useProfileForDexopt = false;
            if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) {
                // 針對系統app,若是是首次啓動或者升級的狀態
                // 先檢測應用的initial preopt profiles是否存在
                if (profileFile.exists()) {
                    ......
                    // 文件存在的話,經過Installer拷貝文件
                    mInstaller.copySystemProfile(profileFile.getAbsolutePath(), ...);
                } else {
                    // 文件不存在,檢查<update-package/>標籤中的應用
                    PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
                    if (disabledPs != null && disabledPs.pkg.isStub) {
                        // 若是標籤中存在,而且應用類型是Stub,查找特殊路徑
                        String systemProfilePath = getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, "");
                        profileFile = new File(systemProfilePath);
                        ......
                        mInstaller.copySystemProfile(profileFile.getAbsolutePath(),...);
                    }
                }
            }
            if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
                ......
                // 若是應用不支持優化,直接跳過
                continue;
            }
            ......
            // performDexOptTraced的調用關係及其噁心
            // 跟蹤代碼,最後調用的是
            // PackageDexOptimizer 類 dexOptPath 函數
            // 函數經過mInstaller.dexopt()函數來進行轉化
            // mInstaller.dexopt() 經過 Binder 遠程調用installd服務
            performDexOptTraced(new DexoptOptions(
                    pkg.packageName,
                    pkgCompilationReason,
                    dexoptFlags));
            ......
        }
        ......
    }
複製代碼

performFstrimIfNeeded()

performFstrimIfNeeded()用來進行磁盤清理工做,相關代碼以下:

public void performFstrimIfNeeded() {
        enforceSystemOrRoot("Only the system can request fstrim");

        // Before everything else, see whether we need to fstrim.
        try {
            // 獲取StorageManagerService對象
            IStorageManager sm = PackageHelper.getStorageManager();
            if (sm != null) {
                boolean doTrim = false;
                // 讀取fstrim的間隔,默認3天
                final 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;
                    }
                }
                if (doTrim) {
                    ......
                    // runMaintenance先經過sm的發送H_FSTRIM消息給Handler
                    // 而後mVold對象經過binder調用vold進程的fstrim函數
                    sm.runMaintenance();
                }
            } else {
                Slog.e(TAG, "storageManager service unavailable!");
            }
        } catch (RemoteException e) {
            // Can't happen; StorageManagerService is local
        }
    }
複製代碼

systemReady()

systemReady()主要進行的是權限相關的更新操做,以及一些服務狀態的變動。

相關代碼以下:

@Override
    public void systemReady() {
        enforceSystemOrRoot("Only the system can claim the system is ready");
        mSystemReady = true;
        ...... // 省略一個和InstantApp相關的 ContentObserver
        CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this, mContext.getContentResolver(), UserHandle.USER_SYSTEM);
        ......
        // 獲取並設置 compatibilityModeEnabled 兼容模式
        boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
                mContext.getContentResolver(),
                android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1;
        PackageParser.setCompatibilityModeEnabled(compatibilityModeEnabled);
        ......
        int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
        synchronized (mPackages) {
            // 檢查PreferredActivity是否存在,及時清理不存在的
            ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
            for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
                ......
            }
            // 檢查用戶權限,將不是默認權限的userID添加到 grantPermissionsUserIds 集合中
            for (int userId : UserManagerService.getInstance().getUserIds()) {
                if (!mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) {
                    grantPermissionsUserIds = ArrayUtils.appendInt(
                            grantPermissionsUserIds, userId);
                }
            }
        }
        sUserManager.systemReady();
        // 設置爲默認權限
        for (int userId : grantPermissionsUserIds) {
            mDefaultPermissionPolicy.grantDefaultPermissions(userId);
        }
        ......
        // 更新權限
        synchronized (mPackages) {
            mPermissionManager.updateAllPermissions(
                    StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
                    mPermissionCallback);
        }
        // 發送systemReady通知
        if (mPostSystemReadyMessages != null) {
            for (Message msg : mPostSystemReadyMessages) {
                msg.sendToTarget();
            }
            mPostSystemReadyMessages = null;
        }
        // 註冊外部儲存設備監聽
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        storage.registerListener(mStorageListener);
        // 發送關聯服務的 systemReady 通知
        mInstallerService.systemReady();
        mDexManager.systemReady();
        mPackageDexOptimizer.systemReady();
        // 獲取 StorageManager 服務並添加相關的狀態通知策略
        StorageManagerInternal StorageManagerInternal = LocalServices.getService(
                StorageManagerInternal.class);
        StorageManagerInternal.addExternalStoragePolicy(...);
        // Now that we're mostly running, clean up stale users and apps
        sUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
        reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
        // 發送 PermissionManager 的 systemReady 通知
        mPermissionManager.systemReady();
        ......
    }
複製代碼

到這裏,PMS的初始化主要過程就梳理完了,整個流程仍是比較長的,不少細節作了忽略,接下來咱們從三個方面來看下PMS初始化細節:

  • permission文件處理
  • 應用目錄的掃描過程
  • APK文件的解析過程

下一篇深刻Android系統(十)PMS-2-初始化的一些細節

相關文章
相關標籤/搜索