android 9 adb安装过程学习(四)覆盖安装

六、PackageManagerService.replacePackageLIF - 覆盖安装

一、参数分析

位置:"frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java"
这里我们来回顾下传入的参数:final int policyFlags 就是我们之前的解析参数 parseFlags

		// Retrieve PackageSettings and parse package@ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY| PackageParser.PARSE_ENFORCE_CODE| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)| (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);

同时,对于扫描标志位 scanFlags,会做如下处理:

		//【2】设置扫描参数@ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;if (args.move != null) {// moving a complete application; perform an initial scan on the new install location//【2.1】如果 args.move 不为 null,表示正在移动一个 app,我们会对其进行一个初始化的扫描// 增加 SCAN_INITIAL 位scanFlags |= SCAN_INITIAL;}if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {//【2.2】如果安装参数指定了 INSTALL_DONT_KILL_APP,那么增加 SCAN_DONT_KILL_APP 位scanFlags |= SCAN_DONT_KILL_APP;}if (instantApp) {scanFlags |= SCAN_AS_INSTANT_APP;}//...//【13】根据安装参数做不同的处理if (args.move != null) {// We did an in-place move, so dex is ready to roll//【13.1】如果是 move package,进入这里scanFlags |= SCAN_NO_DEX;	// 设置以下标签,无需做 odex,我们需要已有的移动过去即可scanFlags |= SCAN_MOVE;//...} else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {//【13.2】如果不是 forward lock 模式安装且没有安装到外置存储上,进入这里// Enable SCAN_NO_DEX flag to skip dexopt at a later stagescanFlags |= SCAN_NO_DEX;	//	扫描参数设置 SCAN_NO_DEX,意味着后面不做 odex,因为这里会做//...}

上面,我们省略掉了不重要的代码段

二、方法分析

下面继续分析核心方法:

    private void replacePackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags,final @ScanFlags int scanFlags, UserHandle user, String installerPackageName,PackageInstalledInfo res, int installReason) {final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;final PackageParser.Package oldPackage;final PackageSetting ps;final String pkgName = pkg.packageName;final int[] allUsers;final int[] installedUsers;synchronized(mPackages) {oldPackage = mPackages.get(pkgName);if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);// don't allow upgrade to target a release SDK from a pre-release SDKfinal boolean oldTargetsPreRelease = oldPackage.applicationInfo.targetSdkVersion== android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;final boolean newTargetsPreRelease = pkg.applicationInfo.targetSdkVersion== android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;//【2】如果安装设置了 PackageParser.PARSE_FORCE_SDK,那就要校验下 sdkif (oldTargetsPreRelease&& !newTargetsPreRelease&& ((parseFlags & PackageParser.PARSE_FORCE_SDK) == 0)) {Slog.w(TAG, "Can't install package targeting released sdk");res.setReturnCode(PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE);return;}ps = mSettings.mPackages.get(pkgName);// verify signatures are valid//【4】校验签名,不匹配,不能安装final KeySetManagerService ksms = mSettings.mKeySetManagerService;if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {if (!ksms.checkUpgradeKeySetLocked(ps, pkg)) {res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,"New package not signed by keys specified by upgrade-keysets: "+ pkgName);return;}} else {// default to original signature matchingif (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails,PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)&& !oldPackage.mSigningDetails.checkCapability(pkg.mSigningDetails,PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,"New package has a different signature: " + pkgName);return;}}// don't allow a system upgrade unless the upgrade hash matches//【5】如果旧的应用是 sys app,并且需要强制 hash 校验,那就会在这里校验 hashif (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {byte[] digestBytes = null;try {final MessageDigest digest = MessageDigest.getInstance("SHA-512");updateDigest(digest, new File(pkg.baseCodePath));if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {for (String path : pkg.splitCodePaths) {updateDigest(digest, new File(path));}}digestBytes = digest.digest();} catch (NoSuchAlgorithmException | IOException e) {res.setError(INSTALL_FAILED_INVALID_APK,"Could not compute hash: " + pkgName);return;}if (!Arrays.equals(oldPackage.restrictUpdateHash, digestBytes)) {res.setError(INSTALL_FAILED_INVALID_APK,"New package fails restrict-update check: " + pkgName);return;}// retain upgrade restrictionpkg.restrictUpdateHash = oldPackage.restrictUpdateHash;}// Check for shared user id changes//【6】检查 shareUserId 是否变化String invalidPackageName =getParentOrChildPackageChangedSharedUser(oldPackage, pkg);if (invalidPackageName != null) {res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,"Package " + invalidPackageName + " tried to change user "+ oldPackage.mSharedUserId);return;}// check if the new package supports all of the abis which the old package supportsboolean oldPkgSupportMultiArch = oldPackage.applicationInfo.secondaryCpuAbi != null;boolean newPkgSupportMultiArch = pkg.applicationInfo.secondaryCpuAbi != null;if (isSystemApp(oldPackage) && oldPkgSupportMultiArch && !newPkgSupportMultiArch) {res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,"Update to package " + pkgName + " doesn't support multi arch");return;}// In case of rollback, remember per-user/profile install state//【7】获得系统中所有的 user,和上一次安装的目标 userallUsers = sUserManager.getUserIds();installedUsers = ps.queryInstalledUsers(allUsers, true);// don't allow an upgrade from full to ephemeral//【3】如果之前安装是 no ephemeral,现在是 ephemeral,那么不能安装if (isInstantApp) {if (user == null || user.getIdentifier() == UserHandle.USER_ALL) {for (int currentUser : allUsers) {if (!ps.getInstantApp(currentUser)) {// can't downgrade from full to instantSlog.w(TAG, "Can't replace full app with instant app: " + pkgName+ " for user: " + currentUser);res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);return;}}} else if (!ps.getInstantApp(user.getIdentifier())) {// can't downgrade from full to instantSlog.w(TAG, "Can't replace full app with instant app: " + pkgName+ " for user: " + user.getIdentifier());res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);return;}}}// Update what is removed//【×7.1.1】针对与 replace 的情况,会创建一个 PackageRemovedInfo,封装被 replace 的 app 的信息!// 如果有子包的话,也会做相同的事情res.removedInfo = new PackageRemovedInfo(this);res.removedInfo.uid = oldPackage.applicationInfo.uid;res.removedInfo.removedPackage = oldPackage.packageName;res.removedInfo.installerPackageName = ps.installerPackageName;res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null;res.removedInfo.isUpdate = true;	// 表示要更新 packageres.removedInfo.origUsers = installedUsers;res.removedInfo.installReasons = new SparseArray<>(installedUsers.length);for (int i = 0; i < installedUsers.length; i++) {final int userId = installedUsers[i];res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));}final int childCount = (oldPackage.childPackages != null)? oldPackage.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {boolean childPackageUpdated = false;PackageParser.Package childPkg = oldPackage.childPackages.get(i);final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);if (res.addedChildPackages != null) {PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);if (childRes != null) {childRes.removedInfo.uid = childPkg.applicationInfo.uid;childRes.removedInfo.removedPackage = childPkg.packageName;if (childPs != null) {childRes.removedInfo.installerPackageName = childPs.installerPackageName;}childRes.removedInfo.isUpdate = true;childRes.removedInfo.installReasons = res.removedInfo.installReasons;childPackageUpdated = true;}}if (!childPackageUpdated) {PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);childRemovedRes.removedPackage = childPkg.packageName;if (childPs != null) {childRemovedRes.installerPackageName = childPs.installerPackageName;}childRemovedRes.isUpdate = false;childRemovedRes.dataRemoved = true;synchronized (mPackages) {if (childPs != null) {childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers, true);}}if (res.removedInfo.removedChildPackages == null) {res.removedInfo.removedChildPackages = new ArrayMap<>();}res.removedInfo.removedChildPackages.put(childPkg.packageName, childRemovedRes);}}//【9】判断下旧应用是系统应用,还是非系统应用,然后做不同的处理boolean sysPkg = (isSystemApp(oldPackage));if (sysPkg) {// Set the system/privileged/oem/vendor/product flags as needed//【9.1】如果是系统应用,再判断是是否是系统特权应用final boolean privileged =(oldPackage.applicationInfo.privateFlags& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;final boolean oem =(oldPackage.applicationInfo.privateFlags& ApplicationInfo.PRIVATE_FLAG_OEM) != 0;final boolean vendor =(oldPackage.applicationInfo.privateFlags& ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;final boolean product =(oldPackage.applicationInfo.privateFlags& ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0//【9.2】对于系统应用会增加如下的解析 flags;final @ParseFlags int systemParseFlags = parseFlags;final @ScanFlags int systemScanFlags = scanFlags| SCAN_AS_SYSTEM| (privileged ? SCAN_AS_PRIVILEGED : 0)| (oem ? SCAN_AS_OEM : 0)| (vendor ? SCAN_AS_VENDOR : 0)| (product ? SCAN_AS_PRODUCT : 0);//【×2.1】处理系统应用的 replacereplaceSystemPackageLIF(oldPackage, pkg, systemParseFlags, systemScanFlags,user, allUsers, installerPackageName, res, installReason);} else {//【×2.2】处理下非系统应用的 replacereplaceNonSystemPackageLIF(oldPackage, pkg, parseFlags, scanFlags,user, allUsers, installerPackageName, res, installReason);}}

对于判断是否是系统应用,调用了如下的接口:

    private static boolean isSystemApp(PackageParser.Package pkg) {return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;}

2.1 replaceSystemPackageLIF - 覆盖安装系统应用

这里我们来回顾下传入的参数:final int policyFlags 就是我们之前的解析参数 parseFlags

		//【1】默认情况下: mDefParseFlags = 0 @ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY| PackageParser.PARSE_ENFORCE_CODE| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)| (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);//【2】针对系统应用额外增加如下的标志       final int systemPolicyFlags = policyFlags| PackageParser.PARSE_IS_SYSTEM| (privileged ? PackageParser.PARSE_IS_PRIVILEGED : 0);

同时,对于扫描标志位 scanFlags,和上面保持一致,下面我们来分析下更新 sys app 的流程

    private void replaceSystemPackageLIF(PackageParser.Package deletedPackage,PackageParser.Package pkg, final @ParseFlags int parseFlags,final @ScanFlags int scanFlags, UserHandle user,int[] allUsers, String installerPackageName, PackageInstalledInfo res,int installReason) {if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg+ ", old=" + deletedPackage);final boolean disabledSystem;// Remove existing system package//【*2.1.1】移除掉已安装的 sys app 的数据,包括解析到的四大组件removePackageLI(deletedPackage, true);synchronized (mPackages) {//【*2.1.2】disable 掉系统应用disabledSystem = disableSystemPackageLPw(deletedPackage, pkg);}//【1】根据 disabledSystem 的值做不同处理if (!disabledSystem) {// We didn't need to disable the .apk as a current system package,// which means we are replacing another update that is already// installed.  We need to make sure to delete the older one's .apk.//【1.1】如果 disabledSystem 为 false,说明我们之前已经更新过 sys app 了,其已经被 disable 过了// 我们现在更新的是位于 data 分区的那个 app,所以要删除掉那个旧的 data app//【*2.1.3】这里会根据要删除 data 分区的 apk 的数据,创建一个 InstallArgs,用于执行删除操作res.removedInfo.args = createInstallArgsForExisting(0,deletedPackage.applicationInfo.getCodePath(),deletedPackage.applicationInfo.getResourcePath(),getAppDexInstructionSets(deletedPackage.applicationInfo));} else {//【1.2】此时无需删除 app,所以设置为 nullres.removedInfo.args = null;}// Successfully disabled the old package. Now proceed with re-installation//【*2.1.4】清除掉新安装的应用 code_cache 目录下的文件数据clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE| StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);//【2】给本次扫描的新 app 设置 ApplicationInfo.FLAG_UPDATED_SYSTEM_APP 标志位pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);PackageParser.Package newPackage = null;try {// Add the package to the internal data structures//【3】再次扫描 apk 文件newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);// Set the update and install times//【4】更新新 apk 的安装时间和最新更新时间(包括子包的)PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras;setInstallAndUpdateTime(newPackage, deletedPkgSetting.firstInstallTime,System.currentTimeMillis());// Update the package dynamic state if succeeded//【5】处理安装成功的情况if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {// Now that the install succeeded make sure we remove data// directories for any child package the update removed.final int deletedChildCount = (deletedPackage.childPackages != null)? deletedPackage.childPackages.size() : 0;final int newChildCount = (newPackage.childPackages != null)? newPackage.childPackages.size() : 0;//【5.1】比较旧的应用和新安装的应用的子包for (int i = 0; i < deletedChildCount; i++) {PackageParser.Package deletedChildPkg = deletedPackage.childPackages.get(i);boolean childPackageDeleted = true;for (int j = 0; j < newChildCount; j++) {PackageParser.Package newChildPkg = newPackage.childPackages.get(j);if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {childPackageDeleted = false;break;}}//【5.1.1】如果旧的应用的子包被删除了,那会移除旧的应用子包遗留下的数据if (childPackageDeleted) {PackageSetting ps = mSettings.getDisabledSystemPkgLPr(deletedChildPkg.packageName);if (ps != null && res.removedInfo.removedChildPackages != null) {PackageRemovedInfo removedChildRes = res.removedInfo.removedChildPackages.get(deletedChildPkg.packageName);// 移除数据removePackageDataLIF(ps, allUsers, removedChildRes, 0, false);removedChildRes.removedForAllUsers = mPackages.get(ps.name) == null;}}}//【*7.2.1】更新数据updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,installReason);//【*7.2.2】准备数据目录,即 data/data/packageName 的目录prepareAppDataAfterInstallLIF(newPackage);mDexManager.notifyPackageUpdated(newPackage.packageName,newPackage.baseCodePath, newPackage.splitCodePaths);}} catch (PackageManagerException e) {res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);res.setError("Package couldn't be installed in " + pkg.codePath, e);}//【3】处理安装失败的情况if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {// Re installation failed. Restore old information// Remove new pkg informationif (newPackage != null) {//【*2.1.6】移除新解析的 apk 的数据removeInstalledPackageLI(newPackage, true);}// Add back the old system packagetry {//【3.1】重新扫描旧的 apk(注意如果之前 system apk 已经被更新过了,那么这里// 恢复的是处于 data 的那个新的 apk)scanPackageTracedLI(deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);} catch (PackageManagerException e) {Slog.e(TAG, "Failed to restore original package: " + e.getMessage());}synchronized (mPackages) {if (disabledSystem) {//【*2.1.7】如果本次是第一次更新(即 disable 了 sys app),那么我们要 enable sys appenableSystemPackageLPw(deletedPackage);}// Ensure the installer package name up to date//【3.2】更新升级包的安装信息,就是 PackageInstallersetInstallerPackageNameLPw(deletedPackage, installerPackageName);// Update permissions for restored package//【3.3】更新权限信息,关于权限的相关逻辑,这里不再分析,但是能推测到更新权限的原因mPermissionManager.updatePermissions(deletedPackage.packageName, deletedPackage, false, mPackages.values(),mPermissionCallback);//【3.4】持久化数据mSettings.writeLPr();}Slog.i(TAG, "Successfully restored package : " + deletedPackage.packageName+ " after failed upgrade");}}

2.1.1 removePackageLI

移除旧的apk的数据信息:

    private void removePackageLI(PackageParser.Package pkg, boolean chatty) {// Remove the parent package setting  处理父包PackageSetting ps = (PackageSetting) pkg.mExtras;if (ps != null) {removePackageLI(ps, chatty);	//【1.1】移除父包的 Settings 对象的 Settings 数据}// Remove the child package setting 处理子包final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {PackageParser.Package childPkg = pkg.childPackages.get(i);ps = (PackageSetting) childPkg.mExtras;if (ps != null) {removePackageLI(ps, chatty);	//【2.1】移除子包的 Settings 对象的 Settings 数据}}}

调用了另外一个 removePackageLI 方法

    void removepackageli(packagesetting ps, boolean chatty) {if (debug_install) {if (chatty)log.d(tag, "removing package " + ps.name);}// writersynchronized (mpackages) {//【1】将应用对应的 PackageSettings 对象删除掉mpackages.remove(ps.name);final packageparser.package pkg = ps.pkg;if (pkg != null) {//【×2.1.1.1】移除四大组件数据cleanpackagedatastructureslilpw(pkg, chatty);}}}
2.1.1.1 cleanPackageDataStructuresLILPw

用于删除 apk 的四大组件和共享库数据:

    void cleanPackageDataStructuresLILPw(PackageParser.Package pkg, boolean chatty) {//【1】移除 providerint N = pkg.providers.size();StringBuilder r = null;int i;for (i=0; i<N; i++) {PackageParser.Provider p = pkg.providers.get(i);mProviders.removeProvider(p);if (p.info.authority == null) {/* There was another ContentProvider with this authority when* this app was installed so this authority is null,* Ignore it as we don't have to unregister the provider.* 【1.1】表示系统之前已经有相同 authority 的 provider,那么这个应用的 provider 是不会注册的.* 对于没有注册的 provider 不处理*/continue;}String names[] = p.info.authority.split(";");for (int j = 0; j < names.length; j++) {if (mProvidersByAuthority.get(names[j]) == p) {mProvidersByAuthority.remove(names[j]);if (DEBUG_REMOVE) {if (chatty)Log.d(TAG, "Unregistered content provider: " + names[j]+ ", className = " + p.info.name + ", isSyncable = "+ p.info.isSyncable);}}}if (DEBUG_REMOVE && chatty) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(p.info.name);}}if (r != null) {if (DEBUG_REMOVE) Log.d(TAG, "  Providers: " + r);}//【2】移除 serviceN = pkg.services.size();r = null;for (i=0; i<N; i++) {PackageParser.Service s = pkg.services.get(i);mServices.removeService(s);if (chatty) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(s.info.name);}}if (r != null) {if (DEBUG_REMOVE) Log.d(TAG, "  Services: " + r);}//【3】移除 receiverN = pkg.receivers.size();r = null;for (i=0; i<N; i++) {PackageParser.Activity a = pkg.receivers.get(i);mReceivers.removeActivity(a, "receiver");if (DEBUG_REMOVE && chatty) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(a.info.name);}}if (r != null) {if (DEBUG_REMOVE) Log.d(TAG, "  Receivers: " + r);}//【4】移除 activityN = pkg.activities.size();r = null;for (i=0; i<N; i++) {PackageParser.Activity a = pkg.activities.get(i);mActivities.removeActivity(a, "activity");if (DEBUG_REMOVE && chatty) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(a.info.name);}}if (r != null) {if (DEBUG_REMOVE) Log.d(TAG, "  Activities: " + r);}//【5】移除定义的 permission,设置了 appop 标志为的权限,从 mAppOpPermissionPackages 也要移除mPermissionManager.removeAllPermissions(pkg, chatty);//【7】移除请求的 instrumentationN = pkg.instrumentation.size();r = null;for (i=0; i<N; i++) {PackageParser.Instrumentation a = pkg.instrumentation.get(i);mInstrumentation.remove(a.getComponentName());if (DEBUG_REMOVE && chatty) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(a.info.name);}}if (r != null) {if (DEBUG_REMOVE) Log.d(TAG, "  Instrumentation: " + r);}//【8】移除 SharedLibrariesr = null;if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {// Only system apps can hold shared libraries.if (pkg.libraryNames != null) {for (i = 0; i < pkg.libraryNames.size(); i++) {String name = pkg.libraryNames.get(i);if (removeSharedLibraryLPw(name, 0)) {if (DEBUG_REMOVE && chatty) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(name);}}}}}r = null;// Any package can hold static shared libraries.if (pkg.staticSharedLibName != null) {if (removeSharedLibraryLPw(pkg.staticSharedLibName, pkg.staticSharedLibVersion)) {if (DEBUG_REMOVE && chatty) {if (r == null) {r = new StringBuilder(256);} else {r.append(' ');}r.append(pkg.staticSharedLibName);}}}if (r != null) {if (DEBUG_REMOVE) Log.d(TAG, "  Libraries: " + r);}}

2.1.2 disableSystemPackageLPw

disable 掉系统应用

    private boolean disableSystemPackageLPw(PackageParser.Package oldPkg,PackageParser.Package newPkg) {// Disable the parent package (parent always replaced)	//【2.1.2.1】disable 掉父包,父包是要通过 replace 方式boolean disabled = mSettings.disableSystemPackageLPw(oldPkg.packageName, true);// Disable the child packagesfinal int childCount = (oldPkg.childPackages != null) ? oldPkg.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {PackageParser.Package childPkg = oldPkg.childPackages.get(i);final boolean replace = newPkg.hasChildPackage(childPkg.packageName);//【2.1.2.1】disable 掉子包,如果新包也有子包,那就通过 replace 方式disabled |= mSettings.disableSystemPackageLPw(childPkg.packageName, replace);}return disabled;}

这里调用了 Settings 的 disableSystemPackageLPw 方法

2.1.2.1 Settings.disableSystemPackageLPw

位置:./frameworks/base/services/core/java/com/android/server/pm/Settings.java

    boolean disableSystemPackageLPw(String name, boolean replaced) {final PackageSetting p = mPackages.get(name);if(p == null) {	//【1】异常判断,如果没安装,直接返回Log.w(PackageManagerService.TAG, "Package " + name + " is not an installed package");return false;}//【2】尝试从 mDisabledSysPackages 列表中获得 PackageSettingfinal PackageSetting dp = mDisabledSysPackages.get(name);//【3】判断 disable 的条件是否满足:// 1、之前没有 disable;2、是系统应用;3、没有更新过// always make sure the system package code and resource paths dont changeif (dp == null && p.pkg != null && p.pkg.isSystem() && !p.pkg.isUpdatedSystemApp()) {//【3.1】设置 ApplicationInfo.FLAG_UPDATED_SYSTEM_APP 标志位,表示更新过的 sys appif((p.pkg != null) && (p.pkg.applicationInfo != null)) {p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;}//【3.2】将其旧应用的 PackageSetting 添加到 mDisabledSysPackages 中mDisabledSysPackages.put(name, p);//【3.4】判断是否是 replace,显然,对于 base apk 是需要 replace 的,对于 child apk,// 需要看新安装的应用是否有相应的子包if (replaced) {// a little trick...  when we install the new package, we don't// want to modify the existing PackageSetting for the built-in// version.  so at this point we need a new PackageSetting that// is okay to muck with.PackageSetting newp = new PackageSetting(p);	// copy 一份旧数据replacePackageLPw(name, newp);		//【3.5】用 copy 后的数据替换之前的数据}return true;}return false;}

可以看到,对于系统应用来说,只有第一次覆盖更新时,会 disable 掉 sys 下的那个 app;如果多次覆盖安装,后续的不会再 disable
下面是替换的具体操作

    private void replacePackageLPw(String name, PackageSetting newp) {//【1】获得之前数据,然后根据 uid 的不同做不同的处理final PackageSetting p = mPackages.get(name);if (p != null) {if (p.sharedUser != null) {p.sharedUser.removePackage(p);p.sharedUser.addPackage(newp);} else {replaceUserIdLPw(p.appId, newp);}}//【2】更新 mPackages 中的数据mPackages.put(name, newp);}

旧的数据,此时是在 mDisabledSysPackages 中

2.1.3 createInstallArgsForExisting - 要删除的被更新 apk

这里是针对已存在的应用创建一个 InstallArgs

    /*** Create args that describe an existing installed package. Typically used* when cleaning up old installs, or used as a move source.*/private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,String resourcePath, String[] instructionSets) {// 创建了实例,描述一个已经存在的 appreturn new FileInstallArgs(codePath, resourcePath, instructionSets);}

2.1.4 clearAppDataLIF

清除 app 的数据:

    private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) {if (pkg == null) {Slog.wtf(TAG, "Package was null!", new Throwable());return;}//【1】处理父包clearAppDataLeafLIF(pkg, userId, flags);final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {//【2】调用另外一个方法处理子包clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);}clearAppProfilesLIF(pkg, UserHandle.USER_ALL);}

继续看:

    private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {final PackageSetting ps;synchronized (mPackages) {ps = mSettings.mPackages.get(pkg.packageName);}for (int realUserId : resolveUserIds(userId)) {final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;try {mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags,ceDataInode);} catch (InstallerException e) {Slog.w(TAG, String.valueOf(e));}}}

2.1.5 clearAppProfilesLIF

清除 app 的 profiles 数据:

    private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) {if (pkg == null) {Slog.wtf(TAG, "Package was null!", new Throwable());return;}mArtManagerService.clearAppProfiles(pkg);final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {mArtManagerService.clearAppProfiles(pkg.childPackages.get(i));}}

2.1.6 removeInstalledPackageLI

移除被扫描到的新的 apk 的数据:

    void removeInstalledPackageLI(PackageParser.Package pkg, boolean chatty) {if (DEBUG_INSTALL) {if (chatty)Log.d(TAG, "Removing package " + pkg.applicationInfo.packageName);}// writersynchronized (mPackages) {// Remove the parent package	//【1】移除父包mPackages.remove(pkg.applicationInfo.packageName);//【*2.1.1.1】移除父包的数据结构cleanPackageDataStructuresLILPw(pkg, chatty);// Remove the child packagesfinal int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {//【2】移除子包PackageParser.Package childPkg = pkg.childPackages.get(i);mPackages.remove(childPkg.applicationInfo.packageName);//【*2.1.1.1】移除子包的数据结构cleanPackageDataStructuresLILPw(childPkg, chatty);}}}

2.1.7 enableSystemPackageLPw

恢复系统应用

    private void enableSystemPackageLPw(PackageParser.Package pkg) {// Enable the parent package //【1】恢复父包mSettings.enableSystemPackageLPw(pkg.packageName);// Enable the child packagesfinal int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {PackageParser.Package childPkg = pkg.childPackages.get(i);//【*2.1.7.1】恢复子包mSettings.enableSystemPackageLPw(childPkg.packageName);}}

2.1.7.1 Settings.enableSystemPackageLPw

最终会调用 Settings 的 enableSystemPackageLPw 方法 enable package:

    PackageSetting enableSystemPackageLPw(String name) {//【1】判断该 sys package 是否被 disable 了PackageSetting p = mDisabledSysPackages.get(name);if(p == null) {Log.w(PackageManagerService.TAG, "Package " + name + " is not disabled");return null;}// Reset flag in ApplicationInfo object//【2】取消 ApplicationInfo.FLAG_UPDATED_SYSTEM_APP 标志位if((p.pkg != null) && (p.pkg.applicationInfo != null)) {p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;}//【3】重新为该 system 创建一个 PackageSetting 对象,并添加到相应的集合中PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,p.legacyNativeLibraryPathString, p.primaryCpuAbiString,p.secondaryCpuAbiString, p.cpuAbiOverrideString,p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,p.parentPackageName, p.childPackageNames, p.usesStaticLibraries,p.usesStaticLibrariesVersions);//【4】从 mDisabledSysPackages 中删除mDisabledSysPackages.remove(name);return ret;}

关于 addPackageLPw 的逻辑这里就不在分析了

2.2 replaceNonSystemPackageLIF

这里我们分析下覆盖安装三方应用的流程,对于扫描标志位 scanFlags,和上面保持一致:

    private void replaceNonSystemPackageLIF(PackageParser.Package deletedPackage,PackageParser.Package pkg, final @ParseFlags int parseFlags,final @ScanFlags int scanFlags, UserHandle user, int[] allUsers,String installerPackageName, PackageInstalledInfo res, int installReason) {if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="+ deletedPackage);String pkgName = deletedPackage.packageName;	// 获得包名boolean deletedPkg = true;boolean addedPkg = false;boolean updatedSettings = false;final boolean killApp = (scanFlags & SCAN_DONT_KILL_APP) == 0;//【1】设置删除 flags,会保留用户数据final int deleteFlags = PackageManager.DELETE_KEEP_DATA| (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);final long origUpdateTime = (pkg.mExtras != null)? ((PackageSetting)pkg.mExtras).lastUpdateTime : 0;// First delete the existing package while retaining the data directory//【*2.2.1】删除在 data 分区的旧 apk 数据,res.removedInfo 用于表示要移除的 apk!!// 删除失败会进入 if 分支if (!deletePackageLIF(pkgName, null, true, allUsers, deleteFlags,res.removedInfo, true, pkg)) {// If the existing package wasn't successfully deletedres.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI");deletedPkg = false;} else {// Successfully deleted the old package; proceed with replace.// If deleted package lived in a container, give users a chance to// relinquish resources before killing.//【2】删除成功后,要判断下被删除的 apk 是否安装在 External 位置或者其是否是 forward lock 的// 如果是,那么我们要发送资源变化的广播通知其他进程if (deletedPackage.isForwardLocked() || isExternal(deletedPackage)) {if (DEBUG_INSTALL) {Slog.i(TAG, "upgrading pkg " + deletedPackage + " is ASEC-hosted -> UNAVAILABLE");}final int[] uidArray = new int[] { deletedPackage.applicationInfo.uid };final ArrayList<String> pkgList = new ArrayList<String>(1);pkgList.add(deletedPackage.applicationInfo.packageName);sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);}//【*2.1.4】清楚 code cache 数据//【*2.1.5】清楚要被删除的应用的 profile 数据,这个和 odex 优化相关clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE| StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);try {//【3】扫描新的 apk 应用final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);//【4】更新系统中的数据结构updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,installReason);// Update the in-memory copy of the previous code paths.//【5】记录保存被删除的旧 apk 的 code path,包括父包和子包PackageSetting ps = mSettings.mPackages.get(pkgName);if (!killApp) {if (ps.oldCodePaths == null) {ps.oldCodePaths = new ArraySet<>();}Collections.addAll(ps.oldCodePaths, deletedPackage.baseCodePath);if (deletedPackage.splitCodePaths != null) {Collections.addAll(ps.oldCodePaths, deletedPackage.splitCodePaths);}} else {ps.oldCodePaths = null;}if (ps.childPackageNames != null) {for (int i = ps.childPackageNames.size() - 1; i >= 0; --i) {final String childPkgName = ps.childPackageNames.get(i);final PackageSetting childPs = mSettings.mPackages.get(childPkgName);childPs.oldCodePaths = ps.oldCodePaths;}}//【*7.2.2】准备 app 数据目录prepareAppDataAfterInstallLIF(newPackage);addedPkg = true;	// 表示安装应用成功mDexManager.notifyPackageUpdated(newPackage.packageName,newPackage.baseCodePath, newPackage.splitCodePaths);} catch (PackageManagerException e) {res.setError("Package couldn't be installed in " + pkg.codePath, e);}}//【6】如果前面的处理没有问题,进入 if 分支if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);// Revert all internal state mutations and added folders for the failed install//【*2.2.1】如果安装不成功,但是我们已经扫描了新的 apk 那么这里会删除其遗留的数据if (addedPkg) {deletePackageLIF(pkgName, null, true, allUsers, deleteFlags,res.removedInfo, true, null);}// Restore the old package //【2.1】如果新 apk 安装失败了,并且执行了清理旧的 apk 的数据,那么这里会 reinstall 旧 apkif (deletedPkg) {if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);File restoreFile = new File(deletedPackage.codePath);// Parse old package //【2.1.1】设置解析参数 flagsboolean oldExternal = isExternal(deletedPackage);int oldParseFlags  = mDefParseFlags | PackageParser.PARSE_CHATTY |(deletedPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0) |(oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;try {//【2.1.2】重新解析和扫描旧的 apkscanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime,null);} catch (PackageManagerException e) {Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: "+ e.getMessage());return;}synchronized (mPackages) {// Ensure the installer package name up to date	//【2.1.3】设置 installer namesetInstallerPackageNameLPw(deletedPackage, installerPackageName);// Update permissions for restored package	//【2.1.4】更新被恢复的旧 apk 的权限信息mPermissionManager.updatePermissions(deletedPackage.packageName, deletedPackage, false, mPackages.values(),mPermissionCallback);//【2.1.5】持久化内存中的应用数据mSettings.writeLPr();}Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade");}} else {//【7】新 apk 覆盖安装成功了,进入 elsesynchronized (mPackages) {PackageSetting ps = mSettings.getPackageLPr(pkg.packageName);if (ps != null) {//【7.1】更新要从那些 user 下移除res.removedInfo.removedForAllUsers = mPackages.get(ps.name) == null;if (res.removedInfo.removedChildPackages != null) {final int childCount = res.removedInfo.removedChildPackages.size();// Iterate in reverse as we may modify the collectionfor (int i = childCount - 1; i >= 0; i--) {String childPackageName = res.removedInfo.removedChildPackages.keyAt(i);if (res.addedChildPackages.containsKey(childPackageName)) {res.removedInfo.removedChildPackages.removeAt(i);} else {PackageRemovedInfo childInfo = res.removedInfo.removedChildPackages.valueAt(i);childInfo.removedForAllUsers = mPackages.get(childInfo.removedPackage) == null;}}}}}}}

2.2.1 deletePackageLIF

删除 apk,参数 PackageParser.Package replacingPackage 表示用于 replace 的 package:
deletePackageLIF 中的逻辑很多涉及到了 delete package 的逻辑,和 install package 并没有太大关系,我们在后面的 delete package 中会继续分析

    private boolean deletePackageLIF(String packageName, UserHandle user,boolean deleteCodeAndResources, int[] allUserHandles, int flags,PackageRemovedInfo outInfo, boolean writeSettings,PackageParser.Package replacingPackage) {if (packageName == null) {Slog.w(TAG, "Attempt to delete null packageName.");return false;}if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);PackageSetting ps;synchronized (mPackages) {	//【1】获得要删除的 apk 的 PackageSetting 数据ps = mSettings.mPackages.get(packageName);if (ps == null) {Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");return false;}//【2】处理子包的数据:// 如果是子包,并且(其不是系统应用,或者 deleteFlags 设置了 DELETE_SYSTEM_APP 标志)// 那么我们会清除该子包的使用状态,同时设置在设备用户下处于未安装状态if (ps.parentPackageName != null && (!isSystemApp(ps)|| (flags & PackageManager.DELETE_SYSTEM_APP) != 0)) {if (DEBUG_REMOVE) {Slog.d(TAG, "Uninstalled child package:" + packageName + " for user:"+ ((user == null) ? UserHandle.USER_ALL : user));}//【2.1】判断下要在那些 user 下删除该子包 apk 的状态信息final int removedUserId = (user != null) ? user.getIdentifier(): UserHandle.USER_ALL;//【*2.2.1.1】清除掉 userId 下的子包 apk 的使用信息if (!clearPackageStateForUserLIF(ps, removedUserId, outInfo)) {return false;}//【*2.2.1.2】更新子包 apk 在该 user 下为未安装状态markPackageUninstalledForUserLPw(ps, user);//【2.2】更新应用偏好设置scheduleWritePackageRestrictionsLocked(user);return true;}}final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS, userId)) {unsuspendForSuspendingPackage(packageName, userId);}//【3】如果只是删除在指定 user 下的安装信息,那么我们会设置其在该 user 下为未安装的状态,同时删除其数据// 如果 deleteFlags 设置了 DELETE_SYSTEM_APP 标志位,表示删除的是系统应用if (((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null&& user.getIdentifier() != UserHandle.USER_ALL)) {// The caller is asking that the package only be deleted for a single// user.  To do this, we just mark its uninstalled state and delete// its data. If this is a system app, we only allow this to happen if// they have set the special DELETE_SYSTEM_APP which requests different// semantics than normal for uninstalling system apps.//【*2.2.1.2】更新子包 apk 在该 user 下为未安装状态markPackageUninstalledForUserLPw(ps, user);if (!isSystemApp(ps)) {// Do not uninstall the APK if an app should be cached//【3.1】对于非系统应用的情况,先判断下是否需要保留不卸载boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);//【3.2】如果该 package 在其他用户下有安装;或者其需要保留不卸载// 那么我们只需要清楚在当前指定的用户 user 下的数据并设置其为未安装状态if (ps.isAnyInstalled(sUserManager.getUserIds()) || keepUninstalledPackage) {// Other user still have this package installed, so all// we need to do is clear this user's data and save that// it is uninstalled.if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");//【*2.2.1.1】清除掉当前指定的用户 user 下的 apk 的使用信息if (!clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo)) {return false;}//【3.3】更新应用偏好设置,然后返回scheduleWritePackageRestrictionsLocked(user);return true;} else {// We need to set it back to 'installed' so the uninstall// broadcasts will be sent correctly.//【3.4】这里说明其没有在其他 user 下安装,同时也不需要保留,那么后面会执行删除操作// 这里会将在当前指定的用户 user 下的安装状态设置为 installed,这样卸载广播能正确发出if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");ps.setInstalled(true, user.getIdentifier());mSettings.writeKernelMappingLPr(ps);}} else {// This is a system app, so we assume that the// other users still have this package installed, so all// we need to do is clear this user's data and save that// it is uninstalled.//【3.5】对于系统应用的情况,这里只会清楚当前指定用户下的数据,然后返回if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");//【*2.2.1.1】清除掉当前指定的用户 user 下的 apk 的使用信息if (!clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo)) {return false;}//【3.6】更新应用偏好设置,然后返回scheduleWritePackageRestrictionsLocked(user);return true;}}// If we are deleting a composite package for all users, keep track// of result for each child.//【4】这里按照逻辑,只有系统 apk 才能进入,因为只有系统 apk 才有子包!// 这里是处理每个子包的移除信息,包括子包名,子包所在 userif (ps.childPackageNames != null && outInfo != null) {synchronized (mPackages) {final int childCount = ps.childPackageNames.size();outInfo.removedChildPackages = new ArrayMap<>(childCount);for (int i = 0; i < childCount; i++) {String childPackageName = ps.childPackageNames.get(i);PackageRemovedInfo childInfo = new PackageRemovedInfo(this);childInfo.removedPackage = childPackageName;childInfo.installerPackageName = ps.installerPackageName;//【4.1】添加到父包的 removedChildPackages 集合中outInfo.removedChildPackages.put(childPackageName, childInfo);PackageSetting childPs = mSettings.getPackageLPr(childPackageName);if (childPs != null) {childInfo.origUsers = childPs.queryInstalledUsers(allUserHandles, true);}}}}boolean ret = false;if (isSystemApp(ps)) {if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);// When an updated system application is deleted we delete the existing resources// as well and fall back to existing code in system partition//【×2.2.1.3】删除系统 apk,如果系统 apk 之前被覆盖更新了,那么我们会删除新的 apk// 回滚到 system 分区的旧 apkret = deleteSystemPackageLIF(ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);} else {if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);//【×8.1.4】删除非系统的 apkret = deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,outInfo, writeSettings, replacingPackage);}// Take a note whether we deleted the package for all users//【5】记录下,我们是否从所有的用户 user 下删除了该 packageif (outInfo != null) {//【5.1】更新父包和子包的 removedForAllUsers 属性outInfo.removedForAllUsers = mPackages.get(ps.name) == null;if (outInfo.removedChildPackages != null) {synchronized (mPackages) {final int childCount = outInfo.removedChildPackages.size();for (int i = 0; i < childCount; i++) {PackageRemovedInfo childInfo = outInfo.removedChildPackages.valueAt(i);if (childInfo != null) {childInfo.removedForAllUsers = mPackages.get(childInfo.removedPackage) == null;}}}}// If we uninstalled an update to a system app there may be some// child packages that appeared as they are declared in the system// app but were not declared in the update.//【5.2】我们卸载的是一个系统 apk 的更新包,那么可能有些子包在 system 分区的旧 apk 中声明了// 但是在新的 data 更新 apk 中没有申明,这里会处理这种情况if (isSystemApp(ps)) {synchronized (mPackages) {PackageSetting updatedPs = mSettings.getPackageLPr(ps.name);final int childCount = (updatedPs.childPackageNames != null)? updatedPs.childPackageNames.size() : 0;for (int i = 0; i < childCount; i++) {String childPackageName = updatedPs.childPackageNames.get(i);if (outInfo.removedChildPackages == null|| outInfo.removedChildPackages.indexOfKey(childPackageName) < 0) {PackageSetting childPs = mSettings.getPackageLPr(childPackageName);if (childPs == null) {continue;}PackageInstalledInfo installRes = new PackageInstalledInfo();installRes.name = childPackageName;installRes.newUsers = childPs.queryInstalledUsers(allUserHandles, true);installRes.pkg = mPackages.get(childPackageName);installRes.uid = childPs.pkg.applicationInfo.uid;if (outInfo.appearedChildPackages == null) {outInfo.appearedChildPackages = new ArrayMap<>();}//【5.3】将这些子包保存到 outInfo.appearedChildPackages 中outInfo.appearedChildPackages.put(childPackageName, installRes);}}}}}return ret;}
2.2.1.1 clearPackageStateForUserLIF

清除指定 user 下的应用数据,这里涉及到的清理操作很多,由于篇幅,这里先不细讲,等到卸载应用时在深入分析

    private boolean clearPackageStateForUserLIF(PackageSetting ps, int userId,PackageRemovedInfo outInfo) {final PackageParser.Package pkg;synchronized (mPackages) {pkg = mPackages.get(ps.name);	//【1】获得旧 apk 的扫描信息}//【2】判断要删除那些 user 下的状态数据final int[] userIds = (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds(): new int[] {userId};for (int nextUserId : userIds) {if (DEBUG_REMOVE) {Slog.d(TAG, "Updating package:" + ps.name + " install state for user:"+ nextUserId);}//【×2.2.1.1.1】删除掉旧 apk 的使用状态本地记录文件destroyAppDataLIF(pkg, userId,StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);//【×2.2.1.1.2】删除 profile 相关信息destroyAppProfilesLIF(pkg, userId);clearDefaultBrowserIfNeededForUser(ps.name, userId);//【4】移除 key storeremoveKeystoreDataIfNeeded(nextUserId, ps.appId);//【×2.2.1.1.3】执行 package clean 操作schedulePackageCleaning(ps.name, nextUserId, false);synchronized (mPackages) {//【5】清楚默认应用设置if (clearPackagePreferredActivitiesLPw(ps.name, nextUserId)) {//【5.1】更新偏好设置scheduleWritePackageRestrictionsLocked(nextUserId);}//【6】更新运行时权限信息resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, nextUserId);}}if (outInfo != null) {outInfo.removedPackage = ps.name;outInfo.installerPackageName = ps.installerPackageName;outInfo.isStaticSharedLib = pkg != null && pkg.staticSharedLibName != null;outInfo.removedAppId = ps.appId;outInfo.removedUsers = userIds;outInfo.broadcastUsers = userIds;}return true;}

关于以下的内容,会在卸载 app 和权限相关文章中分析,这里由于篇幅原因(markdown 只支持 6 级标题),就先不深入分析了

//【3】删除 profile 相关信息!!
destroyAppProfilesLIF(pkg, userId);
//【4】移除 key store!!
removeKeystoreDataIfNeeded(nextUserId, ps.appId);
//【5】清楚默认应用设置
clearPackagePreferredActivitiesLPw();
//【6】更新运行时权限信息!
resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, nextUserId);

clearPackageStateForUserLIF >> destroyAppDataLIF

    private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) {if (pkg == null) {Slog.wtf(TAG, "Package was null!", new Throwable());return;}//【1】删除父包的数据destroyAppDataLeafLIF(pkg, userId, flags);final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {//【2】删除子包的数据destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);}}

继续看:
destroyAppDataLIF >> destroyAppDataLeafLIF

    private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {final PackageSetting ps;synchronized (mPackages) {//【1】获得该应用的安装信息 PackageSettingps = mSettings.mPackages.get(pkg.packageName);}for (int realUserId : resolveUserIds(userId)) {//【2】获得要删除的状态信息目录:final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;try {//【3】调用了 Installd 删除指定目录mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags,ceDataInode);} catch (InstallerException e) {Slog.w(TAG, String.valueOf(e));}mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId);}}

这里使用了 PackageSetting.getCeDataInode 方法:

    long getCeDataInode(int userId) {return readUserState(userId).ceDataInode;}

该方法返回的是 PackageUserState.ceDataInode 的值。
clearPackageStateForUserLIF >> schedulePackageCleaning

    void schedulePackageCleaning(String packageName, int userId, boolean andCode) {//这里会发送一个 START_CLEANING_PACKAGE 的消息给 PackageHandlerfinal Message msg = mHandler.obtainMessage(START_CLEANING_PACKAGE,userId, andCode ? 1 : 0, packageName);if (mSystemReady) {msg.sendToTarget();} else {if (mPostSystemReadyMessages == null) {mPostSystemReadyMessages = new ArrayList<>();}mPostSystemReadyMessages.add(msg);}}

schedulePackageCleaning >> START_CLEANING_PACKAGE处理

                case START_CLEANING_PACKAGE: {Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);final String packageName = (String)msg.obj;final int userId = msg.arg1;final boolean andCode = msg.arg2 != 0;synchronized (mPackages) {if (userId == UserHandle.USER_ALL) {int[] users = sUserManager.getUserIds();for (int user : users) {//【1】将 package 加入到 Settings 内部的 mPackagesToBeCleaned 集合中mSettings.addPackageToCleanLPw(new PackageCleanItem(user, packageName, andCode));}} else {mSettings.addPackageToCleanLPw(new PackageCleanItem(userId, packageName, andCode));}}Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);startCleaningPackages();} break;

START_CLEANING_PACKAGE处理 >> startCleaningPackages

    void startCleaningPackages() {// readerif (!isExternalMediaAvailable()) {return;}synchronized (mPackages) {if (mSettings.mPackagesToBeCleaned.isEmpty()) {return;}}//【1】发送 action PackageManager.ACTION_CLEAN_EXTERNAL_STORAGEIntent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE);//【2】目标组件服务:DefaultContainerServiceintent.setComponent(DEFAULT_CONTAINER_COMPONENT);IActivityManager am = ActivityManager.getService();if (am != null) {int dcsUid = -1;synchronized (mPackages) {if (!mDefaultContainerWhitelisted) {mDefaultContainerWhitelisted = true;PackageSetting ps = mSettings.mPackages.get(DEFAULT_CONTAINER_PACKAGE);dcsUid = UserHandle.getUid(UserHandle.USER_SYSTEM, ps.appId);}}try {if (dcsUid > 0) {am.backgroundWhitelistUid(dcsUid);}//【2.1】启动服务am.startService(null, intent, null, false, mContext.getOpPackageName(),UserHandle.USER_SYSTEM);} catch (RemoteException e) {}}}

进入 DefaultContainerService.onHandleIntent 方法:

@Override
protected void onHandleIntent(Intent intent) {if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) {final IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));PackageCleanItem item = null;try {while ((item = pm.nextPackageToClean(item)) != null) {final UserEnvironment userEnv = new UserEnvironment(item.userId);eraseFiles(userEnv.buildExternalStorageAppDataDirs(item.packageName));eraseFiles(userEnv.buildExternalStorageAppMediaDirs(item.packageName));if (item.andCode) {eraseFiles(userEnv.buildExternalStorageAppObbDirs(item.packageName));}}} catch (RemoteException e) {}}
}
2.2.1.2 markPackageUninstalledForUserLPw
    private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user) {final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL)? sUserManager.getUserIds() : new int[] {user.getIdentifier()};for (int nextUserId : userIds) {if (DEBUG_REMOVE) {Slog.d(TAG, "Marking package:" + ps.name + " uninstalled for user:" + nextUserId);}//【1】调用了 PackageSettings 的 setUserState 接口ps.setUserState(nextUserId, 0, COMPONENT_ENABLED_STATE_DEFAULT,false /*installed*/,true /*stopped*/,true /*notLaunched*/,false /*hidden*/,false /*suspended*/,null /*suspendingPackage*/,null /*dialogMessage*/,null /*suspendedAppExtras*/,null /*suspendedLauncherExtras*/,false /*instantApp*/,false /*virtualPreload*/,null /*lastDisableAppCaller*/,null /*enabledComponents*/,null /*disabledComponents*/,ps.readUserState(nextUserId).domainVerificationStatus,0, PackageManager.INSTALL_REASON_UNKNOWN,null /*harmfulAppWarning*/);}mSettings.writeKernelMappingLPr(ps);}
2.2.1.3 deleteSystemPackageLIF

在replaceNonSystemPackageLIF设置了关于 flags 的设置:

        //【1】设置删除 flags,会保留用户数据final int deleteFlags = PackageManager.DELETE_KEEP_DATA| (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);

删除 system 更新 apk,PackageRemovedInfo outInfo 用于封装移除的相关信息

    private boolean deleteSystemPackageLIF(PackageParser.Package deletedPkg,PackageSetting deletedPs, int[] allUserHandles, int flags, PackageRemovedInfo outInfo,boolean writeSettings) {if (deletedPs.parentPackageName != null) {Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName);return false;}final boolean applyUserRestrictions= (allUserHandles != null) && (outInfo.origUsers != null);final PackageSetting disabledPs;// Confirm if the system package has been updated// An updated system app can be deleted. This will also have to restore// the system pkg from system partition// readersynchronized (mPackages) {//【1】尝试获得被更新的 system apk 数据disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPs.name);}if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName+ " disabledPs=" + disabledPs);//【2】如果没有更新,那就不能删除,对于系统 apk,我们只能删除覆盖更新在 data 分区的if (disabledPs == null) {Slog.w(TAG, "Attempt to delete unknown system package "+ deletedPkg.packageName);return false;} else if (DEBUG_REMOVE) {Slog.d(TAG, "Deleting system pkg from data partition");}if (DEBUG_REMOVE) {if (applyUserRestrictions) {Slog.d(TAG, "Remembering install states:");for (int userId : allUserHandles) {final boolean finstalled = ArrayUtils.contains(outInfo.origUsers, userId);Slog.d(TAG, "   u=" + userId + " inst=" + finstalled);}}}// Delete the updated package//【3】设置父包和子包的 isRemovedPackageSystemUpdate 为 trueoutInfo.isRemovedPackageSystemUpdate = true;if (outInfo.removedChildPackages != null) {final int childCount = (deletedPs.childPackageNames != null)? deletedPs.childPackageNames.size() : 0;for (int i = 0; i < childCount; i++) {String childPackageName = deletedPs.childPackageNames.get(i);if (disabledPs.childPackageNames != null && disabledPs.childPackageNames.contains(childPackageName)) {PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(childPackageName);if (childInfo != null) {childInfo.isRemovedPackageSystemUpdate = true;}}}}//【4】判读 version code,如果出现降级,那么就不能保留数据if (disabledPs.versionCode < deletedPs.versionCode) {// Delete data for downgradesflags &= ~PackageManager.DELETE_KEEP_DATA;} else {// Preserve data by setting flagflags |= PackageManager.DELETE_KEEP_DATA;}//【×2.2.1.4】执行删除 data 分区新 apk,删除失败就结束操作boolean ret = deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,outInfo, writeSettings, disabledPs.pkg);if (!ret) {return false;}// writer	//【5】恢复 system 分区的旧 apk 的数据,同时删除更新的 apk 的 native libssynchronized (mPackages) {// NOTE: The system package always needs to be enabled; even if it's for// a compressed stub. If we don't, installing the system package fails// during scan [scanning checks the disabled packages]. We will reverse// this later, after we've "installed" the stub.// Reinstate the old system packageenableSystemPackageLPw(disabledPs.pkg);// Remove any native libraries from the upgraded package.removeNativeBinariesLI(deletedPs);}// Install the system package	//【6】设置扫描 flagsif (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);try {installPackageFromSystemLIF(disabledPs.codePathString, false, allUserHandles,outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings);} catch (PackageManagerException e) {Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "+ e.getMessage());return false;} finally {if (disabledPs.pkg.isStub) {mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/);}}return true;}

deleteSystemPackageLIF >> installPackageFromSystemLIF

    /*** Installs a package that's already on the system partition.*/private PackageParser.Package installPackageFromSystemLIF(@NonNull String codePathString,boolean isPrivileged, @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,@Nullable PermissionsState origPermissionState, boolean writeSettings)throws PackageManagerException {@ParseFlags int parseFlags =mDefParseFlags| PackageParser.PARSE_MUST_BE_APK| PackageParser.PARSE_IS_SYSTEM_DIR;@ScanFlags int scanFlags = SCAN_AS_SYSTEM;if (isPrivileged || locationIsPrivileged(codePathString)) {scanFlags |= SCAN_AS_PRIVILEGED;}if (locationIsOem(codePathString)) {scanFlags |= SCAN_AS_OEM;}if (locationIsVendor(codePathString)) {scanFlags |= SCAN_AS_VENDOR;}if (locationIsProduct(codePathString)) {scanFlags |= SCAN_AS_PRODUCT;}//【7】扫描旧的 system apkfinal File codePath = new File(codePathString);final PackageParser.Package pkg =scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);try {// update shared libraries for the newly re-installed system package//【8】更新 shared libs 给重新安装的 system apkupdateSharedLibrariesLPr(pkg, null);} catch (PackageManagerException e) {Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());}//【*7.2.2】准备 app 数据目录prepareAppDataAfterInstallLIF(pkg);// writer	//【9】更新 pms 数据synchronized (mPackages) {PackageSetting ps = mSettings.mPackages.get(pkg.packageName);// Propagate the permissions state as we do not want to drop on the floor// runtime permissions. The update permissions method below will take// care of removing obsolete permissions and grant install permissions.if (origPermissionState != null) {//【9.1】从被删除的 data 分区 apk 那里继承最新的权限信息ps.getPermissionsState().copyFrom(origPermissionState);}//【9.2】更新系统中所有应用的权限信息mPermissionManager.updatePermissions(pkg.packageName, pkg, true, mPackages.values(),mPermissionCallback);final boolean applyUserRestrictions= (allUserHandles != null) && (origUserHandles != null);if (applyUserRestrictions) {boolean installedStateChanged = false;if (DEBUG_REMOVE) {Slog.d(TAG, "Propagating install state across reinstall");}for (int userId : allUserHandles) {final boolean installed = ArrayUtils.contains(origUserHandles, userId);if (DEBUG_REMOVE) {Slog.d(TAG, "    user " + userId + " => " + installed);}if (installed != ps.getInstalled(userId)) {installedStateChanged = true;}ps.setInstalled(installed, userId);//【9.3】持久化运行时权限mSettings.writeRuntimePermissionsForUserLPr(userId, false);}// Regardless of writeSettings we need to ensure that this restriction// state propagation is persisted	//【9.4】持久化应用偏好设置mSettings.writeAllUsersPackageRestrictionsLPr();if (installedStateChanged) {mSettings.writeKernelMappingLPr(ps);}}// can downgrade to reader here	//【9.5】持久化最新的所有应用数据if (writeSettings) {mSettings.writeLPr();}}return pkg;}
2.2.1.4 deleteInstalledPackageLIF

如果不是系统app就用这个删除函数,他的falgs和2.2.1.3里的一样的

    private boolean deleteInstalledPackageLIF(PackageSetting ps,boolean deleteCodeAndResources, int flags, int[] allUserHandles,PackageRemovedInfo outInfo, boolean writeSettings,PackageParser.Package replacingPackage) {synchronized (mPackages) {if (outInfo != null) {outInfo.uid = ps.appId;}if (outInfo != null && outInfo.removedChildPackages != null) {final int childCount = (ps.childPackageNames != null)? ps.childPackageNames.size() : 0;for (int i = 0; i < childCount; i++) {String childPackageName = ps.childPackageNames.get(i);PackageSetting childPs = mSettings.mPackages.get(childPackageName);if (childPs == null) {return false;}PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(childPackageName);if (childInfo != null) {childInfo.uid = childPs.appId;}}}}// Delete package data from internal structures and also remove data if flag is set//【×2.2.1.4.1】移除 package 的数据removePackageDataLIF(ps, allUserHandles, outInfo, flags, writeSettings);// Delete the child packages data //【1】如果有子包,也会移除子包的数据final int childCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;for (int i = 0; i < childCount; i++) {PackageSetting childPs;synchronized (mPackages) {childPs = mSettings.getPackageLPr(ps.childPackageNames.get(i));}if (childPs != null) {PackageRemovedInfo childOutInfo = (outInfo != null&& outInfo.removedChildPackages != null)? outInfo.removedChildPackages.get(childPs.name) : null;final int deleteFlags = (flags & DELETE_KEEP_DATA) != 0&& (replacingPackage != null&& !replacingPackage.hasChildPackage(childPs.name))? flags & ~DELETE_KEEP_DATA : flags;//【×2.2.1.4.1】移除 package 的数据removePackageDataLIF(childPs, allUserHandles, childOutInfo,deleteFlags, writeSettings);}}// Delete application code and resources only for parent packages//【2】对于被删除的父包,会创建一个 InstallArgs,用于删除 apk 和资源if (ps.parentPackageName == null) {if (deleteCodeAndResources && (outInfo != null)) {//【×6.2.1.3】根据一个存在的 package 创建一个 InstallArgs 中outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);}}return true;}

deleteInstalledPackageLIF >> removePackageDataLIF

    private void removePackageDataLIF(PackageSetting ps, int[] allUserHandles,PackageRemovedInfo outInfo, int flags, boolean writeSettings) {String packageName = ps.name;if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + ps);// Retrieve object to delete permissions for shared user later onfinal PackageParser.Package deletedPkg;final PackageSetting deletedPs;// readersynchronized (mPackages) {//【1】获得要被删除的 apk 的 PackageSetting 和 PackageParser.Package 对象deletedPkg = mPackages.get(packageName);deletedPs = mSettings.mPackages.get(packageName);if (outInfo != null) {outInfo.removedPackage = packageName;outInfo.installerPackageName = ps.installerPackageName;outInfo.isStaticSharedLib = deletedPkg != null&& deletedPkg.staticSharedLibName != null;outInfo.populateUsers(deletedPs == null ? null: deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true), deletedPs);}}//【×2.2.1.4.1】第一部移除,扫描和四大组件信息removePackageLI(ps, (flags & PackageManager.DELETE_CHATTY) != 0);//【2】如果 flags 没有设置 DELETE_KEEP_DATA,那么会清楚 apk 的数据,显然这里由于设置了,那么就不会清楚if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {final PackageParser.Package resolvedPkg;if (deletedPkg != null) {resolvedPkg = deletedPkg;} else {// We don't have a parsed package when it lives on an ejected// adopted storage device, so fake something togetherresolvedPkg = new PackageParser.Package(ps.name);resolvedPkg.setVolumeUuid(ps.volumeUuid);}//【×2.2.1.1.1】删除 apk 的 data 数据destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL,StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);//【×2.2.1.1.2】删除 apk 的 profiles 数据destroyAppProfilesLIF(resolvedPkg, UserHandle.USER_ALL);if (outInfo != null) {outInfo.dataRemoved = true;}//【×2.2.1.1.3】执行 package 清除schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);}int removedAppId = -1;// writer //【3】进一步处理synchronized (mPackages) {boolean installedStateChanged = false;if (deletedPs != null) {//【3.1】如果 flags 没有设置 DELETE_KEEP_DATA 标志位,那么执行其他的清楚操作if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {//【3.1.1】清楚 intentfilter verify 和 默认浏览器的设置数据clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);clearDefaultBrowserIfNeeded(packageName);mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);removedAppId = mSettings.removePackageLPw(packageName);if (outInfo != null) {outInfo.removedAppId = removedAppId;}//【3.1.2】更新权限信息mPermissionManager.updatePermissions(deletedPs.name, null, false, mPackages.values(), mPermissionCallback);if (deletedPs.sharedUser != null) {	//【3.1.3】如果该应用是共享 shared user 的,进入这里// Remove permissions associated with package. Since runtime// permissions are per user we have to kill the removed package// or packages running under the shared user of the removed// package if revoking the permissions requested only by the removed// package is successful and this causes a change in gids.for (int userId : UserManagerService.getInstance().getUserIds()) {//【3.1.3.1】更新该共享 shared uid 的权限,该 package 被移除掉,会导致和该// 应用相关连的权限的变化,从而导致共享 shared uid 的 gids 发生变化final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,userId);if (userIdToKill == UserHandle.USER_ALL|| userIdToKill >= UserHandle.USER_SYSTEM) {// If gids changed for this user, kill all affected packages.//【3.1.3.1】如果共享 shared uid 的 gids 发生变化,杀掉该 uid 下的// 所有的 app 进程mHandler.post(new Runnable() {@Overridepublic void run() {// This has to happen with no lock held.killApplication(deletedPs.name, deletedPs.appId,KILL_APP_REASON_GIDS_CHANGED);}});break;}}}//【3.1.4】清除默认应用的数据clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);}// make sure to preserve per-user disabled state if this removal was just// a downgrade of a system app to the factory package//【3.4】更新下在每个 user 下的安装状态if (allUserHandles != null && outInfo != null && outInfo.origUsers != null) {if (DEBUG_REMOVE) {Slog.d(TAG, "Propagating install state across downgrade");}for (int userId : allUserHandles) {final boolean installed = ArrayUtils.contains(outInfo.origUsers, userId);if (DEBUG_REMOVE) {Slog.d(TAG, "    user " + userId + " => " + installed);}if (installed != ps.getInstalled(userId)) {installedStateChanged = true;}ps.setInstalled(installed, userId);}}}// can downgrade to reader //【3.5】持久化 Settingsif (writeSettings) {// Save settings nowmSettings.writeLPr();}if (installedStateChanged) {mSettings.writeKernelMappingLPr(ps);}}if (removedAppId != -1) {// A user ID was deleted here. Go through all users and remove it// from KeyStore.	//【4】移除 key-storeremoveKeystoreDataIfNeeded(UserHandle.USER_ALL, removedAppId);}}

removePackageDataLIF >> removePackageLI

    void removePackageLI(PackageSetting ps, boolean chatty) {if (DEBUG_INSTALL) {if (chatty)Log.d(TAG, "Removing package " + ps.name);}// writersynchronized (mPackages) {mPackages.remove(ps.name);	//【1】移除扫描信息final PackageParser.Package pkg = ps.pkg;if (pkg != null) {//【×6.2.1.6.1】移除四大组件,共享库解析对象cleanPackageDataStructuresLILPw(pkg, chatty);}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/240085.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

背包小专题

背包小专题 1. CF106C Buns题目描述题目概况思路点拨代码实现 2. CF864E Fire题目描述题目概况思路点拨代码实现 3. CF366C Dima and Salad题目描述题目概况思路点拨背包瓶颈解决方法 代码实现 4. CF1132E Knapsack题目描述题目概况思路点拨代码实现 5. CF632E Thief in a Shop…

Leetcode 435 无重叠区间

题意理解&#xff1a; 给定一个区间的集合 intervals 要求需要移除区间&#xff0c;使剩余区间互不重叠 目标&#xff1a;最少需要移除几个区间。 解题思路&#xff1a; 采用贪心思路解题&#xff0c;什么是全局最优解&#xff0c;什么是局部最优解。 全局最优解&#xff0c;删…

使用Java语言判断一个年度是不是闰年

一、 代码说明 引入Scanner函数&#xff0c;将类命名为Judge类&#xff0c;使用try语句和catch语句将整体代码包围起来&#xff0c;使用if语句来判断是否为闰年&#xff0c;输入年份&#xff0c;然后得到相应的结论。 二、代码 import java.util.Scanner; public class Judg…

叮咚,微信年度聊天报告(圣诞节版)请查收~丨GitHub star 16.8k+

微信年度聊天报告——圣诞节特别版&#xff0c;快发给心仪的ta吧~ 开源地址 GitHub开源地址&#xff1a;https://github.com/LC044/WeChatMsg 我深信有意义的不是微信&#xff0c;而是隐藏在对话框背后的一个个深刻故事。未来&#xff0c;每个人都能拥有AI的陪伴&#xff0c;…

Microsoft Store 里有哪些好用的软件?

Windows 应用商店还是有不少干货软件的。 下面给大家推荐 12 款 Windows 应用商店里优秀实用的 UWP 应用软件&#xff0c;无广告、不流氓、体验好&#xff0c;强烈建议收藏&#xff01; 而且经过商店审核和限制&#xff0c;也更加安全、干净&#xff0c;不用担心有乱七八糟的…

内存管理学习

内存管理 在计算系统中&#xff0c;通常存储空间分为两种&#xff1a;内部存储空间和外部存储空间。 内部存储空间通常访问速度比较快&#xff0c;能够按照变量地址随机访问&#xff0c;也就是我们通常所说的RAM&#xff08;随机存储器&#xff09;&#xff0c;可以把它理解为…

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题

Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时&#xff0c;动态变化时无法及时刷新更新适配界面的问题 目录 Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时&#xff0c;动态变化时无法及时刷新更新适配界面的问题 一、简单介绍…

Yolov5水果分类识别+pyqt交互式界面

Yolov5 Fruits Detector Yolov5 是一种先进的目标检测算法&#xff0c;可以应用于水果分类识别任务。结合 PyQT 框架&#xff0c;可以创建一个交互式界面&#xff0c;使用户能够方便地上传图片并获取水果分类结果。以下将详细阐述 Yolov5 水果分类识别和 PyQT 交互式界面的实现…

基于ssm智能社区管理系统的设计与实现+vue论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本智能社区管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息…

unity脚本API中OnCollisionEnter()、OnTriggerEnter()二者的区别

Unity中的OnCollisionEnter和OnTriggerEnter两个函数在日常的开发中很常见但也容易混淆&#xff0c;下面说一说两者的区别。 碰撞器&#xff08;Collider&#xff09;与触发器&#xff08;Trigger&#xff09;的概念 碰撞器&#xff08;Collider&#xff09;和触发器&#xff…

nodejs+vue+ElementUi医院预约挂号系统3e3g0

本医院预约挂号系统有管理员&#xff0c;医生和用户。该系统将采用B/S结构模式&#xff0c;使用Vue和ElementUI框架搭建前端页面&#xff0c;后端使用Nodejs来搭建服务器&#xff0c;并使用MySQL&#xff0c;通过axios完成前后端的交互 管理员功能有个人中心&#xff0c;用户管…

JAVA程序流程控制

程序的流程控制一般分为3种&#xff1a;顺序结构、分支结构、循环结构 顺序结构&#xff1a;就是不加任何控制&#xff0c;代码从main方法开始自上而下执行 分支结构&#xff1a;就是根据条件判断是true还是false&#xff0c;有选择性的执行哪些代码。在Java语言中提供了两个格…

人工智能_机器学习071_SVM支持向量机_人脸识别算法_LFW人脸数据加载_与理解---人工智能工作笔记0111

然后我们继续来看 这里有个lfw_home可以看到这个数据是,包含了人脸数据 然后我们继续看,在我们的顶你用户目录下,如果安装了,sklearn就会有这样一个目录, scikit_learn_data目录,这个里面可以看到 可以看到这个文件夹中有个 lfw_home文件夹是对.zip文件夹的解压,这个下载以后…

0.618算法和基于Armijo准则的线搜索回退法

0.618代码如下&#xff1a; import math # 定义函数h(t) t^3 - 2t 1 def h(t): return t**3 - 2*t 1 # 0.618算法 def golden_section_search(a, b, epsilon): ratio 0.618 while (b - a) > epsilon: x1 b - ratio * (b - a) x2 a ratio * (b - a) h_…

mybatis-plus阻止全表更新与删除

BlockAttackInnerInterceptor 是mybatis-plus的一个内置拦截器&#xff0c;用于防止恶意的全表更新或删除操作。当你添加了这个拦截器后&#xff0c;它会检查即将执行的 sql语句&#xff0c;如果有尝试进行全表更新或删除的语句&#xff0c;该拦截器会阻止这些操作。 <!-- m…

似然函数的定义:

似然函数&#xff08;Likelihood function&#xff09;是一个统计学中的概念&#xff0c;用于在给定某些数据的条件下&#xff0c;评估不同参数下模型生成这些数据的概率。在概率论和统计学中&#xff0c;似然函数是固定数据并视参数为变量的函数&#xff0c;而概率函数则是固定…

L1-058:6翻了

“666”是一种网络用语&#xff0c;大概是表示某人很厉害、我们很佩服的意思。最近又衍生出另一个数字“9”&#xff0c;意思是“6翻了”&#xff0c;实在太厉害的意思。如果你以为这就是厉害的最高境界&#xff0c;那就错啦 —— 目前的最高境界是数字“27”&#xff0c;因为这…

微信小程序 文件下载、打开、转发

一.下载文件 wx.downloadFile({url: https://img.haihaina.cn/月度支出表.xls, filePath: wx.env.USER_DATA_PATH / 自定义名字.xlsx,success(res) {console.log(downloadFile,res)const filePath res.tempFilePathwx.openDocument({filePath: filePath,fileType: xlsx,succe…

算法-两数之和

题目描述&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。…

openGauss学习笔记-168 openGauss 数据库运维-备份与恢复-导入数据-使用gs_restore命令导入数据

文章目录 openGauss学习笔记-168 openGauss 数据库运维-备份与恢复-导入数据-使用gs_restore命令导入数据168.1 操作场景168.2 操作步骤168.3 示例 openGauss学习笔记-168 openGauss 数据库运维-备份与恢复-导入数据-使用gs_restore命令导入数据 168.1 操作场景 gs_restore是…