《Android 源码 installPackage 流程分析》一节着重分析了 apk 安装流程,接下来我们分析 PackageManagerService 启动时都做了些什么?

  1. 执行 PackageManagerService main 静态方法;
  2. 调用 PackageManagerService 类 isFirstBoot() 方法;
  3. 调用 PackageManagerService 类 getUsageStatsIfNoPackageUsageInfo() 方法;
  4. 调用 PackageManagerService 类 performBootDexOpt() 方法;
  5. 调用 PackageManagerService 类 systemReady() 方法。

frameworks/base/services/java/com/android/server/SystemServer.java

public final class SystemServer {......private PackageManagerService mPackageManagerService;    ....../*** The main entry point from zygote.*/public static void main(String[] args) {new SystemServer().run();}public SystemServer() {// Check for factory test mode.mFactoryTestMode = FactoryTest.getMode();}private void run() {......// Start services.try {startBootstrapServices();startCoreServices();startOtherServices();} catch (Throwable ex) {Slog.e("System", "******************************************");Slog.e("System", "************ Failure starting system services", ex);throw ex;}......}......private void startBootstrapServices() {......// 等待 installd 完成启动,以便它有机会创建具有适当权限的关键目录,如/data/user。// 我们需要在初始化其他服务之前完成此操作。Installer installer = mSystemServiceManager.startService(Installer.class);......// Start the package manager.Slog.i(TAG, "Package Manager");mPackageManagerService = PackageManagerService.main(mSystemContext, installer,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);mFirstBoot = mPackageManagerService.isFirstBoot();......}......private void startCoreServices() {......// UsageStatsService 可用后更新,需要在 performBootDexOpt 之前。mPackageManagerService.getUsageStatsIfNoPackageUsageInfo();......}......private void startOtherServices() {......try {mPackageManagerService.performBootDexOpt();} catch (Throwable e) {reportWtf("performing boot dexopt", e);}        ......try {mPackageManagerService.systemReady();} catch (Throwable e) {reportWtf("making Package Manager Service ready", e);}......}......
}

在 main() 静态方法中:

  1. 创建 PackageManagerService 对象;
  2. 将其注册到“服务大管家” ServiceManager,注册字段是"package",以后就可以通过"package"字符串查询“服务大管家”,获取 PackageManagerService 了。

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public class PackageManagerService extends IPackageManager.Stub {......public static PackageManagerService main(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {PackageManagerService m = new PackageManagerService(context, installer,factoryTest, onlyCore);ServiceManager.addService("package", m);return m;}......
}

PackageManagerService 构造函数中完成了很多任务包括添加特殊用户名称和UID并关联、获取各种路径(/data/data、/data/app、/data/app-lib、/data/app-asec…)、所有外部库运行 dexopt、framework 路径下的文件运行 dexopt(除了 framework-res.apk 和 core-libart.jar)、扫描各种软件包(供应商overlay程序包、基础 framework(无代码的资源包)、特权系统软件包、普通系统软件包、供应商软件包、OEM软件包)、处理所有不再存在的系统软件包、清除任何不完整的包安装、删除所有没有关联软件包的共享用户ID…

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public class PackageManagerService extends IPackageManager.Stub {......public PackageManagerService(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {......mContext = context;mFactoryTest = factoryTest;mOnlyCore = onlyCore;mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));mMetrics = new DisplayMetrics();mSettings = new Settings(mPackages);// 添加特殊用户名称和UID并关联,如 "android.uid.system" 和 Process.SYSTEM_UID 关联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);......mInstaller = installer;mPackageDexOptimizer = new PackageDexOptimizer(this);mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());mOnPermissionChangeListeners = new OnPermissionChangeListeners(FgThread.get().getLooper());getDefaultDisplayMetrics(context, mMetrics);SystemConfig systemConfig = SystemConfig.getInstance();mGlobalGids = systemConfig.getGlobalGids();mSystemPermissions = systemConfig.getSystemPermissions();mAvailableFeatures = systemConfig.getAvailableFeatures();synchronized (mInstallLock) {// writersynchronized (mPackages) {mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);mHandlerThread.start();mHandler = new PackageHandler(mHandlerThread.getLooper());Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);// 返回路径:/dataFile dataDir = Environment.getDataDirectory();mAppDataDir = new File(dataDir, "data");mAppInstallDir = new File(dataDir, "app");mAppLib32InstallDir = new File(dataDir, "app-lib");mAsecInternalPath = new File(dataDir, "app-asec").getPath();mUserAppDataDir = new File(dataDir, "user");mDrmAppPrivateInstallDir = new File(dataDir, "app-private");sUserManager = new UserManagerService(context, this,mInstallLock, mPackages);......mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),mSdkVersion, mOnlyCore);......// 给 monitor 标志设置,而不在扫描安装目录时更改apk文件路径。final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;final ArraySet<String> alreadyDexOpted = new ArraySet<String>();/*** 将引导类路径中的所有内容添加到进程文件列表中,* 因为如果需要,在zygote启动期间将运行dexopt。*/final String bootClassPath = System.getenv("BOOTCLASSPATH");final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");if (bootClassPath != null) {String[] bootClassPathElements = splitString(bootClassPath, ':');for (String element : bootClassPathElements) {alreadyDexOpted.add(element);}} else {Slog.w(TAG, "No BOOTCLASSPATH found!");}if (systemServerClassPath != null) {String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');for (String element : systemServerClassPathElements) {alreadyDexOpted.add(element);}} else {Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");}final List<String> allInstructionSets = InstructionSets.getAllInstructionSets();final String[] dexCodeInstructionSets =getDexCodeInstructionSets(allInstructionSets.toArray(new String[allInstructionSets.size()]));/*** 确保所有外部库都运行了dexopt。*/if (mSharedLibraries.size() > 0) {// NOTE: For now, we're compiling these system "shared libraries"// (and framework jars) into all available architectures. It's possible// to compile them only when we come across an app that uses them (there's// already logic for that in scanPackageLI) but that adds some complexity.for (String dexCodeInstructionSet : dexCodeInstructionSets) {for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {final String lib = libEntry.path;if (lib == null) {continue;}try {int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {alreadyDexOpted.add(lib);mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded, false);}} catch (FileNotFoundException e) {Slog.w(TAG, "Library not found: " + lib);} catch (IOException e) {Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "+ e.getMessage());}}}}File frameworkDir = new File(Environment.getRootDirectory(), "framework");// 我们知道该文件不包含任何代码,因此请不要进行dexopt处理以免产生日志溢出。alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");// 我们知道此文件只是art的启动类路径的一部分,因此请不要进行dexopt处理以免产生日志溢出。alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");/*** 有许多用Java实现的命令,* 我们目前需要对它们执行dexopt,* 以便可以从非root用户shell程序运行它们。*/String[] frameworkFiles = frameworkDir.list();if (frameworkFiles != null) {// TODO: We could compile these only for the most preferred ABI. We should// first double check that the dex files for these commands are not referenced// by other system apps.for (String dexCodeInstructionSet : dexCodeInstructionSets) {for (int i=0; i<frameworkFiles.length; i++) {File libPath = new File(frameworkDir, frameworkFiles[i]);String path = libPath.getPath();// 如果我们已经处理过,跳过该文件。if (alreadyDexOpted.contains(path)) {continue;}// 如果不是我们需要处理(dexopt)的类型,跳过该文件。if (!path.endsWith(".apk") && !path.endsWith(".jar")) {continue;}try {int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded, false);}} catch (FileNotFoundException e) {Slog.w(TAG, "Jar not found: " + path);} catch (IOException e) {Slog.w(TAG, "Exception reading jar: " + path, e);}}}}......// 收集供应商overlay程序包。 (在扫描任何应用程序之前,请执行此操作。)// 出于安全和版本匹配的原因,仅当overlay包位于VENDOR_OVERLAY_DIR中时,才考虑使用它们File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);// 查找基础 framework(无代码的资源包)。scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR| PackageParser.PARSE_IS_PRIVILEGED,scanFlags | SCAN_NO_DEX, 0);// 收集特权系统软件包。final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);// 收集普通系统软件包。final File systemAppDir = new File(Environment.getRootDirectory(), "app");scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);// 收集所有供应商软件包。File vendorAppDir = new File("/vendor/app");try {vendorAppDir = vendorAppDir.getCanonicalFile();} catch (IOException e) {// failed to look up canonical path, continue with original one}scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);// 收集所有OEM程序包。final File oemAppDir = new File(Environment.getOemDirectory(), "app");scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");mInstaller.moveFiles();// 修剪所有不再存在的系统软件包。final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();if (!mOnlyCore) {Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();while (psit.hasNext()) {PackageSetting ps = psit.next();/** 如果这不是一个系统应用程序,它不能是一个禁用系统应用程序。*/if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {continue;}/** 如果包被扫描,它不会被删除。*/final PackageParser.Package scannedPkg = mPackages.get(ps.name);if (scannedPkg != null) {/** 如果系统应用程序被扫描并且在禁用包列表中,那么它一定是通过OTA添加的。* 将其从当前扫描的包中删除,以便可以扫描之前用户安装的应用程序。*/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(ps, 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; wiping its data");removeDataDirsLI(null, ps.name);} else {final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {possiblyDeletedUpdatedSystemApps.add(ps.name);}}}}//查找任何不完整的包安装ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();//清理 listfor(int i = 0; i < deletePkgsList.size(); i++) {cleanupInstallFailedPackage(deletePkgsList.get(i));}//删除 tmp 文件deleteTempPackageFiles();// 删除所有没有关联软件包的共享用户IDmSettings.pruneSharedUsersLPw();if (!mOnlyCore) {EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,SystemClock.uptimeMillis());scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,scanFlags | SCAN_REQUIRE_KNOWN, 0);/*** 删除通过OTA删除的所有更新的系统应用程序的禁用程序包设置。 * 如果它们不是以前更新的应用程序,则将其完全删除。 否则,只需撤销其系统级权限。*/for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {PackageParser.Package deletedPkg = mPackages.get(deletedAppName);mSettings.removeDisabledSystemPackageLPw(deletedAppName);String msg;if (deletedPkg == null) {msg = "Updated system package " + deletedAppName+ " no longer exists; wiping its data";removeDataDirsLI(null, deletedAppName);} else {msg = "Updated system app + " + deletedAppName+ " no longer present; removing system privileges for "+ deletedAppName;deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;}logCriticalInfo(Log.WARN, msg);}/*** 确保确实出现了我们期望出现在userdata分区上的所有系统应用程序。*/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");final int reparseFlags;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 {scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null);} catch (PackageManagerException e) {Slog.e(TAG, "Failed to parse original system package: "+ e.getMessage());}}}}mExpectingBetter.clear();// 既然我们知道了所有的共享库,那么就更新所有客户端以获得正确的库路径。updateAllSharedLibrariesLPw();for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {// NOTE: We ignore potential failures here during a system scan (like// the rest of the commands above) because there's precious little we// can do about it. A settings error is reported, though.adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,false /* force dexopt */, false /* defer dexopt */,false /* boot complete */);}// 现在我们知道了所有要保存的包,读取和更新它们的最后使用时间。mPackageUsage.readLP();EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,SystemClock.uptimeMillis());Slog.i(TAG, "Time to scan packages: "+ ((SystemClock.uptimeMillis()-startTime)/1000f)+ " seconds");// 如果平台SDK自上次启动以来发生了变化,我们需要重新授予应用程序权限,以捕捉出现的任何新版本。// 这是一种黑客行为,这意味着应用程序在某些情况下可以获得用户最初并不明确允许的权限……// 如果有更好的方法来处理这种情况就好了。int updateFlags = UPDATE_PERMISSIONS_ALL;if (ver.sdkVersion != mSdkVersion) {Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to "+ mSdkVersion + "; regranting permissions for internal storage");updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;}updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);ver.sdkVersion = mSdkVersion;// 如果这是第一次启动或从pre-M进行的更新,并且是正常启动,// 则我们需要在所有定义的用户中初始化默认的首选应用程序。if (!onlyCore && (mPromoteSystemApps || !mRestoredSettings)) {for (UserInfo user : sUserManager.getUsers(true)) {mSettings.applyDefaultPreferredAppsLPw(this, user.id);applyFactoryDefaultBrowserLPw(user.id);primeDomainVerificationsLPw(user.id);}}// 如果这是OTA之后的第一次引导,并且是正常引导,那么我们需要清除代码缓存目录。if (mIsUpgrade && !onlyCore) {Slog.i(TAG, "Build fingerprint changed; clearing code caches");for (int i = 0; i < mSettings.mPackages.size(); i++) {final PackageSetting ps = mSettings.mPackages.valueAt(i);if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);}}ver.fingerprint = Build.FINGERPRINT;}checkDefaultBrowser();// 仅在权限和其他默认值已更新后清除mExistingSystemPackages.clear();mPromoteSystemApps = false;// 所有更改都在软件包扫描期间完成。ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;// 可以降级为读mSettings.writeLPr();EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,SystemClock.uptimeMillis());mRequiredVerifierPackage = getRequiredVerifierLPr();mRequiredInstallerPackage = getRequiredInstallerLPr();mInstallerService = new PackageInstallerService(context, this);mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();mIntentFilterVerifier = new IntentVerifierProxy(mContext,mIntentFilterVerifierComponent);} // synchronized (mPackages)} // synchronized (mInstallLock)// 现在,在打开每个应用程序zip压缩包之后,确保它们都被刷新了。// 不是真的需要,但可以保持整洁。Runtime.getRuntime().gc();// 公开私有服务以供系统组件使用。LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());}......
}

接下来进入 isFirstBoot() 方法,此方法非常简单仅返回 mRestoredSettings 标志(恢复设置),它是在 PackageManagerService 构造函数中初始化的。

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public class PackageManagerService extends IPackageManager.Stub {......boolean mRestoredSettings;......@Overridepublic boolean isFirstBoot() {return !mRestoredSettings;}    ......
}

getUsageStatsIfNoPackageUsageInfo() 函数着重更新了一下应用最后一次使用结束时的时间,通过包名从 mPackages 中获取 PackageParser.Package 对象,然后更新。

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public class PackageManagerService extends IPackageManager.Stub {......public void getUsageStatsIfNoPackageUsageInfo() {if (!mPackageUsage.isHistoricalPackageUsageAvailable()) {UsageStatsManager usm = (UsageStatsManager) mContext.getSystemService(Context.USAGE_STATS_SERVICE);if (usm == null) {throw new IllegalStateException("UsageStatsManager must be initialized");}long now = System.currentTimeMillis();Map<String, UsageStats> stats = usm.queryAndAggregateUsageStats(now - mDexOptLRUThresholdInMills, now);for (Map.Entry<String, UsageStats> entry : stats.entrySet()) {String packageName = entry.getKey();PackageParser.Package pkg = mPackages.get(packageName);if (pkg == null) {continue;}UsageStats usage = entry.getValue();// 更新应用最后一次使用结束时的时间pkg.mLastPackageUsageTimeInMills = usage.getLastTimeUsed();mPackageUsage.mIsHistoricalPackageUsageAvailable = true;}}}......
}

fstrim 用于回收(又称为"trim")一个已挂载的文件系统上所有未使用的块。这对于固态硬盘(SSD)和精简配置(thinly-provisioned)的存储设备比较有意义。默认情况下,fstrim 将会回收文件系统上所有未使用的块。但是可以通过选项限定回收的范围和大小。

performBootDexOpt() 函数的作用:

  1. 判断是否需要磁盘维护,这是通过 fstrim 命令完成的
  2. 调用 dexopt 优化核心应用、监听预启动完成的系统应用和近期使用的应用,包优化是通过调用 performBootDexOpt 函数的重载版本完成的

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public class PackageManagerService extends IPackageManager.Stub {......@Overridepublic void performBootDexOpt() {enforceSystemOrRoot("Only the system can request dexopt be performed");// 首先,看看我们是否需要 fstrim。try {IMountService ms = PackageHelper.getMountService();if (ms != null) {final boolean isUpgrade = isUpgrade();boolean doTrim = isUpgrade;// 由于系统更新,立即进行磁盘维护if (doTrim) {Slog.w(TAG, "Running disk maintenance immediately due to system update");} else {// 执行 fstrim 时间间隔,默认值为三天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() - ms.lastMaintenance();// 大于三天触发磁盘维护(doTrim 赋值为 true)if (timeSinceLast > interval) {doTrim = true;Slog.w(TAG, "No disk maintenance in " + timeSinceLast+ "; running immediately");}}}if (doTrim) {// 如果不是第一次启动,界面显示正在优化存储空间。if (!isFirstBoot()) {try {ActivityManagerNative.getDefault().showBootMessage(mContext.getResources().getString(R.string.android_upgrading_fstrim), true);} catch (RemoteException e) {}}// 运行磁盘维护,这是通过 fstrim 命令完成的ms.runMaintenance();}} else {Slog.e(TAG, "Mount service unavailable!");}} catch (RemoteException e) {// Can't happen; MountService is local}final ArraySet<PackageParser.Package> pkgs;synchronized (mPackages) {// 清除 Set 中延迟的 dexopt 包。返回清除 dexopt Set 之前的内容(如果不为空)pkgs = mPackageDexOptimizer.clearDeferredDexOptPackages();}if (pkgs != null) {// 按重要性对应用程序排序以进行dexopt排序。 // 如果设备空间不足,重要的应用程序将获得更高的优先级。ArrayList<PackageParser.Package> sortedPkgs = new ArrayList<PackageParser.Package>();// 优先考虑核心应用、。for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {PackageParser.Package pkg = it.next();if (pkg.coreApp) {if (DEBUG_DEXOPT) {Log.i(TAG, "Adding core app " + sortedPkgs.size() + ": " + pkg.packageName);}sortedPkgs.add(pkg);it.remove();}}// 优先考虑监听预启动完成的系统应用程序。Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);ArraySet<String> pkgNames = getPackageNamesForIntent(intent);for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {PackageParser.Package pkg = it.next();if (pkgNames.contains(pkg.packageName)) {if (DEBUG_DEXOPT) {Log.i(TAG, "Adding pre boot system app " + sortedPkgs.size() + ": " + pkg.packageName);}sortedPkgs.add(pkg);it.remove();}}// 过滤掉最近没有使用的包。filterRecentlyUsedApps(pkgs);// 添加所有剩余的应用程序。for (PackageParser.Package pkg : pkgs) {if (DEBUG_DEXOPT) {Log.i(TAG, "Adding app " + sortedPkgs.size() + ": " + pkg.packageName);}sortedPkgs.add(pkg);}// 懒惰模式下过滤那些最近没有用过的包。if (mLazyDexOpt) {filterRecentlyUsedApps(sortedPkgs);}int i = 0;int total = sortedPkgs.size();File dataDir = Environment.getDataDirectory();// 获取低存储空间阈值long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);if (lowThreshold == 0) {throw new IllegalStateException("Invalid low memory threshold");}for (PackageParser.Package pkg : sortedPkgs) {long usableSpace = dataDir.getUsableSpace();// 由于存储空间不足,未在其余应用上运行 dexoptif (usableSpace < lowThreshold) {Log.w(TAG, "Not running dexopt on remaining apps due to low memory: " + usableSpace);break;}// 完成包优化工作performBootDexOpt(pkg, ++i, total);}}}    ......
}
  1. 如果不是第一次启动,界面显示 应用:xx / xx。
  2. 实际优化工作是调用 PackageDexOptimizer 类 performDexOpt 处理的

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public class PackageManagerService extends IPackageManager.Stub {......private void performBootDexOpt(PackageParser.Package pkg, int curr, int total) {if (DEBUG_DEXOPT) {Log.i(TAG, "Optimizing app " + curr + " of " + total + ": " + pkg.packageName);}if (!isFirstBoot()) {try {// 如果不是第一次启动,界面显示 应用:xx / xx。ActivityManagerNative.getDefault().showBootMessage(mContext.getResources().getString(R.string.android_upgrading_apk,curr, total), true);} catch (RemoteException e) {}}PackageParser.Package p = pkg;synchronized (mInstallLock) {// 实际优化工作是调用 PackageDexOptimizer 类 performDexOpt 处理的mPackageDexOptimizer.performDexOpt(p, null /* 指令集 */,false /* 强制 dex */, false /* 推迟 */, true /* 包括依赖关系 */,false /* 启动完成 */);}}      ......
}

对指定指令集的指定程序包的所有代码路径和库执行 dexopt。performDexOpt() 方法内部调用了 performDexOptLI() 方法,performDexOptLI() 方法内部最终调用 Installer 类 dexopt 完成实际优化动作。

frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java

final class PackageDexOptimizer {......int performDexOpt(PackageParser.Package pkg, String[] instructionSets,boolean forceDex, boolean defer, boolean inclDependencies, boolean bootComplete) {ArraySet<String> done;if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {done = new ArraySet<String>();done.add(pkg.packageName);} else {done = null;}synchronized (mPackageManagerService.mInstallLock) {final boolean useLock = mSystemReady;if (useLock) {mDexoptWakeLock.setWorkSource(new WorkSource(pkg.applicationInfo.uid));mDexoptWakeLock.acquire();}try {return performDexOptLI(pkg, instructionSets, forceDex, defer, bootComplete, done);} finally {if (useLock) {mDexoptWakeLock.release();}}}}private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,boolean forceDex, boolean defer, boolean bootComplete, ArraySet<String> done) {......final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);for (String dexCodeInstructionSet : dexCodeInstructionSets) {......for (String path : paths) {......if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {......final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,!pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet,dexoptNeeded, vmSafeMode, debuggable, oatDir, bootComplete);......}}......}......}    ......
}

最后再来看 PackageManagerService 类 systemReady() 方法。

  1. 验证所有 PreferredActivity 组件是否确实存在
  2. 如果我们升级,在启动之前授予所有默认权限
  3. 监视外部容量变化

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public class PackageManagerService extends IPackageManager.Stub {......@Overridepublic void systemReady() {mSystemReady = true;// 系统就绪后,读取兼容性模式开关。boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(mContext.getContentResolver(),android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1;PackageParser.setCompatibilityModeEnabled(compatibilityModeEnabled);if (DEBUG_SETTINGS) {Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled);}int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;synchronized (mPackages) {// 验证所有 PreferredActivity 组件是否确实存在。 ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);removed.clear();for (PreferredActivity pa : pir.filterSet()) {if (mActivities.mActivities.get(pa.mPref.mComponent) == null) {removed.add(pa);}}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();// 如果我们升级,在启动之前授予所有默认权限。for (int userId : grantPermissionsUserIds) {mDefaultPermissionPolicy.grantDefaultPermissions(userId);}// 启动任何消息,等待系统就绪if (mPostSystemReadyMessages != null) {for (Message msg : mPostSystemReadyMessages) {msg.sendToTarget();}mPostSystemReadyMessages = null;}// 监视外部容量变化final StorageManager storage = mContext.getSystemService(StorageManager.class);storage.registerListener(mStorageListener);mInstallerService.systemReady();mPackageDexOptimizer.systemReady();MountServiceInternal mountServiceInternal = LocalServices.getService(MountServiceInternal.class);mountServiceInternal.addExternalStoragePolicy(new MountServiceInternal.ExternalStorageMountPolicy() {@Overridepublic int getMountMode(int uid, String packageName) {if (Process.isIsolated(uid)) {return Zygote.MOUNT_EXTERNAL_NONE;}if (checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED) {return Zygote.MOUNT_EXTERNAL_DEFAULT;}if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {return Zygote.MOUNT_EXTERNAL_DEFAULT;}if (checkUidPermission(WRITE_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {return Zygote.MOUNT_EXTERNAL_READ;}return Zygote.MOUNT_EXTERNAL_WRITE;}@Overridepublic boolean hasExternalStorage(int uid, String packageName) {return true;}});}     ......
}

Android 源码 PackageManagerService 启动流程分析相关推荐

  1. Android4.0源码Launcher启动流程分析【android源码Launcher系列一】

    最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程. Launcher其实是贯彻于手机的整个系统的,时时刻刻都 ...

  2. Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】

    最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程.Launcher其实是贯彻于手机的整个系统的,时时刻刻都在 ...

  3. redhad环境android源码编译,启动流程  |  Android 开源项目  |  Android Open Source Project...

    建议的设备启动流程如下所示: 图 1. 启动时验证流程 适用于 A/B 设备的流程 如果设备使用的是 A/B 系统,则启动流程略有不同.必须先使用启动控件 HAL 将要启动的槽位标记为 SUCCESS ...

  4. Android 源码 Wi-Fi 连接流程分析

    Wi-Fi 连接过程可以从 Settings App 中点击任意 Wi-Fi 条目连接说起.点击条目以后会弹出一个对话框,根据不同的 Wi-Fi 类型需要填入必要的信息,再点击连接按钮,发起连接过程. ...

  5. 渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(上)

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/08/11/es-code02/ 前提 上篇文章写了 ElasticSearch 源码解析 -- ...

  6. Python源码学习:启动流程简析

    Python源码分析 本文环境python2.5系列 参考书籍<<Python源码剖析>> Python简介: python主要是动态语言,虽然Python语言也有编译,生成中 ...

  7. Mybatis源码之核心流程分析

    终于谈到了Mybatis最核心的东西了,最核心的就是通过配置XML文件或注解中的SQL,直接调用接口就能执行配置好的SQL语句并封装成对应的返回类型的数据. 先看一下Mybatis使用示例: //创建 ...

  8. 新手大战Android源码之启动过程

    新手上路,当学习笔记整理记忆. 先来个基本启动流程框架图: 1 招兵买马 古语云:机会总是留给有准备的人.为了这场战役,我准备了如下几个利器. 深入理解Android卷I书籍一本 有关博客数篇,大小不 ...

  9. android启动流程之lk,Android系统之LK启动流程分析(一)

    1.前言 LK是Little Kernel的缩写,在Qualcomm平台的Android系统中普遍采用LK作为bootloader,它是一个开源项目,LK是整个系统的引导部分,所以不是独立存在的,但是 ...

最新文章

  1. 给出一个单链表,不知道节点N的值,只遍历一次就可以求出中间节点,写出算法...
  2. python打包exe之打包深度学习模型踩坑记录及其解决办法。
  3. java并发计数器_浅谈java并发之计数器CountDownLatch
  4. 网络爬虫--14.【糗事百科实战】
  5. text-overflow: ellipsis; 文本溢出隐藏
  6. 2009年2月26日经济报道:奥巴马2010年3.5万亿美元预算。
  7. java通字乱码_Java解决通信过程的中文乱码的问题
  8. 安卓 电话黑名单拦截
  9. 2021年计算机网络常见面试题
  10. idea中字体由繁体变简体
  11. java获取本机ip和端口_java获取本机ip和端口
  12. 存储器的概述——DRAM动态存储器
  13. matlab进行预测误差过大,神经网络预测误差太大怎么办,如何看预测结果
  14. 西湖,半含春雨半垂丝
  15. win10关闭自带的杀毒
  16. Deep Learning模型之:CNN卷积神经网络(一)深度解析CNN
  17. GDAL——命令使用专题——gdalinfo命令
  18. 在Unity中实现简单的动画转场
  19. 计算机硬盘分区信息,你知道电脑硬盘分区Guid格式和MBR格式有什么区别吗?来看看!...
  20. BCG 使用之CBCGPProgressDlgCtrl进度条使用

热门文章

  1. 《深入理解计算机系统》学习笔记——处理器体系结构
  2. 怎么用手机记笔记?安卓手机超实用的笔记app
  3. 随机点名器(java基础)
  4. 使用setViewControllers实现一些不同寻常的跳转
  5. Windows10+ubuntu 双系统安装(针对联想小新air14)
  6. 【C语言】打印出一箭穿心图案(简单版)----gotoxy函数
  7. 程序员,这12个问题让经理比你痛苦多了
  8. 计算机系双选会方案,黄山学院2018届毕业生就业双选会工作方案
  9. 天朝皇叔:学习笔记 Qt 连接数据库sql server
  10. 【解题笔记】编程初学者入门训练