Android 安装应用-浏览阶段

embedded/2024/10/19 3:27:09/

  应用安装的浏览阶段主要是由PackageManagerService类中的scanPackageNewLI()实现的,看一下它的代码:

java">    // TODO: scanPackageNewLI() and scanPackageOnly() should be merged. But, first, commiting// the results / removing app data needs to be moved up a level to the callers of this// method. Also, we need to solve the problem of potentially creating a new shared user// setting. That can probably be done later and patch things up after the fact.@GuardedBy({"mInstallLock", "mLock"})private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,@Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {final String renamedPkgName = mSettings.getRenamedPackageLPr(parsedPackage.getRealPackage());final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);if (realPkgName != null) {ensurePackageRenamed(parsedPackage, renamedPkgName);}final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,renamedPkgName);final PackageSetting pkgSetting = mSettings.getPackageLPr(parsedPackage.getPackageName());final PackageSetting disabledPkgSetting =mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());if (mTransferredPackages.contains(parsedPackage.getPackageName())) {Slog.w(TAG, "Package " + parsedPackage.getPackageName()+ " was transferred to another, but its .apk remains");}scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);synchronized (mLock) {boolean isUpdatedSystemApp;if (pkgSetting != null) {isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();} else {isUpdatedSystemApp = disabledPkgSetting != null;}applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, isUpdatedSystemApp);assertPackageIsValid(parsedPackage, parseFlags, scanFlags);SharedUserSetting sharedUserSetting = null;if (parsedPackage.getSharedUserId() != null) {// SIDE EFFECTS; may potentially allocate a new shared usersharedUserSetting = mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);if (DEBUG_PACKAGE_SCANNING) {if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()+ " (uid=" + sharedUserSetting.userId + "):"+ " packages=" + sharedUserSetting.packages);}}}String platformPackageName = mPlatformPackage == null? null : mPlatformPackage.getPackageName();final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,originalPkgSetting, realPkgName, parseFlags, scanFlags,Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,cpuAbiOverride);return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime);}}

  参数parsedPackage是解析安装文件得到的解析包对象。parsedPackage.getRealPackage()是在解析安装文件时,配置文件存在application同级别original-package标签 的属性 "name"的值,并且它的值和包名不一致时,会设置该值。在这里是找是否存在更改包名的情况,包名的更改关系维护在mSettings中的mRenamedPackages中,所以如果存在包名更改,renamedPkgName就是改名之前的包名。
  getRealPackageName(parsedPackage, renamedPkgName)是来判断改名之前的名字,是否存在parsedPackage对象的OriginalPackages中。如果存在,就返回parsedPackage.getRealPackage()。
  如果realPkgName != null不为null,说明存在改名字的情况,所以调用ensurePackageRenamed(parsedPackage, renamedPkgName)来将解析包的包名改为之前的包名。
  getOriginalPackageLocked(parsedPackage, renamedPkgName)是得到它原来的PackageSetting对象,但是像上面改名之前的包名在parsedPackage对象的OriginalPackages中的,则会返回null。因为它的名字已经是现在解析包的包名了。如果renamedPkgName不在parsedPackage对象的OriginalPackages中,并且parsedPackage对象的OriginalPackages中包名存在PackageSetting对象,但是看它的条件得旧包是系统包,并且在mPackages中不存在对应的值的情况下,才能返回它的PackageSetting对象。所以在这里我们也知道,包名修改是为了系统应用的功能,普通APP没法使用。在这里originalPkgSetting大部分情况下,为null。
  pkgSetting则是维护在mSettings中根据包名得到的PackageSetting对象。disabledPkgSetting是禁止的系统包的PackageSetting对象。
  mTransferredPackages中保存的是改过应用的包名。
  接下来就是调用adjustScanFlags()方法调整浏览标识。
  接着通过判断pkgSetting里面的状态和disabledPkgSetting != null来设置变量isUpdatedSystemApp的值。
  applyPolicy()方法,是用来根据标识来设置解析包对象的一些状态。
  assertPackageIsValid(parsedPackage, parseFlags, scanFlags)是进行一些判断,如果有些状态不符合要求,会报PackageManagerException异常。
  sharedUserSetting是解析包中配置的“android:sharedUserId”标签的共享用户的SharedUserSetting对象。
  platformPackageName是平台包名,它是由mPlatformPackage得出的值。mPlatformPackage是应用包名为"android"的安装包对象,如果存在,它不为null;如果不存在,它为null。
  下面会将上面说的变量封装到ScanRequest对象request中去。最后就是调用scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime),并将它的结果返回。下面看一下它的代码:

scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime)的实现

分段一

  它的代码也很长,所以分段阅读,第一段:

java">    @GuardedBy("mInstallLock")@VisibleForTesting@NonNullstatic ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,Injector injector,boolean isUnderFactoryTest, long currentTime)throws PackageManagerException {final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();final UserManagerInternal userManager = injector.getUserManagerInternal();ParsedPackage parsedPackage = request.parsedPackage;PackageSetting pkgSetting = request.pkgSetting;final PackageSetting disabledPkgSetting = request.disabledPkgSetting;final PackageSetting originalPkgSetting = request.originalPkgSetting;final @ParseFlags int parseFlags = request.parseFlags;final @ScanFlags int scanFlags = request.scanFlags;final String realPkgName = request.realPkgName;final SharedUserSetting sharedUserSetting = request.sharedUserSetting;final UserHandle user = request.user;final boolean isPlatformPackage = request.isPlatformPackage;List<String> changedAbiCodePath = null;if (DEBUG_PACKAGE_SCANNING) {if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());}}// Initialize package source and resource directoriesfinal File destCodeFile = new File(parsedPackage.getPath());// We keep references to the derived CPU Abis from settings in oder to reuse// them in the case where we're not upgrading or booting for the first time.String primaryCpuAbiFromSettings = null;String secondaryCpuAbiFromSettings = null;boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;if (!needToDeriveAbi) {if (pkgSetting != null) {// TODO(b/154610922): if it is not first boot or upgrade, we should directly use// API info from existing package setting. However, stub packages currently do not// preserve ABI info, thus the special condition check here. Remove the special// check after we fix the stub generation.if (pkgSetting.pkg != null && pkgSetting.pkg.isStub()) {needToDeriveAbi = true;} else {primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString;}} else {// Re-scanning a system package after uninstalling updates; need to derive ABIneedToDeriveAbi = true;}}

  开始是给局部变量赋值。packageAbiHelper实际是PackageAbiHelperImpl类型。parsedPackage是解析的包实例,pkgSetting是旧包的PackageSetting对象。disabledPkgSetting是禁止的系统包的PackageSetting对象(包名相同)、originalPkgSetting是更改包名之前的原来包名对应的PackageSetting对象、realPkgName是如果存在更改包名现在的包名、user目前用户,像例子中也就是System用户(userId为0)。
  parsedPackage.getPath()是解析包的路径。解析Apk文件时,分为解析的是Apk还是目录。如果解析的直接是Apk,则parsedPackage.getPath()为Apk的完整路径名;如果是目录,则parsedPackage.getPath()为Apk文件的目录。
  primaryCpuAbiFromSettings是主要的cpu架构的abi,secondaryCpuAbiFromSettings是次要的cpu架构的abi。在升级系统或第一次启动的情况下,abi是需要从解析包里面取的。所以scanFlags 里面有SCAN_FIRST_BOOT_OR_UPGRADE标识时,会将变量needToDeriveAbi 设置为true,代表abi需要从解析包parsedPackage取得。
  所以needToDeriveAbi为false情况,如果pkgSetting 不为null,则直接从pkgSetting 取得abi。但是如果包设置pkgSetting对应的解析包pkg.isStub()的情况,那stub包是没有保留ABI信息的,所以也需要从当前解析的包信息里面取得。如果pkgSetting 为null,那也只能从解析包信息里面取得ABI。

分段二

  继续看scanPackageOnlyLI()的第二段代码:

java">        if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {PackageManagerService.reportSettingsProblem(Log.WARN,"Package " + parsedPackage.getPackageName() + " shared user changed from "+ (pkgSetting.sharedUser != null? pkgSetting.sharedUser.name : "<nothing>")+ " to "+ (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")+ "; replacing with new");pkgSetting = null;}String[] usesStaticLibraries = null;if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);}final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();// TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans//  to avoid adding something that's unsupported due to lack of state, since it's called//  with null.final boolean createNewPackage = (pkgSetting == null);if (createNewPackage) {final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;// Flags contain system values stored in the server variant of AndroidPackage,// and so the server-side PackageInfoUtils is still called, even without a// PackageSetting to pass in.int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);// REMOVE SharedUserSetting from method; update in a separate callpkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,destCodeFile, parsedPackage.getNativeLibraryRootDir(),AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user,true /*allowInstall*/, instantApp, virtualPreload,UserManagerService.getInstance(), usesStaticLibraries,parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),newDomainSetId);} else {// make a deep copy to avoid modifying any existing system state.pkgSetting = new PackageSetting(pkgSetting);pkgSetting.pkg = parsedPackage;// REMOVE SharedUserSetting from method; update in a separate call.//// TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,// secondaryCpuAbi are not known at this point so we always update them// to null here, only to reset them at a later point.Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,destCodeFile, parsedPackage.getNativeLibraryDir(),AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),UserManagerService.getInstance(),usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),parsedPackage.getMimeGroups(), newDomainSetId);}

  如果pkgSetting存在共享用户,但是和sharedUserSetting不同,则会将pkgSetting = null。sharedUserSetting是来自解析包,如果不同,认为共享用户发生了变化,这个时候,将pkgSetting置为null,后面将建一个新的PackageSetting对象。
  parsedPackage.getUsesStaticLibraries()来自解析Apk文件AndroidManifest.xml时,"application"标签包裹内"uses-static-library"标签的属性值。这里将它们取出来放到usesStaticLibraries变量内。
  injector.getDomainVerificationManagerInternal()得到的是DomainVerificationService对象,调用它的generateNewId()生成一个UUID值newDomainSetId。
  如果pkgSetting == null,将createNewPackage置为true,代表需要新建一个PackageSetting对象。scanFlags如果存在SCAN_AS_INSTANT_APP或SCAN_AS_VIRTUAL_PRELOAD标识,会将变量instantApp和virtualPreload设置为true。这俩属性在创建新PackageSetting对象时,使用。然后从解析包对象parsedPackage中得到标识pkgFlags、私有标识pkgPrivateFlags。再接着就是调用Settings.createNewSetting()创建一个新的PackageSetting对象。
  如果它不为null,则会通过深copy新建一个PackageSetting对象。并且将解析包指定给它。接着又调用Settings.updatePackageSetting()来更新pkgSetting的对应值。

分段三

    继续看scanPackageOnlyLI()的第三段代码:

java">        if (createNewPackage && originalPkgSetting != null) {// This is the initial transition from the original package, so,// fix up the new package's name now. We must do this after looking// up the package under its new name, so getPackageLP takes care of// fiddling things correctly.parsedPackage.setPackageName(originalPkgSetting.name);// File a report about this.String msg = "New package " + pkgSetting.realName+ " renamed to replace old package " + pkgSetting.name;reportSettingsProblem(Log.WARN, msg);}final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());// for existing packages, change the install state; but, only if it's explicitly specifiedif (!createNewPackage) {final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;setInstantAppForUser(injector, pkgSetting, userId, instantApp, fullApp);}// TODO(patb): see if we can do away with disabled check here.if (disabledPkgSetting != null|| (0 != (scanFlags & SCAN_NEW_INSTALL)&& pkgSetting != null && pkgSetting.isSystem())) {pkgSetting.getPkgState().setUpdatedSystemApp(true);}parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,injector.getCompatibility())).setSeInfoUser(SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId)));if (parsedPackage.isSystem()) {configurePackageComponents(parsedPackage);}

  如果是新创建包,并且originalPkgSetting不为null,则将解析包的包名改为originalPkgSetting的包名。这可能发生在改包名的首次安装时。
  得到userId,默认为UserHandle.USER_SYSTEM。
  如果不是新创建包,会根据SCAN_AS_INSTANT_APP和SCAN_AS_FULL_APP标识,设置pkgSetting里面对应用户的状态。
  如果参数disabledPkgSetting不为null,或者scanFlags 存在SCAN_NEW_INSTALL标识,并且pkgSetting是系统包的情况下,会将pkgSetting的成员变量PackageStateUnserialized对象 pkgState的updatedSystemApp设置为true。什么时候会设置SCAN_NEW_INSTALL标识呢?它是在用户手动安装一个Apk的时候,会设置该标识。
  如果解析包parsedPackage是系统的,调用configurePackageComponents()来设置解析包里组件的enabled属性。

分段四

继续看scanPackageOnlyLI()的第四段代码:

java">        final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride);final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();final File appLib32InstallDir = getAppLib32InstallDir();if ((scanFlags & SCAN_NEW_INSTALL) == 0) {if (needToDeriveAbi) {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,cpuAbiOverride, appLib32InstallDir);derivedAbi.first.applyTo(parsedPackage);derivedAbi.second.applyTo(parsedPackage);Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);// Some system apps still use directory structure for native libraries// in which case we might end up not detecting abi solely based on apk// structure. Try to detect abi based on directory structure.String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);if (parsedPackage.isSystem() && !isUpdatedSystemApp&& pkgRawPrimaryCpuAbi == null) {final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(parsedPackage);abis.applyTo(parsedPackage);abis.applyTo(pkgSetting);final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,isUpdatedSystemApp, appLib32InstallDir);nativeLibraryPaths.applyTo(parsedPackage);}} else {// This is not a first boot or an upgrade, don't bother deriving the// ABI during the scan. Instead, trust the value that was stored in the// package setting.parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings).setSecondaryCpuAbi(secondaryCpuAbiFromSettings);final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,isUpdatedSystemApp, appLib32InstallDir);nativeLibraryPaths.applyTo(parsedPackage);if (DEBUG_ABI_SELECTION) {Slog.i(TAG, "Using ABIS and native lib paths from settings : " +parsedPackage.getPackageName() + " " +AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)+ ", "+ AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));}}} else {if ((scanFlags & SCAN_MOVE) != 0) {// We haven't run dex-opt for this move (since we've moved the compiled output too)// but we already have this packages package info in the PackageSetting. We just// use that and derive the native library path based on the new codepath.parsedPackage.setPrimaryCpuAbi(pkgSetting.primaryCpuAbiString).setSecondaryCpuAbi(pkgSetting.secondaryCpuAbiString);}// Set native library paths again. For moves, the path will be updated based on the// ABIs we've determined above. For non-moves, the path will be updated based on the// ABIs we determined during compilation, but the path will depend on the final// package path (after the rename away from the stage path).final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,appLib32InstallDir);nativeLibraryPaths.applyTo(parsedPackage);}

  cpuAbiOverride是从request.cpuAbiOverride里面得到的Abi,isUpdatedSystemApp则是代表是系统升级App。
  文件appLib32InstallDir的路径为"/data/app-lib"。
  下面要从scanFlags存不存在SCAN_NEW_INSTALL标识两种情况去讨论,SCAN_NEW_INSTALL是在用户手动安装APP的时候,会设置。
  1、scanFlags不存在SCAN_NEW_INSTALL标识,并且需要从解析包里得到Abi值
  会调用packageAbiHelper.derivePackageAbi()函数得到Pair对象值。packageAbiHelper.derivePackageAbi()其实主要是将本地库文件提取出来,并且确定解析包的主要、次要CPU ABI。可以进入这篇文件里面了解一下,Android 提取出Apk的本地库。
  然后将Pair对象值应用到解析包。Pair的first是Abis对象,Pair的second是PackageAbiHelper.NativeLibraryPaths。看一下它俩的applyTo()方法:

java">类Abis
public void applyTo(ParsedPackage pkg) {pkg.setPrimaryCpuAbi(primary).setSecondaryCpuAbi(secondary);}NativeLibraryPathspublic void applyTo(ParsedPackage pkg) {pkg.setNativeLibraryRootDir(nativeLibraryRootDir).setNativeLibraryRootRequiresIsa(nativeLibraryRootRequiresIsa).setNativeLibraryDir(nativeLibraryDir).setSecondaryNativeLibraryDir(secondaryNativeLibraryDir);}        

  可以看到,解析包对象主要设置了它的primaryCpuAbi、secondaryCpuAbi、nativeLibraryRootDir、nativeLibraryRootRequiresIsa、nativeLibraryDir、secondaryNativeLibraryDir。
  如果设置完之后,parsedPackage的主CpuAbi依然为null,这个时候,会调用packageAbiHelper.getBundledAppAbis()通过Apk文件所在的目录结构结合系统支持的ABI来得到Abi。得到结果之后,应用到解析包和包设置对象中。最后packageAbiHelper.deriveNativeLibraryPaths()(该方法也在这篇文章有介绍)得到本地库的路径然后设置到解析包中。
  2、scanFlags不存在SCAN_NEW_INSTALL标识,并且不需要从解析包里得到Abi值。
  在这种情况下,解析包直接取primaryCpuAbiFromSettings、secondaryCpuAbiFromSettings的值,从前面我们知道,它俩的值是从包设置对象里面取得的。接着调用packageAbiHelper.deriveNativeLibraryPaths()得到本地库的路径然后设置到解析包中。
  3、scanFlags存在SCAN_NEW_INSTALL标识
  代表着有可能用户手动安装APP的。SCAN_MOVE代表是要移动一个安装包,这个时候,将parsedPackage的Abi设置成包设置里的。接着也是调用packageAbiHelper.deriveNativeLibraryPaths()得到本地库的路径然后设置到解析包中。 Android 安装应用-准备阶段 里面已经执行过这些步骤了,为什么在这里又执行了一遍呢?Android 安装应用-准备阶段 里面执行过之后,紧接着就进行了更改文件目录名的操作,所以这些个路径其实已经发生改变了,在这里又执行了一遍,就将路径名改对了。

分段五

继续看scanPackageOnlyLI()的第五段代码:

java">        // This is a special case for the "system" package, where the ABI is// dictated by the zygote configuration (and init.rc). We should keep track// of this ABI so that we can deal with "normal" applications that run under// the same UID correctly.if (isPlatformPackage) {parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit() ?Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);}// If there's a mismatch between the abi-override in the package setting// and the abiOverride specified for the install. Warn about this because we// would've already compiled the app without taking the package setting into// account.if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {if (cpuAbiOverride == null) {Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +" for package " + parsedPackage.getPackageName());}}pkgSetting.primaryCpuAbiString = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);pkgSetting.secondaryCpuAbiString = AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage);pkgSetting.cpuAbiOverrideString = cpuAbiOverride;if (DEBUG_ABI_SELECTION) {Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()+ " to root=" + parsedPackage.getNativeLibraryRootDir()+ ", to dir=" + parsedPackage.getNativeLibraryDir()+ ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());}// Push the derived path down into PackageSettings so we know what to// clean up at uninstall time.pkgSetting.legacyNativeLibraryPathString = parsedPackage.getNativeLibraryRootDir();if (DEBUG_ABI_SELECTION) {Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"+ " primary=" + pkgSetting.primaryCpuAbiString+ " secondary=" + pkgSetting.primaryCpuAbiString+ " abiOverride=" + pkgSetting.cpuAbiOverrideString);}if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {// We don't do this here during boot because we can do it all// at once after scanning all existing packages.//// We also do this *before* we perform dexopt on this package, so that// we can avoid redundant dexopts, and also to make sure we've got the// code and package path correct.changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, parsedPackage,packageAbiHelper.getAdjustedAbiForSharedUser(pkgSetting.sharedUser.packages, parsedPackage));}parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions().contains(android.Manifest.permission.FACTORY_TEST));if (parsedPackage.isSystem()) {pkgSetting.setIsOrphaned(true);}

  如果是平台包,根据虚拟机是不是64位,将平台包的主Cpu ABI设置为系统支持的64位或32位的数组的第一序列的值。
  设置SCAN_NO_DEX是为了跳过dexopt。
  接着设置了包设置对象pkgSetting的主要、次要ABI为解析包里的主要次要ABI。并将pkgSetting.cpuAbiOverrideString = cpuAbiOverride。
  接着会将解析包的本地库文件的根目录,设置到包设置对象的legacyNativeLibraryPathString属性中。
  在不是启动的过程(SCAN_BOOTING代表系统启动)中,并且包设置对象pkgSetting的共享用户不为null的情况下,先调用packageAbiHelper.getAdjustedAbiForSharedUser(pkgSetting.sharedUser.packages, parsedPackage) 得到共享用户下包需要调整的ABI,接着调用applyAdjustedAbiToSharedUser()会找到共享用户下的包们主要CPU ABI为null的包,将包的getPath()放入changedAbiCodePath中。
  接着设置解析包的FACTORY_TEST标识。它是由系统处于的工程测试模式打开 和解析包中权限包含android.Manifest.permission.FACTORY_TEST共同确定的。
  如果解析包是系统的,会设置包设置对象setIsOrphaned(true)。

得到共享用户下包需要调整的ABI

  packageAbiHelper是PackageAbiHelperImpl实例,先看一下PackageAbiHelperImpl的getAdjustedAbiForSharedUser():

java">    /*** Adjusts ABIs for a set of packages belonging to a shared user so that they all match.* i.e, so that all packages can be run inside a single process if required.** Optionally, callers can pass in a parsed package via {@code newPackage} in which case* this function will either try and make the ABI for all packages in* {@code packagesForUser} match {@code scannedPackage} or will update the ABI of* {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This* variant is used when installing or updating a package that belongs to a shared user.** NOTE: We currently only match for the primary CPU abi string. Matching the secondary* adds unnecessary complexity.*/@Override@Nullablepublic String getAdjustedAbiForSharedUser(Set<PackageSetting> packagesForUser, AndroidPackage scannedPackage) {String requiredInstructionSet = null;if (scannedPackage != null) {String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(scannedPackage);if (pkgRawPrimaryCpuAbi != null) {requiredInstructionSet = VMRuntime.getInstructionSet(pkgRawPrimaryCpuAbi);}}PackageSetting requirer = null;for (PackageSetting ps : packagesForUser) {// If packagesForUser contains scannedPackage, we skip it. This will happen// when scannedPackage is an update of an existing package. Without this check,// we will never be able to change the ABI of any package belonging to a shared// user, even if it's compatible with other packages.if (scannedPackage != null && scannedPackage.getPackageName().equals(ps.name)) {continue;}if (ps.primaryCpuAbiString == null) {continue;}final String instructionSet =VMRuntime.getInstructionSet(ps.primaryCpuAbiString);if (requiredInstructionSet != null && !requiredInstructionSet.equals(instructionSet)) {// We have a mismatch between instruction sets (say arm vs arm64) warn about// this but there's not much we can do.String errorMessage = "Instruction set mismatch, "+ ((requirer == null) ? "[caller]" : requirer)+ " requires " + requiredInstructionSet + " whereas " + ps+ " requires " + instructionSet;Slog.w(PackageManagerService.TAG, errorMessage);}if (requiredInstructionSet == null) {requiredInstructionSet = instructionSet;requirer = ps;}}if (requiredInstructionSet == null) {return null;}final String adjustedAbi;if (requirer != null) {// requirer != null implies that either scannedPackage was null or that// scannedPackage did not require an ABI, in which case we have to adjust// scannedPackage to match the ABI of the set (which is the same as// requirer's ABI)adjustedAbi = requirer.primaryCpuAbiString;} else {// requirer == null implies that we're updating all ABIs in the set to// match scannedPackage.adjustedAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(scannedPackage);}return adjustedAbi;}

  先取到解析包的主要ABI对应的指令集requiredInstructionSet。
  接着循环包设置对象集合packagesForUser,如果解析包不为null,并且包名和当前循环的包名相等,就直接跳出,进行下次循环。
  如果循环的包设置的主要ABI为null,就进行下次循环。
  接下来,如果解析包的指令集为null,找到第一个主要ABI不为null的包设置对象,在解析包的主要指令为null的情况下,将解析包的指令设置给requiredInstructionSet变量,将包对象设置为requirer。
  接着判断requirer不为null,其实就是取值为循环包设置集合里面第一个ABI不为null的包设置对象的ABI。
  如果requirer为null,取值就是解析包的主要ABI。
  其实它这个方法,主要就是解析包的主要ABI存在,就取解析包的主要ABI;如果不存在,就去包设置集合里面寻找第一个主要ABI不为null的包设置的主要ABI。

得到共享用户中包需要修改ABI的包的路径

java">    /*** Applies the adjusted ABI calculated by* {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all* relevant packages and settings.* @param sharedUserSetting The {@code SharedUserSetting} to adjust* @param scannedPackage the package being scanned or null* @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}* @return the list of code paths that belong to packages that had their ABIs adjusted.*/private static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,ParsedPackage scannedPackage, String adjustedAbi) {if (scannedPackage != null)  {scannedPackage.setPrimaryCpuAbi(adjustedAbi);}List<String> changedAbiCodePath = null;for (PackageSetting ps : sharedUserSetting.packages) {if (scannedPackage == null || !scannedPackage.getPackageName().equals(ps.name)) {if (ps.primaryCpuAbiString != null) {continue;}ps.primaryCpuAbiString = adjustedAbi;if (ps.pkg != null) {if (!TextUtils.equals(adjustedAbi,AndroidPackageUtils.getRawPrimaryCpuAbi(ps.pkg))) {if (DEBUG_ABI_SELECTION) {Slog.i(TAG,"Adjusting ABI for " + ps.name + " to " + adjustedAbi+ " (scannedPackage="+ (scannedPackage != null ? scannedPackage : "null")+ ")");}if (changedAbiCodePath == null) {changedAbiCodePath = new ArrayList<>();}changedAbiCodePath.add(ps.getPathString());}}}}return changedAbiCodePath;}

  解析包不为null,直接设置解析包的主要ABI。
  接着就是在循环中寻找,包名和解析包的包名不同的情况下,包设置的主要ABI为null的包设置对象,并且会将包设置对象的主要ABI设置为调整的ABI adjustedAbi。然后检查包设置对象的解析包对象的ABI和调整的ABI不同,就会将包设置对象的getPathString()添加到结果集合中。

分段六

  继续看scanPackageOnlyLI()的最后一段代码:

java">        // Take care of first install / last update times.final long scanFileTime = getLastModifiedTime(parsedPackage);if (currentTime != 0) {if (pkgSetting.firstInstallTime == 0) {pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;} else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {pkgSetting.lastUpdateTime = currentTime;}} else if (pkgSetting.firstInstallTime == 0) {// We need *something*.  Take time time stamp of the file.pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;} else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {if (scanFileTime != pkgSetting.timeStamp) {// A package on the system image has changed; consider this// to be an update.pkgSetting.lastUpdateTime = scanFileTime;}}pkgSetting.setTimeStamp(scanFileTime);// TODO(b/135203078): Remove, move to constructorpkgSetting.pkg = parsedPackage;pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);pkgSetting.pkgPrivateFlags =PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {pkgSetting.versionCode = parsedPackage.getLongVersionCode();}// Update volume if neededfinal String volumeUuid = parsedPackage.getVolumeUuid();if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {Slog.i(PackageManagerService.TAG,"Update" + (pkgSetting.isSystem() ? " system" : "")+ " package " + parsedPackage.getPackageName()+ " volume from " + pkgSetting.volumeUuid+ " to " + volumeUuid);pkgSetting.volumeUuid = volumeUuid;}SharedLibraryInfo staticSharedLibraryInfo = null;if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {staticSharedLibraryInfo =AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);}List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());for (String name : parsedPackage.getLibraryNames()) {dynamicSharedLibraryInfos.add(AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));}}return new ScanResult(request, true, pkgSetting, changedAbiCodePath,!createNewPackage /* existingSettingCopied */, staticSharedLibraryInfo,dynamicSharedLibraryInfos);}

  处理包设置对象的firstInstallTime、lastUpdateTime。
  这里注意这一点,在这里将包设置对象的pkg设置为解析包对象parsedPackage。
  将包设置对象和解析包对象的Flags合并赋值给包设置对象的pkgFlags。
  将解析包对象的PrivateFlags合并赋值给包设置对象的pkgPrivateFlags。
  如果解析包对象的版本和包设置对象的版本不同,将包设置对象的版本改为解析包对象的版本。
  如果解析包对象的版本和包设置对象的volumeUuid不同,将pkgSetting.volumeUuid = volumeUuid。
  如果解析包对象里面如果配置了静态库,则生成静态库对象staticSharedLibraryInfo。
  如果解析包对象里面如果配置了动态库,则生成动态库对象添加到dynamicSharedLibraryInfos。
  最后将相关信息封装到ScanResult对象返回。封装的内容包括生成的ScanRequest对象,成功结果true、新生成的PackageSetting对象pkgSetting、共享用户中其他修改了ABI的文件路径、是否是深拷贝存在的PackageSetting对象、静态分享库信息、动态分享库信息。

总结

  根据标识调整浏览标识,设置解析包的状态,也会检查一些状态。
  将相关内容封装到ScanRequest对象中,包括解析包对象、共享用户对象、旧安装包对象、现在使用PackageSetting对象、禁止的系统PackageSetting对象、旧PackageSetting对象(改包名之前)、真包名、解析标识、浏览标识、是否是系统平台包、用户、CPU ABI参数。
  创建了一个新的PackageSetting对象(可能是直接new,也可能深copy现在使用的PackageSetting对象)。
  如果是系统第一次启动或者系统更新之后第一次启动时,需要去安装包里得到主要CPU ABI和次要CPU ABI,本地库解析的路径,提取本地库文件(能否提取需要判断)。如果不是,它的主要CPU ABI和次要CPU ABI取值是在生成PackageSetting对象时定的,但它会重新确定本地库解析的路径。
  最后将生成的ScanRequest对象,成功结果true、新生成的PackageSetting对象pkgSetting、共享用户中其他修改了ABI的文件路径、是否是深拷贝存在的PackageSetting对象、静态分享库信息、动态分享库信息,封装成ScanResult对象返回。


http://www.ppmy.cn/embedded/89043.html

相关文章

ELK对业务日志进行收集

ELK对业务日志进行收集 下载httpd 进到文件设置收集httpd的文件进行 设置 编辑内容 用于收集日志的内容 将日志的内容发送到实例当中 input {file{path > /etc/httpd/logs/access_logtype > "access"start_position > "beginning"}file{path &g…

【系统架构设计师】二十四、安全架构设计理论与实践①

目录 一、安全架构概述 1.1 信息安全面临的威胁 1.1.1 安全威胁分类 1.1.2 常见的安全威胁 1.2 安全架构的定义和范围 二、安全模型 2.1 状态机模型 2.2 Bell-LaPadula模型 2.3 Biba模型 2.4 Clark-Wilson模型 2.5 Chinese Wall 模型 往期推荐 一、安全架构概述 1…

SQLite库笔记:API函数编程

本文主要介绍SQLite库的一些核心API函数&#xff0c;和实现数据库增删查改功能的C语言示例程序代码。 目录 1. API函数原型 1.1 sqlite3_open 1.2 sqlite3_close 1.3 sqlite3_free 1.4 sqlite3_errmsg 1.5 sqlite3_exec 1.6 sqlite3_get_table 1.7 sqlite3_free_table…

屏幕比例与分辨率 响应式大屏实现的几种方式

一、屏幕比例&#xff1a; 屏幕比例即屏幕宽度和高度的比例&#xff0c;又名纵横比或者长宽比&#xff0c;常见的屏幕比例主要是 16:9 和 4:3 和 16:10。 其中&#xff0c;16:9是目前最为常见的屏幕比例&#xff0c;适用于电视、电脑显示器等。 (1).4:3【1.33:1】比例是最早…

HDFS常用命令

HDFS常用命令 1.HDFS命令介绍1.1基本语法格式1.2常用命令 1.HDFS命令介绍 HDFS 提供了一组命令行工具&#xff0c;用于管理和操作 HDFS 文件系统。 1.1基本语法格式 hdfs dfs -<命令> [选项] <参数>1.2常用命令 1.显示<path>指定的文件的详细信息。 had…

计算机基础(Windows 10+Office 2016)教程 —— 第7章 演示文稿软件PowerPoint 2016

第7章 演示文稿软件PowerPoint 2016 7.1 PowerPoint 2016入门7.1.1 PowerPoint 2016 简介7.1.2 PowerPoint 2016 的操作界面组成7.1.3 PowerPoint 2016 的窗口视图方式7.1.4 PowerPoint 2016 的演示文稿及其操作7.1.5 PowerPoint 2016 的幻灯片及其操作 7.2 演示文稿的编辑与设…

自定义配置新纪元:Laravel配置仓库深度解析

自定义配置新纪元&#xff1a;Laravel配置仓库深度解析 Laravel框架以其优雅的架构和“约定优于配置”的理念而广受开发者喜爱。在Laravel中&#xff0c;配置文件是管理应用设置的中心方式。然而&#xff0c;随着应用的扩展&#xff0c;你可能会发现需要更灵活的配置管理策略。…

头发健康知识一

头发重要性: 头发是人第二张脸,影响人容貌 形象和气质.头皮基本结构 角质层,颗粒层,有挤层,基底层4个细胞层组成,含有大量毛囊和皮脂腺和汗腺.头屑是什么? 头屑为角质细胞的新陈代谢脱落的死细胞,每天都会有十几万角质细胞脱落 (正常的代谢周期为28天). 当基底层细胞受损时…