PackageManagerService 系列文章以下(基於 Android 9.0 源碼)
🍁 Framework 核心服務之 PackageManagerService 鑽研(1)- 啓動流程
🍁 Framework 核心服務之 PackageManagerService 鑽研(2)- 構造函數
🍁 Framework 核心服務之 PackageManagerService 鑽研(3)- PackageManager
🍁 Framework 核心服務之 PackageManagerService 鑽研(4)- PackageInstaller
🍁 Framework 核心服務之 PackageManagerService 鑽研(5)- APK 安裝流程(PackageInstaller)
🍁 Framework 核心服務之 PackageManagerService 鑽研(6)- APK 安裝流程(PMS)
🍁 Framework 核心服務之 PackageManagerService 鑽研(7)- PackageParserjava
關鍵類 | 路徑 |
---|---|
SystemServer.java | frameworks/base/services/java/com/android/server/SystemServer.java |
PackageManagerService.java | frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java |
Process.java | frameworks/base/core/java/android/os/Process.java |
SystemConfig.java | frameworks/base/core/java/com/android/server/SystemConfig.java |
Settings.java | frameworks/base/services/core/java/com/android/server/pm/Settings.java |
PackageManagerService(PMS)是 SystemServer 啓動後的第一個核心服務,也是 Android 系統中最經常使用的服務之一。它負責系統中 Package 的管理,應用程序的安裝、卸載、信息查詢等。若是你是面向 Android 系統開發的工程師,基礎概念我也不須要再多贅述,咱們直接跟源碼。android
PMS 構造函數第二階段的工做就是掃描系統中的 APK 了。因爲須要逐個掃描文件,所以手機上裝的程序越多,PMS 的工做量就越大,系統啓動速度也就越慢,這就是爲何你的手機啓動速度有快慢的緣由。segmentfault
接着上面的 PMS 構造函數繼續分析源碼:數據結構
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { ... ... // DEX 優化 mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context, "*dexopt*"); mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock); synchronized (mPackages) { mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); mHandlerThread.start(); mHandler = new PackageHandler(mHandlerThread.getLooper()); mProcessLoggingHandler = new ProcessLoggingHandler(); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this); mInstantAppRegistry = new InstantAppRegistry(this); // 爲 data/ 目錄下的某些子目錄生成File實例 File dataDir = Environment.getDataDirectory(); // data/app 存放第三方應用 mAppInstallDir = new File(dataDir, "app"); mAppLib32InstallDir = new File(dataDir, "app-lib"); mAsecInternalPath = new File(dataDir, "app-asec").getPath(); // data/app-private 存放 drm 保護的應用 mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); sUserManager = new UserManagerService(context, this, new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages); // 獲取 SystemConfig 中解析到的 <permission> 標籤標識的 permission 信息,保存到 Settings::mPermissions ArrayMap<String, SystemConfig.PermissionEntry> permConfig = systemConfig.getPermissions(); for (int i=0; i<permConfig.size(); i++) { SystemConfig.PermissionEntry perm = permConfig.valueAt(i); BasePermission bp = mSettings.mPermissions.get(perm.name); if (bp == null) { bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN); mSettings.mPermissions.put(perm.name, bp); } if (perm.gids != null) { bp.setGids(perm.gids, perm.perUser); } } // 獲得除 framework 以外的系統中的共享庫列表,從 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); } mFoundPolicyFile = SELinuxMMAC.readInstallPolicy(); // 讀取packages.xml的內容,並對mSettings::mPackages等成員進行賦值;packages.xml文件中的內容是上一次掃描apk目錄的結果; // 當前這一次掃描的結果是保存在PackageManagerService::mPackages列表中; // 對比上次掃描的結果來檢查本次掃描到的應用中是否有被升級包覆蓋的系統應用,若是有則從PackageManagerService::mPackages中移除; // 這樣,PackageManagerService::mPackages的記錄就和mSettings::mPackages的一致了; // 系統最終會將本次apk掃描的結果從新寫入packages.xml中 mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false)); // Clean up orphaned packages for which the code path doesn't exist // and they are an update to a system app - caused by bug/32321269 final int packageSettingCount = mSettings.mPackages.size(); // 清理那些代碼路徑不存在的異常 package 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); } } if (mFirstBoot) { requestCopyPreoptedFiles(); } // 設置模塊來代替 framework-res.apk 中缺省的 ResolverActivity String customResolverActivity = Resources.getSystem().getString( R.string.config_customResolverActivity); if (TextUtils.isEmpty(customResolverActivity)) { customResolverActivity = null; } else { mCustomResolverComponentName = ComponentName.unflattenFromString( customResolverActivity); } long startTime = SystemClock.uptimeMillis(); // 記錄掃描開始的時間 // 須要系統提早加載的一些 jar final String bootClassPath = System.getenv("BOOTCLASSPATH"); final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH"); if (bootClassPath == null) { Slog.w(TAG, "No BOOTCLASSPATH found!"); } if (systemServerClassPath == null) { Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!"); }
清空 cache 文件後,PMS 終於進入重點段了。接下來看 PMS 第二階段工做的核心內容,即掃描 Package,相關代碼以下:app
// PackageManagerService.java // 定義 frameworkDir 指向 /system/frameworks 目錄 File frameworkDir = new File(Environment.getRootDirectory(), "framework"); final VersionInfo ver = mSettings.getInternalVersion(); mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint); // when upgrading from pre-M, promote system app permissions from install to runtime mPromoteSystemApps = mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1; // When upgrading from pre-N, we need to handle package extraction like first boot, // as there is no profiling data available. mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N; mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1; // 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)) { // 遍歷Settings::mPackages集合,將系統APP加入到PackageManagerService::mExistingSystemPackages mExistingSystemPackages.add(ps.name); } } } mCacheDir = preparePackageParserCache(mIsUpgrade); // 定義掃描參數 int scanFlags = SCAN_BOOTING | SCAN_INITIAL; if (mIsUpgrade || mFirstBoot) { scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE; } // 先掃描 /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); // 調用 scanDirTracedLI 函數掃描 /system/frameworks 目錄 scanDirTracedLI(frameworkDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED, scanFlags | SCAN_NO_DEX, 0); // Collected privileged system packages. final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); // 掃描 /system/priv-app 下的 package scanDirTracedLI(privilegedAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0); // Collect ordinary system packages. final File systemAppDir = new File(Environment.getRootDirectory(), "app"); // 掃描 /system/app 下的 package scanDirTracedLI(systemAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0); // Collect all vendor packages. 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 下的 package scanDirTracedLI(vendorAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0); // Collect all OEM packages. final File oemAppDir = new File(Environment.getOemDirectory(), "app"); // 掃描 OEM 的 Package scanDirTracedLI(oemAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
由以上代碼可知,PMS 將掃描如下幾個目錄(僅列出重點):dom
✨ /system/frameworks :該目錄中的文件都是系統庫,例如:framework.jar、services.jar、framework-res.apk。不過 scanDirTracedLI 只掃描 APK 文件,因此 framework-res.apk 是該目錄中惟一「受寵」的文件。ide
✨ /system/app :該目錄下全是默認的系統應用。例如:Browser.apk、SettingsProvider.apk 等。函數
✨ /vendor/app :該目錄中的文件由廠商提供,即全是廠商特定的 APK 文件,目前市面上的廠商都把本身的應用放在 /system/app 目錄下。oop
PMS 調用 scanDirTracedLI 函數進行掃描,下面分析此函數:優化
public void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) { try { scanDirLI(dir, parseFlags, scanFlags, currentTime); // 調用 scanDirLI 函數 } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } }
咱們能夠看出,實際是調用了 scanDirLI 函數進行掃描工做!
下面的重點就是來關注 scanDirLI 函數了:
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) { // 列舉該目錄下的文件 final File[] files = dir.listFiles(); if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + dir); return; } ParallelPackageParser parallelPackageParser = new ParallelPackageParser( mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback); int fileCount = 0; for (File file : files) { final boolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); if (!isPackage) { // Ignore entries which are not packages continue; } parallelPackageParser.submit(file, parseFlags); fileCount++; } // Process results one by one for (; fileCount > 0; fileCount--) { ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); Throwable throwable = parseResult.throwable; int errorCode = PackageManager.INSTALL_SUCCEEDED; if (throwable == null) { // Static shared libraries have synthetic package names if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(parseResult.pkg); } try { if (errorCode == PackageManager.INSTALL_SUCCEEDED) { // 調用 scanPackageLI 函數掃描一個特定的文件,返回值是 PackageParser 的內部類 Package,該類的實例表明一個 APK 文件,因此它就是和 APK 文件對應的數據結構 scanPackageLI(parseResult.pkg, parseResult.scanFile, parseFlags, scanFlags, currentTime, null); } ... ... } // Delete invalid userdata apps if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 && errorCode == PackageManager.INSTALL_FAILED_INVALID_APK) { // 非系統 Package 掃描失敗,刪除文件 removeCodePathLI(parseResult.scanFile); } } parallelPackageParser.close(); }
PMS 中有三處 scanPackageLI,咱們後面會一一分析到,先來看第一個也是最早碰到的 sanPackageLI 函數。
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile, final int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { if ((scanFlags & SCAN_CHECK_ONLY) == 0) { if (pkg.childPackages != null && pkg.childPackages.size() > 0) { scanFlags |= SCAN_CHECK_ONLY; } } else { scanFlags &= ~SCAN_CHECK_ONLY; } // Scan the parent PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, policyFlags, scanFlags, currentTime, user); // Scan the children final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPackage = pkg.childPackages.get(i); scanPackageInternalLI(childPackage, scanFile, policyFlags, scanFlags, currentTime, user); } if ((scanFlags & SCAN_CHECK_ONLY) != 0) { return scanPackageLI(pkg, scanFile, policyFlags, scanFlags, currentTime, user); } return scannedPkg; }
調用 scanPackageInternalLI():
private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile, int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { PackageSetting ps = null; PackageSetting updatedPkg; // 判斷系統 APP 是否須要更新 synchronized (mPackages) { // 查看是否已經有該安裝包,經過 mSetting 查找 String oldName = mSettings.getRenamedPackageLPr(pkg.packageName); if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) { // 若是存在同一個包名的老的安裝包,且已經改回原始名稱了 ps = mSettings.getPackageLPr(oldName); } // 若是沒有原始包,則使用真實包名 if (ps == null) { ps = mSettings.getPackageLPr(pkg.packageName); } // 查這個包是不是一個隱藏或者能夠更新的系統包 updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName); // If this is a package we don't know about on the system partition, we // may need to remove disabled child packages on the system partition // or may need to not add child packages if the parent apk is updated // on the data partition and no longer defines this child package. if ((policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0) { // If this is a parent package for an updated system app and this system // app got an OTA update which no longer defines some of the child packages // we have to prune them from the disabled system packages. PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); if (disabledPs != null) { final int scannedChildCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; final int disabledChildCount = disabledPs.childPackageNames != null ? disabledPs.childPackageNames.size() : 0; for (int i = 0; i < disabledChildCount; i++) { String disabledChildPackageName = disabledPs.childPackageNames.get(i); boolean disabledPackageAvailable = false; for (int j = 0; j < scannedChildCount; j++) { PackageParser.Package childPkg = pkg.childPackages.get(j); if (childPkg.packageName.equals(disabledChildPackageName)) { disabledPackageAvailable = true; break; } } if (!disabledPackageAvailable) { mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName); } } } } } ... ... // Note that we invoke the following method only if we are about to unpack an application // 調用第二個 scanPackageLI 函數 PackageParser.Package scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); ... ... }
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { // 建立一個 PackageParser 對象,用於解析包 PackageParser pp = new PackageParser(); // 設置 PackageParse 的三個屬性 pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); pp.setCallback(mPackageParserCallback); // 判斷掃描模式 if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) { parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY; } // 解析APK獲取對應PackageParser.Package對象 pkg final PackageParser.Package pkg; // 調用 PackageParser 的 parsePackage 函數解析 APK 文件 try { // 💥 💥 💥 💥 💥 💥 真正的解析 pkg = pp.parsePackage(scanFile, parseFlags); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } // Static shared libraries have synthetic package names if (pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(pkg); } return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user); }
if (!mOnlyCore) { // mOnlyCore 用於控制是否掃描非系統 Package // 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); } } final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator(); while (psit.hasNext()) { PackageSetting ps = psit.next(); /* * If this is not a system app, it can't be a * disable system app. */ if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { /// M: Operator apps are belong to system domain, therefore, need prune. /// We should also consider OTA from old version without mtkFlag if (sPmsExt.isNotOperatorApp(ps)) continue; } /* * If the package is scanned, it's not erased. */ final PackageParser.Package scannedPkg = mPackages.get(ps.name); if (scannedPkg != null) { /* * If the system app is both scanned and in the * disabled packages list, then it must have been * added via OTA. Remove it from the currently * scanned package so the previously user-installed * application can be scanned. */ if (mSettings.isDisabledSystemPackageLPr(ps.name)) { logCriticalInfo(Log.WARN, "Expecting better updated system app for " + ps.name + "; removing system app. Last known codePath=" + ps.codePathString + ", installStatus=" + ps.installStatus + ", versionCode=" + ps.versionCode + "; scanned versionCode=" + scannedPkg.mVersionCode); removePackageLI(scannedPkg, true); mExpectingBetter.put(ps.name, ps.codePath); } continue; } if (!mSettings.isDisabledSystemPackageLPr(ps.name)) { psit.remove(); logCriticalInfo(Log.WARN, "System package " + ps.name + " no longer exists; it's data will be wiped"); // Actual deletion of code and data will be handled by later // reconciliation step } else { // we still have a disabled system package, but, it still might have // been removed. check the code path still exists and check there's // still a package. the latter can happen if an OTA keeps the same // code path, but, changes the package name. final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name); if (disabledPs.codePath == null || !disabledPs.codePath.exists() || disabledPs.pkg == null) { possiblyDeletedUpdatedSystemApps.add(ps.name); } } } } if (!mOnlyCore) { scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0); scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags | PackageParser.PARSE_FORWARD_LOCK, 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. for (String deletedAppName : possiblyDeletedUpdatedSystemApps) { PackageParser.Package deletedPkg = mPackages.get(deletedAppName); mSettings.removeDisabledSystemPackageLPw(deletedAppName); final String msg; if (deletedPkg == null) { // should have found an update, but, we didn't; remove everything msg = "Updated system package " + deletedAppName + " no longer exists; removing its data"; // Actual deletion of code and data will be handled by later // reconciliation step } else { // found an update; revoke system privileges msg = "Updated system package + " + deletedAppName + " no longer exists; revoking system privileges"; // Don't do anything if a stub is removed from the system image. If // we were to remove the uncompressed version from the /data partition, // this is where it'd be done. final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName); deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM; deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM; /// M: [Operator] Revoke operator permissions for the original operator sPmsExt.clearExtFlags(deletedPkg, deletedPs); } logCriticalInfo(Log.WARN, msg); } /* * 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. */ for (int i = 0; i < mExpectingBetter.size(); i++) { final String packageName = mExpectingBetter.keyAt(i); if (!mPackages.containsKey(packageName)) { final File scanFile = mExpectingBetter.valueAt(i); logCriticalInfo(Log.WARN, "Expected better " + packageName + " but never showed up; reverting to system"); int reparseFlags = mDefParseFlags; if (FileUtils.contains(privilegedAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED; } else if (FileUtils.contains(systemAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR; } else if (FileUtils.contains(vendorAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR; } else if (FileUtils.contains(oemAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR; } else { Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile); continue; } mSettings.enableSystemPackageLPw(packageName); try { scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 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. decompressSystemApplications(stubSystemApps, scanFlags); final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get() - cachedSystemApps; final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime; final int dataPackagesCount = mPackages.size() - systemPackagesCount; Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime + " ms, packageCount: " + dataPackagesCount + " , timePerPackage: " + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount) + " , cached: " + cachedNonSystemApps); if (mIsUpgrade && dataPackagesCount > 0) { MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time", ((int) dataScanTime) / dataPackagesCount); } }
這部分任務比較簡單,就是將第二階段手機的信息再集中整理一次,可自行研究。
第二階段分析就此結束!