Android 源码 PackageManagerService 启动流程分析

2024-05-01 21:08

本文主要是介绍Android 源码 PackageManagerService 启动流程分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《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 启动流程分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/952670

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL