应用安装的浏览阶段主要是由PackageManagerService类中的scanPackageNewLI()实现的,看一下它的代码:
// 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 user
sharedUserSetting = 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)的实现
分段一
它的代码也很长,所以分段阅读,第一段:
@GuardedBy("mInstallLock")
@VisibleForTesting
@NonNull
static 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 directories
final 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 ABI
needToDeriveAbi = 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()的第二段代码:
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 call
pkgSetting = 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()的第三段代码:
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 specified
if (!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()的第四段代码:
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()方法:
类Abis
public void applyTo(ParsedPackage pkg) {
pkg.setPrimaryCpuAbi(primary)
.setSecondaryCpuAbi(secondary);
}
类NativeLibraryPaths
public 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()的第五段代码:
// 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():
/**
* 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
@Nullable
public 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的包的路径
/**
* 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()的最后一段代码:
// 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 constructor
pkgSetting.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 needed
final 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对象返回。