Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings"); //读取并解析/data/system下的XML文件 mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false)); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// 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 // 清理代码路径不存在的孤立软件包 finalint packageSettingCount = mSettings.mPackages.size(); for (int i = packageSettingCount - 1; i >= 0; i--) { PackageSetting ps = mSettings.mPackages.valueAt(i); if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists()) && mSettings.getDisabledSystemPkgLPr(ps.name) != null) { mSettings.mPackages.removeAt(i); mSettings.enableSystemPackageLPw(ps.name); } }
if (!mOnlyCore && mFirstBoot) { // 如果是首次启动,也不是CORE应用,则拷贝预编译的DEX文件 requestCopyPreoptedFiles(); }
// If the build is setup to drop runtime permissions // on update drop the files before loading them. // 开发阶段可以将该配置设置成true,取消所有用户的运行时权限校验。 if (PackageManagerService.CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE) { final VersionInfo internal = getInternalVersion(); if (!Build.FINGERPRINT.equals(internal.fingerprint)) { for (UserInfo user : users) { mRuntimePermissionsPersistence.deleteUserRuntimePermissionsFile(user.id); } } }
/* * Make sure all the updated system packages have their shared users * associated with them. */ // 确保所有更新的系统包都有与其关联的共享用户 final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator(); while (disabledIt.hasNext()) { final PackageSetting disabledPs = disabledIt.next(); final Object id = getSettingLPr(disabledPs.appId); if (id != null && id instanceof SharedUserSetting) { disabledPs.sharedUser = (SharedUserSetting) id; } } .......
//从init.rc中获取环境变量 final String bootClassPath = System.getenv("BOOTCLASSPATH"); final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH"); ......
//获取system/framework目录 File frameworkDir = new File(Environment.getRootDirectory(), "framework");
// 获取内部版本 final VersionInfo ver = mSettings.getInternalVersion(); // 判断fingerprint是否有更新 mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint); if (mIsUpgrade) { logCriticalInfo(Log.INFO, "Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT); }
// when upgrading from pre-M, promote system app permissions from install to runtime //对于Android M之前版本升级上来的情况,需将系统应用程序权限从安装升级到运行时 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. // 对于Android N之前版本升级上来的情况,需像首次启动一样处理package mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
// 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 // 在扫描之前保存预先存在的系统package的名称,不希望自动为新系统应用授予运行时权限 if (mPromoteSystemApps) { Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator(); while (pkgSettingIter.hasNext()) { PackageSetting ps = pkgSettingIter.next(); if (isSystemApp(ps)) { mExistingSystemPackages.add(ps.name); } } }
// Set flag to monitor and not change apk file paths when // scanning install directories. // 设置flag,不在扫描安装时更改文件路径 int scanFlags = SCAN_BOOTING | SCAN_INITIAL; if (mIsUpgrade || mFirstBoot) { scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE; }
// Collect vendor/product/product_services overlay packages. (Do this before scanning // any apps.) // For security and version matching reason, only consider overlay packages if they // reside in the right directory. // 首先扫描vendor/product/product_services的overlay目录下的apk // 路径包括:/vendor/overlay、/product/overlay、/product_services/overlay、/odm/overlay、/oem/overlay、 /system/framework、/system/priv-app、/system/app、/vendor/priv-app、/vendor/app、/odm/priv-app、/odm/app、/oem/app、/oem/priv-app、/product/priv-app、/product/app、/product_services/priv-app、/product_services/app、/product_services/priv-app //扫描传入目录的所有apk文件 scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_VENDOR, 0); ......
// Find base frameworks (resource packages without code). // 扫描fw中的apk, scanDirTracedLI(frameworkDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_NO_DEX | SCAN_AS_SYSTEM | SCAN_AS_PRIVILEGED, 0); if (!mPackages.containsKey("android")) { thrownew IllegalStateException( "Failed to load frameworks package; check log for warnings"); }
// Collect privileged system packages. // 扫描特权目录下的apk,即/system/framework/framework-res.apk,这个包里面全是资源文件,没有代码 final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); scanDirTracedLI(privilegedAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRIVILEGED, 0);
// Collect ordinary system packages. //扫描system/app目录下的apk final File systemAppDir = new File(Environment.getRootDirectory(), "app"); scanDirTracedLI(systemAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM, 0);
...... // 这之间的代码都是扫描上面列出来目录中的apk
// 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<>(); // 删掉不存在的package 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); } }
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. */ // 如果不是系统应用,则不被允许disable if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { 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. */ //如果扫描出来的系统apk在禁用列表中,它将从扫描列表中删除,只能通过OTA再次添加进来 if (mSettings.isDisabledSystemPackageLPr(ps.name)) { .... 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) { //如果系统应用路径,apk包,代码都不存在的话,就把这个应用加到删除系统应用的列表中 possiblyDeletedUpdatedSystemApps.add(ps.name); } else { // We're expecting that the system app should remain disabled, but add // it to expecting better to recover in case the data version cannot // be scanned. //如果apk还在,希望系统应用程序应该保持禁用状态,但是如果数据版本无法被扫描,则添加到恢复列表中。 mExpectingBetter.put(disabledPs.name, disabledPs.codePath); } } } }
// 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循环,移除通过OTA删除的更新系统应用程序的禁用package设置 // 如果更新不再存在,则完全删除该应用。否则,撤消其系统权限 for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) { final String packageName = possiblyDeletedUpdatedSystemApps.get(i); final PackageParser.Package pkg = mPackages.get(packageName); final String msg;
// remove from the disabled system list; do this first so any future // scans of this package are performed without this state mSettings.removeDisabledSystemPackageLPw(packageName);
if (pkg == null) { // should have found an update, but, we didn't; remove everything msg = "Updated system package " + packageName + " no longer exists; removing its data"; // Actual deletion of code and data will be handled by later // reconciliation step // 如果进入到这个if语句,说明被更新的APK不存在了,需要清理他的data数据,但是这里原生什么也没做,意思是没有真正删除原来被禁用apk的数据 } else { // found an update; revoke system privileges msg = "Updated system package " + packageName + " no longer exists; rescanning package on data";
// NOTE: We don't do anything special if a stub is removed from the // system image. But, if we were [like removing the uncompressed // version from the /data partition], this is where it'd be done.
// remove the package from the system and re-scan it without any // special privileges // 删除应用包,并且重新扫描原来的APK路径 removePackageLI(pkg, true); try { final File codePath = new File(pkg.applicationInfo.getCodePath()); scanPackageTracedLI(codePath, 0, scanFlags, 0, null); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse updated, ex-system package: " + e.getMessage()); } }
// one final check. if we still have a package setting [ie. it was // previously scanned and known to the system], but, we don't have // a package [ie. there was an error scanning it from the /data // partition], completely remove the package data. // 删除应用包的数据 final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && mPackages.get(packageName) == null) { removePackageDataLIF(ps, null, null, 0, false);
} 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. */ // 确保所有在/data分区的应用都是真实存在的 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");
try { //重新扫描这个apk文件 scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse original system package: " + e.getMessage()); } } }
// Uncompress and install any stubbed system applications. // This must be done last to ensure all stubs are replaced or disabled. // 解压缩并安装任何存根系统应用程序。这必须最后完成,以确保所有存根被替换或禁用。 installSystemStubPackages(stubSystemApps, scanFlags);
// Resolve the storage manager. // 获取StorageManager的包名 mStorageManagerPackage = getStorageManagerPackageName();
// Resolve protected action filters. Only the setup wizard is allowed to // have a high priority filter for these actions. // 处理受保护的action过滤器。只允许setup wizard(开机向导)为这些action设置高优先级过滤器 mSetupWizardPackage = getSetupWizardPackageName(); mComponentResolver.fixProtectedFilterPriorities();
// Now that we know all of the shared libraries, update all clients to have // the correct library paths. // 由于更新了所有共享库,更新所有的应用以确保这些更新库之后运行起没有问题 updateAllSharedLibrariesLocked(null, Collections.unmodifiableMap(mPackages));
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. // 调整Abis让所有拥有sharedUserId的app都能调用 final List<String> changedAbiCodePath = adjustCpuAbisForSharedUserLPw(setting.packages, null/*scannedPackage*/); if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) { for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) { final String codePathString = changedAbiCodePath.get(i); try { mInstaller.rmdex(codePathString, getDexCodeInstructionSet(getPreferredInstructionSet())); } catch (InstallerException ignored) { } } } // Adjust seInfo to ensure apps which share a sharedUserId are placed in the same // SELinux domain. // 调整seInfo,确保拥有一个sharedUserId的app属于SeLinux的域中 setting.fixSeInfoLocked(); }
// Now that we know all the packages we are keeping, // read and update their last usage times. // 更新用户的使用时间,数据埋点功能采集用户使用数据可以用到 mPackageUsage.read(mPackages); mCompilerStats.read(); }
// If the platform SDK has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some // cases get permissions that the user didn't initially explicitly // allow... it would be nice to have some better way to handle // this situation. // 如果platform sdk从上次启动之后更新了,那需要重新给app更新权限 finalboolean sdkUpdated = (ver.sdkVersion != mSdkVersion); if (sdkUpdated) { Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to " + mSdkVersion + "; regranting permissions for internal storage"); } // 更新权限 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. // 如果是第一次启动,或者从Android M之前升级上来的系统,并且启动正常。 // 那需要在所有已定义的用户中初始化默认的首选应用程序 if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) { for (UserInfo user : sUserManager.getUsers(true)) { mSettings.applyDefaultPreferredAppsLPw(user.id); primeDomainVerificationsLPw(user.id); } }
// Prepare storage for system user really early during boot, // since core system apps like SettingsProvider and SystemUI // can't wait for user to start // 为系统用户准备内存,因为SettingsProvider和SystemUI不可能等待用户去启动 finalint storageFlags; if (StorageManager.isFileEncryptedNativeOrEmulated()) { storageFlags = StorageManager.FLAG_STORAGE_DE; } else { storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; } ......
// If this is first boot after an OTA, and a normal boot, then // we need to clear code cache directories. // Note that we do *not* clear the application profiles. These remain valid // across OTAs and are used to drive profile verification (post OTA) and // profile compilation (without waiting to collect a fresh set of profiles). // 如果是在OTA之后首次启动,并且正常启动,那需要清除代码缓存目录,但不清除应用程序配置文件 if (mIsUpgrade && !onlyCore) { Slog.i(TAG, "Build fingerprint changed; clearing code caches"); for (int i = 0; i < mSettings.mPackages.size(); i++) { final PackageSetting ps = mSettings.mPackages.valueAt(i); if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) { // No apps are running this early, so no need to freeze clearAppDataLIF(ps.pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); } } ver.fingerprint = Build.FINGERPRINT; }
// Grandfather existing (installed before Q) non-system apps to hide // their icons in launcher. // 安装Android-Q前的非系统应用程序在Launcher中隐藏他们的图标 if (!onlyCore && mIsPreQUpgrade) { Slog.i(TAG, "Whitelisting all existing apps to hide their icons"); int size = mSettings.mPackages.size(); for (int i = 0; i < size; i++) { final PackageSetting ps = mSettings.mPackages.valueAt(i); if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { continue; } ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME, UserHandle.USER_SYSTEM); } }
// clear only after permissions and other defaults have been updated // mExistingSystemPackages是在收到OTA之前跟踪现有的系统包列表,他将被清空,当权限和默认设置被更新处理完成之后 mExistingSystemPackages.clear(); mPromoteSystemApps = false;
// All the changes are done during package scanning. ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
// can downgrade to reader Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "write settings"); // 将settings中的数据写回到package.xml中 mSettings.writeLPr(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); }
if (!mOnlyCore) { mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(); mRequiredInstallerPackage = getRequiredInstallerLPr(); mRequiredUninstallerPackage = getRequiredUninstallerLPr(); mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr(); if (mIntentFilterVerifierComponent != null) { mIntentFilterVerifier = new IntentVerifierProxy(mContext, mIntentFilterVerifierComponent); } else { mIntentFilterVerifier = null; } mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr( PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES, SharedLibraryInfo.VERSION_UNDEFINED); mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr( PackageManager.SYSTEM_SHARED_LIBRARY_SHARED, SharedLibraryInfo.VERSION_UNDEFINED); } else { mRequiredVerifierPackage = null; mRequiredInstallerPackage = null; mRequiredUninstallerPackage = null; mIntentFilterVerifierComponent = null; mIntentFilterVerifier = null; mServicesSystemSharedLibraryPackageName = null; mSharedSystemSharedLibraryPackageName = null; } // PermissionController hosts default permission granting and role management, so it's a // critical part of the core system. // PermissionController主持这授予默认权限和角色管理,他是系统的关键组成部分 mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
// Initialize InstantAppRegistry's Instant App list for all users. finalint[] userIds = UserManagerService.getInstance().getUserIds(); for (PackageParser.Package pkg : mPackages.values()) { if (pkg.isSystem()) { continue; } for (int userId : userIds) { final PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null || !ps.getInstantApp(userId) || !ps.getInstalled(userId)) { continue; } mInstantAppRegistry.addInstantAppLPw(userId, ps.appId); } }
mInstallerService = new PackageInstallerService(context, this, mApexManager); final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { if (DEBUG_INSTANT) { Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent); } mInstantAppResolverConnection = new InstantAppResolverConnection( mContext, instantAppResolverComponent.first, instantAppResolverComponent.second); mInstantAppResolverSettingsComponent = getInstantAppResolverSettingsLPr(instantAppResolverComponent.first); } else { mInstantAppResolverConnection = null; mInstantAppResolverSettingsComponent = null; } updateInstantAppInstallerLocked(null);
// Read and update the usage of dex files. // Do this at the end of PM init so that all the packages have their // data directory reconciled. // At this point we know the code paths of the packages, so we can validate // the disk file and build the internal cache. // The usage file is expected to be small so loading and verifying it // should take a fairly small time compare to the other activities (e.g. package // scanning). // 阅读并更新dex文件的用法 // 在PM init结束时执行此操作,以便所有程序包都已协调其数据目录 // 此时知道了包的代码路径,因此可以验证磁盘文件并构建内部缓存 // 使用文件预计很小,因此与其他活动(例如包扫描)相比,加载和验证它应该花费相当小的时间 final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>(); for (int userId : userIds) { userPackages.put(userId, getInstalledPackages(/*flags*/0, userId).getList()); } mDexManager.load(userPackages); if (mIsUpgrade) { MetricsLogger.histogram(null, "ota_package_manager_init_time", (int) (SystemClock.uptimeMillis() - startTime)); } } // synchronized (mPackages) } // synchronized (mInstallLock)
mModuleInfoProvider = new ModuleInfoProvider(mContext, this);
// Now after opening every single application zip, make sure they // are all flushed. Not really needed, but keeps things nice and // tidy. Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "GC"); // 打开应用之后,及时回收处理 Runtime.getRuntime().gc(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// The initial scanning above does many calls into installd while // holding the mPackages lock, but we're mostly interested in yelling // once we have a booted system. // 上面的初始扫描在持有mPackage锁的同时对installd进行了多次调用 mInstaller.setWarnIfHeld(mPackages);
privatevoidscanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime){ ...... // 创建并行扫描解析器 try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser( mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback)) { // Submit files for parsing in parallel int fileCount = 0; for (File file : files) { finalboolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); if (!isPackage) { // Ignore entries which are not packages continue; } //将APK文件交给submit准备解析 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;
public Package parseMonolithicPackage(File apkFile, int flags)throws PackageParserException { final AssetManager assets = newConfiguredAssetManager(); // 里面会收集签名信息 final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); if (mOnlyCoreApps) { if (!lite.coreApp) { thrownew PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Not a coreApp: " + apkFile); } }
...... //如果读到AndroidManifest.xml中的tag是"application",执行parseBaseApplication()进行解析 if (tagName.equals(TAG_APPLICATION)) { if (foundApp) { if (RIGID_PARSER) { outError[0] = "<manifest> has more than one <application>"; mParseError = android.content.pm.PackageManager. INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; returnnull; } else { Slog.w(TAG, "<manifest> has more than one <application>"); XmlUtils.skipCurrentTag(parser); continue; } }
// This is the first onResume in a single life of the activity if (mInstallingTask == null) { PackageInstaller installer = getPackageManager().getPackageInstaller(); PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
if (sessionInfo != null && !sessionInfo.isActive()) { // 在AsyncTask中将APK以流的方式提交到session中然后会调用PackageInstaller.Session.commit() // 最终会通过AIDL 调到PackageInstallerSession.commit()中 mInstallingTask = new InstallingAsyncTask(); mInstallingTask.execute(); } else { // we will receive a broadcast when the install is finished mCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } } }
// For a multiPackage session, read the child sessions // outside of the lock, because reading the child // sessions with the lock held could lead to deadlock // (b/123391593). List<PackageInstallerSession> childSessions = getChildSessions();
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java // HandlerParams是PackageManagerService内部抽象类,具体实现类是MultiPackageInstallParams privateabstractclassHandlerParams{ /** User handle for the user requesting the information or installation. */ ...... finalvoidstartCopy(){ if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this); handleStartCopy(); // 开始拷贝 handleReturnCode(); // 处理拷贝结果 }
privatevoidexecutePostCommitSteps(CommitRequest commitRequest){ for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) { finalboolean instantApp = ((reconciledPkg.scanResult.request.scanFlags & PackageManagerService.SCAN_AS_INSTANT_APP) != 0); final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg; final String packageName = pkg.packageName; //1)进行安装 prepareAppDataAfterInstallLIF(pkg); //2)如果需要替换安装,则需要清楚原有的APP数据 if (reconciledPkg.prepareResult.clearCodeCache) { clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); } if (reconciledPkg.prepareResult.replace) { mDexManager.notifyPackageUpdated(pkg.packageName, pkg.baseCodePath, pkg.splitCodePaths); }
// Prepare the application profiles for the new code paths. // This needs to be done before invoking dexopt so that any install-time profile // can be used for optimizations. //3)为新的代码路径准备应用程序配置文件。这需要在调用dexopt之前完成,以便任何安装时配置文件都可以用于优化。 mArtManagerService.prepareAppProfiles( pkg, resolveUserIds(reconciledPkg.installArgs.user.getIdentifier()), /* updateReferenceProfileContent= */true);
// Check whether we need to dexopt the app. // // NOTE: it is IMPORTANT to call dexopt: // - after doRename which will sync the package data from PackageParser.Package and // its corresponding ApplicationInfo. // - after installNewPackageLIF or replacePackageLIF which will update result with the // uid of the application (pkg.applicationInfo.uid). // This update happens in place! // // We only need to dexopt if the package meets ALL of the following conditions: // 1) it is not an instant app or if it is then dexopt is enabled via gservices. // 2) it is not debuggable. // // Note that we do not dexopt instant apps by default. dexopt can take some time to // complete, so we skip this step during installation. Instead, we'll take extra time // the first time the instant app starts. It's preferred to do it this way to provide // continuous progress to the useur instead of mysteriously blocking somewhere in the // middle of running an instant app. The default behaviour can be overridden // via gservices. //4)执行dex优化。只有不是instant app和debug app才做dex优化。 finalboolean performDexopt = (!instantApp || Global.getInt(mContext.getContentResolver(), Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
if (performDexopt) { // Compile the layout resources. if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts"); mViewCompiler.compileLayouts(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); }
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Do not run PackageDexOptimizer through the local performDexOpt // method because `pkg` may not be in `mPackages` yet. // // Also, don't fail application installs if the dexopt step fails. DexoptOptions dexoptOptions = new DexoptOptions(packageName, REASON_INSTALL, DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE); // 执行dex优化 mPackageDexOptimizer.performDexOpt(pkg, null/* instructionSets */, getOrCreateCompilerPackageStats(pkg), mDexManager.getPackageUseInfoOrDefault(packageName), dexoptOptions); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); }
// Notify BackgroundDexOptService that the package has been changed. // If this is an update of a package which used to fail to compile, // BackgroundDexOptService will remove it from its blacklist. // TODO: Layering violation BackgroundDexOptService.notifyPackageChanged(packageName); } }
privatevoidprepareAppDataAfterInstallLIF(PackageParser.Package pkg){ ...... UserManagerInternal umInternal = getUserManagerInternal(); for (UserInfo user : um.getUsers(false/* excludeDying */)) { ...... if (ps.getInstalled(user.id)) { // TODO: when user data is locked, mark that we're still dirty prepareAppDataLIF(pkg, user.id, flags); } } }
1 2 3 4 5 6 7 8 9 10 11 12
// 验证目录是否存在,以及所有已安装应用的所有权和标签是否正确。如果所有权不匹配,将尝试通过擦除数据恢复系统应用程序;第三方应用程序数据保持不变。 privatevoidprepareAppDataLIF(PackageParser.Package pkg, int userId, int flags){ if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } prepareAppDataLeafLIF(pkg, userId, flags); finalint childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { prepareAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } }
privatevoidmoveAbArtifacts(Installer installer){ if (mDexoptCommands != null) { thrownew IllegalStateException("Should not be ota-dexopting when trying to move."); }
// 如果没有升级,直接跳过 if (!mPackageManagerService.isUpgrade()) { Slog.d(TAG, "No upgrade, skipping A/B artifacts check."); return; }
// Look into all packages. Collection<PackageParser.Package> pkgs = mPackageManagerService.getPackages(); int packagePaths = 0; int pathsSuccessful = 0; for (PackageParser.Package pkg : pkgs) { if (pkg == null) { continue; }
// Does the package have code? If not, there won't be any artifacts. // 如果apk只是资源文件,直接跳过 if (!PackageDexOptimizer.canOptimizePackage(pkg)) { continue; } if (pkg.codePath == null) { Slog.w(TAG, "Package " + pkg + " can be optimized but has null codePath"); continue; }
// If the path is in /system, /vendor or /product, ignore. It will have been // ota-dexopted into /data/ota and moved into the dalvik-cache already. // 如果是/system、/vendor、/product目录下的应用不用做优化,因为已经在系统预编译的时候,编译到/data/dalvik-cache中了。 if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor") || pkg.codePath.startsWith("/product")) { continue; }
final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo); // 获取代码的目录 final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly(); final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); for (String dexCodeInstructionSet : dexCodeInstructionSets) { for (String path : paths) { String oatDir = PackageDexOptimizer.getOatDir(new File(pkg.codePath)). getAbsolutePath();
// TODO: Check first whether there is an artifact, to save the roundtrip time.
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java @Override publicvoidupdatePackagesIfNeeded(){ enforceSystemOrRoot("Only the system can request package update");
// We need to re-extract after an OTA. // 是否需要重新提取dex boolean causeUpgrade = isUpgrade();
// First boot or factory reset. // Note: we also handle devices that are upgrading to N right now as if it is their // first boot, as they do not have profile data. // 如果是第一次启动或者恢复出厂都会重新进行dex优化 boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;
// We need to re-extract after a pruned cache, as AoT-ed files will be out of date. // 如果清空缓存之后也要重新提取dex boolean causePrunedCache = VMRuntime.didPruneDalvikCache(); // 如果不是以上三个原因所致,直接返回,不做dex优化 if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) { return; }
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java publicstatic List<PackageParser.Package> getPackagesForDexopt( Collection<PackageParser.Package> packages, PackageManagerService packageManagerService) { ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages); LinkedList<PackageParser.Package> result = new LinkedList<>(); ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size());
// Give priority to core apps. // 对core app进行排序 applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp, packageManagerService);
// Give priority to system apps that listen for pre boot complete. // 对system app进行排序 Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM); applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs, sortTemp, packageManagerService);
// Give priority to apps used by other apps. // 对其他app进行排序 DexManager dexManager = packageManagerService.getDexManager(); applyPackageFilter((pkg) -> dexManager.getPackageUseInfoOrDefault(pkg.packageName) .isAnyCodePathUsedByOtherApps(), result, remainingPkgs, sortTemp, packageManagerService);
// Filter out packages that aren't recently used, add all remaining apps. // TODO: add a property to control this? // 将列表中最近没有使用的应用移除,添加常驻app Predicate<PackageParser.Package> remainingPredicate; if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) { if (DEBUG_DEXOPT) { Log.i(TAG, "Looking at historical package use"); } // Get the package that was used last. PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) -> Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(), pkg2.getLatestForegroundPackageUseTimeInMills())); if (DEBUG_DEXOPT) { Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use"); } long estimatedPreviousSystemUseTime = lastUsed.getLatestForegroundPackageUseTimeInMills(); // Be defensive if for some reason package usage has bogus data. if (estimatedPreviousSystemUseTime != 0) { finallong cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS; remainingPredicate = (pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime; } else { // No meaningful historical info. Take all. remainingPredicate = (pkg) -> true; } sortPackagesByUsageDate(remainingPkgs, packageManagerService); } else { // No historical info. Take all. remainingPredicate = (pkg) -> true; } applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp, packageManagerService);
if (DEBUG_DEXOPT) { Log.i(TAG, "Packages to be dexopted: " + packagesToString(result)); Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs)); }
// 遍历每一个apk for (PackageParser.Package pkg : pkgs) { numberOfPackagesVisited++;
boolean useProfileForDexopt = false;
if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. // 拷贝预编译配置,因为不会对已经编译过的代码进行JIT采样 File profileFile = new File(getPrebuildProfilePath(pkg)); // Copy profile if it exists. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The issue // is that we don't have a good way to say "do this only once". // 拷贝配置文件到InstallerD if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Installer failed to copy system profile!"); } else { ...... } else { PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); // Handle compressed APKs in this path. Only do this for stubs with profiles to // minimize the number off apps being speed-profile compiled during first boot. // The other paths will not change the filter. // 解压apk将配置文件拷贝到installD if (disabledPs != null && disabledPs.pkg.isStub) { ..... } } } // 如果apk没有代码不进行优化 if (!PackageDexOptimizer.canOptimizePackage(pkg)) { .... continue; } ...... // 拼接优化参数执行dex优化 int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, pkgCompilationReason, dexoptFlags));
// Run dexopt on a given package. Returns true if dexopt did not fail, i.e. // if the package can now be considered up to date for the given filter. privateintperformDexOptInternal(DexoptOptions options){ ...... try { synchronized (mInstallLock) { return performDexOptInternalWithDependenciesLI(p, options); } } finally { Binder.restoreCallingIdentity(callingId); } }
int result = DEX_OPT_SKIPPED; for (int i = 0; i < paths.size(); i++) { ......
// Append shared libraries with split dependencies for this split. // 获取共享库路径 String path = paths.get(i); if (options.getSplitName() != null) { // We are asked to compile only a specific split. Check that the current path is // what we are looking for. if (!options.getSplitName().equals(new File(path).getName())) { continue; } }
..... for (String dexCodeIsa : dexCodeInstructionSets) { // 对给定路径的dex进行优化 int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid, packageStats, options.isDowngrade(), profileName, dexMetadataPath, options.getCompilationReason()); // The end result is: // - FAILED if any path failed, // - PERFORMED if at least one path needed compilation, // - SKIPPED when all paths are up to date if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) { result = newResult; } } } return result; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
privateintdexOptPath(PackageParser.Package pkg, String path, String isa, String compilerFilter, boolean profileUpdated, String classLoaderContext, int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade, String profileName, String dexMetadataPath, int compilationReason){ ....
// TODO: Consider adding 2 different APIs for primary and secondary dexopt. // installd only uses downgrade flag for secondary dex files and ignores it for // primary dex files. // 调用到mInstalld.dexopt(),由InstallD守护进程对执行路径下的dex进行优化 mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags, compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo, false/* downgrade*/, pkg.applicationInfo.targetSdkVersion, profileName, dexMetadataPath, getAugmentedReasonName(compilationReason, dexMetadataPath != null));
// Check if we're dealing with a secondary dex file and if we need to compile it. // 检查我们是否正在处理一个辅助dex文件,以及是否需要编译它。 std::string oat_dir_str; if (is_secondary_dex) { if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid, instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str, downgrade, class_loader_context, error_msg)) { ...... // Open the input file. // 读入待优化的dex文件 unique_fd input_fd(open(dex_path, O_RDONLY, 0)); ......
// Remember when we kicked it off try { mLastMaintenance = System.currentTimeMillis(); mLastMaintenanceFile.setLastModified(mLastMaintenance); } catch (Exception e) { Slog.e(TAG, "Unable to record last fstrim!"); }
// TODO: Reintroduce shouldBenchmark() test fstrim(0, null);
publicvoidsystemReady(){ ...... // 获取各种配置 sUserManager.systemReady(); // If we upgraded grant all default permissions before kicking off. // 设置默认权限 for (int userId : grantPermissionsUserIds) { mDefaultPermissionPolicy.grantDefaultPermissions(userId); } // Now that we've scanned all packages, and granted any default // permissions, ensure permissions are updated. Beware of dragons if you // try optimizing this. synchronized (mPackages) { // 扫描所有包,确认所有应用更新了默认权限 mPermissionManager.updateAllPermissions( StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(), mPermissionCallback); }
// Kick off any messages waiting for system ready // 启动所有等待系统就绪的消息 if (mPostSystemReadyMessages != null) { for (Message msg : mPostSystemReadyMessages) { msg.sendToTarget(); } mPostSystemReadyMessages = null; }
// Watch for external volumes that come and go over time final StorageManager storage = mContext.getSystemService(StorageManager.class); storage.registerListener(mStorageListener);
// Now that we're mostly running, clean up stale users and apps sUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL); reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL); // 通知PermissionManger系统启动完毕。 mPermissionManager.systemReady();
// frameworks/base/core/java/com/android/server/SystemConfig.java SystemConfig() { // Read configuration from system readPermissions(Environment.buildPath( Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
// Read configuration from the old permissions dir readPermissions(Environment.buildPath( Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
// Make sure all dynamic permissions have been assigned to a package, // and make sure there are no dangling permissions. // 更新所有应用的动态权限,确保没有摇摆权限没有赋值给应用 flags = updatePermissions(changingPkgName, changingPkg, flags);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "grantPermissions"); // Now update the permissions for all packages, in particular // replace the granted permissions of the system packages. // 逐个跟新权限,尤其是给系统应用赋权 if ((flags & UPDATE_PERMISSIONS_ALL) != 0) { for (PackageParser.Package pkg : allPackages) { if (pkg != changingPkg) { // Only replace for packages on requested volume final String volumeUuid = getVolumeUuidForPackage(pkg); finalboolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0) && Objects.equals(replaceVolumeUuid, volumeUuid); // 如果uuid不一样,就更新改应用权限 grantPermissions(pkg, replace, changingPkgName, callback); } } }
if (changingPkg != null) { // Only replace for packages on requested volume final String volumeUuid = getVolumeUuidForPackage(changingPkg); finalboolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0) && Objects.equals(replaceVolumeUuid, volumeUuid); grantPermissions(changingPkg, replace, changingPkgName, callback); } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); }
Intent nextActivity = new Intent(intent); nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
......
if (isSessionInstall) { // 设置安装界面 nextActivity.setClass(this, PackageInstallerActivity.class); } else { Uri packageUri = intent.getData();
if (packageUri != null && packageUri.getScheme().equals( ContentResolver.SCHEME_CONTENT)) { // [IMPORTANT] This path is deprecated, but should still work. Only necessary // features should be added. // Copy file to prevent it from being changed underneath this process nextActivity.setClass(this, InstallStaging.class); } elseif (packageUri != null && packageUri.getScheme().equals( PackageInstallerActivity.SCHEME_PACKAGE)) { nextActivity.setClass(this, PackageInstallerActivity.class); } else { Intent result = new Intent(); result.putExtra(Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_FAILED_INVALID_URI); setResult(RESULT_FIRST_USER, result);
// load dummy layout with OK button disabled until we override this layout in // startInstallConfirm // 绑定确定安装或者取消按钮 bindUi(R.layout.install_confirm, false); // 此方法用于检查是否运行安装,如果运行就初始化安装流程。 checkIfAllowedAndInitiateInstall(); }
privatevoidcheckIfAllowedAndInitiateInstall(){ if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) { // 如果临时运行安装,就走这里 initiateInstall(); return; } // If the admin prohibits it, just show error and exit. // 如果不允许安装,走下面 if (isUnknownSourcesDisallowed()) { if ((mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle()) & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) { // Someone set user restriction via UserManager#setUserRestriction. We don't want to // break apps that might already be doing this showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER); return; } else { startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)); finish(); } } else { handleUnknownSources(); } }
// 此方法主要检查要安装的包是否已经被安装 privatevoidinitiateInstall(){ String pkgName = mPkgInfo.packageName; // Check if there is already a package on the device with this name // but it has been renamed to something else. // 校验设备上是否已经有该包 String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName }); if (oldName != null && oldName.length > 0 && oldName[0] != null) { pkgName = oldName[0]; mPkgInfo.packageName = pkgName; mPkgInfo.applicationInfo.packageName = pkgName; } // Check if package is already installed. display confirmation dialog if replacing pkg // 确认该包是否已经安装,弹出对话框让用户确认是否安装 try { // This is a little convoluted because we want to get all uninstalled // apps, but this may include apps with just data, and if it is just // data we still want to count it as "installed". mAppInfo = mPm.getApplicationInfo(pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES); if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { mAppInfo = null; } } catch (NameNotFoundException e) { mAppInfo = null; } // 在该方法中显示app申请的权限,并且绑定ui供用户选择是否安装。 startInstallConfirm(); }
privatevoidstartInstallConfirm(){ // We might need to show permissions, load layout with permissions // 绑定确认取消按钮,供用户选择 if (mAppInfo != null) { bindUi(R.layout.install_confirm_perm_update, true); } else { bindUi(R.layout.install_confirm_perm, true); }
((TextView) findViewById(R.id.install_confirm_question)) .setText(R.string.install_confirm_question); TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost); tabHost.setup(); // 用viewpager显示权限。 ViewPager viewPager = (ViewPager)findViewById(R.id.pager); TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager); // If the app supports runtime permissions the new permissions will // be requested at runtime, hence we do not show them at install. boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M; boolean permVisible = false; mScrollView = null; mOkCanInstall = false; int msg = 0;
AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo); finalint N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL); ...... // 以下是显示应用申请的各种权限 }
# Script to start "pm" on the device, which has a very rudimentary shell. # base=/system export CLASSPATH=$base/framework/pm.jar exec app_process $base/bin com.android.commands.pm.Pm "$@"
可以发现其执行的是pm.jar包的main函数,我们进入Pm.java类。
1 2 3 4 5 6 7 8 9 10 11 12 13
publicstaticvoidmain(String[] args){ int exitCode = 1; try { exitCode = new Pm().run(args); } catch (Exception e) { Log.e(TAG, "Error", e); System.err.println("Error: " + e); if (e instanceof RemoteException) { System.err.println(PM_NOT_RUNNING_ERR); } } System.exit(exitCode); }