Android 安装应用-准备阶段

avatar
作者
筋斗云
阅读量:0

  安装应用的准备阶段是在PackageManagerService类中的preparePackageLI(InstallArgs args, PackageInstalledInfo res),代码有些长,分段阅读。

分段一

  分段一:

    @GuardedBy("mInstallLock")     private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)             throws PrepareFailure {         final int installFlags = args.installFlags;         final File tmpPackageFile = new File(args.getCodePath());         final boolean onExternal = args.volumeUuid != null;         final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);         final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);         final boolean virtualPreload =                 ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);         final boolean isRollback = args.installReason == PackageManager.INSTALL_REASON_ROLLBACK;         @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             scanFlags |= SCAN_INITIAL;         }         if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {             scanFlags |= SCAN_DONT_KILL_APP;         }         if (instantApp) {             scanFlags |= SCAN_AS_INSTANT_APP;         }         if (fullApp) {             scanFlags |= SCAN_AS_FULL_APP;         }         if (virtualPreload) {             scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;         }          if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);          // Validity check         if (instantApp && onExternal) {             Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);             throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID);         }          // Retrieve PackageSettings and parse package         @ParseFlags final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY                 | ParsingPackageUtils.PARSE_ENFORCE_CODE                 | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);          Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");         final ParsedPackage parsedPackage;         try (PackageParser2 pp = mInjector.getPreparingPackageParser()) {             parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);             AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);         } catch (PackageParserException e) {             throw new PrepareFailure("Failed parse during installPackageLI", e);         } finally {             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);         }          // Instant apps have several additional install-time checks.         if (instantApp) {             if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) {                 Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()                                 + " does not target at least O");                 throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,                         "Instant app package must target at least O");             }             if (parsedPackage.getSharedUserId() != null) {                 Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()                         + " may not declare sharedUserId.");                 throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,                         "Instant app package may not declare a sharedUserId");             }         }          if (parsedPackage.isStaticSharedLibrary()) {             // Static shared libraries have synthetic package names             renameStaticSharedLibraryPackage(parsedPackage);              // No static shared libs on external storage             if (onExternal) {                 Slog.i(TAG, "Static shared libs can only be installed on internal storage.");                 throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,                         "Packages declaring static-shared libs cannot be updated");             }         }          String pkgName = res.name = parsedPackage.getPackageName();         if (parsedPackage.isTestOnly()) {             if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {                 throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");             }         }          try {             // either use what we've been given or parse directly from the APK             if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {                 parsedPackage.setSigningDetails(args.signingDetails);             } else {                 parsedPackage.setSigningDetails(ParsingPackageUtils.getSigningDetails(                         parsedPackage, false /* skipVerify */));             }         } catch (PackageParserException e) {             throw new PrepareFailure("Failed collect during installPackageLI", e);         }          if (instantApp && parsedPackage.getSigningDetails().signatureSchemeVersion                 < SignatureSchemeVersion.SIGNING_BLOCK_V2) {             Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()                     + " is not signed with at least APK Signature Scheme v2");             throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,                     "Instant app package must be signed with APK Signature Scheme v2 or greater");         }         

  先设置一些变量。installFlags为安装表示,tmpPackageFile为安装apk文件目录,onExternal是安装在外部空间,instantApp是代表INSTANT_APP。在这安装的例子中,args是FileInstallArgs类对象,它的.getCodePath()的值是来自InstallParams对象的成员origin的成员file,而构造InstallParams对象时,参数就是安装apk文件的目录。scanFlags开始时,就有SCAN_NEW_INSTALL和SCAN_UPDATE_SIGNATURE标识,它的值是在浏览那个步骤,需要使用,它还根据状态,是否增加几个标识。
  在INSTANT_APP并且是装在外部的情况下,会扔出PrepareFailure异常。
  下面是要解析包,pp是PackageParser2类对象,tmpPackageFile是目录,解析出来的parsedPackage实际类型是PackageImpl。
  AndroidPackageUtils.validatePackageDexMetadata(parsedPackage)是如果存在安装包的dex元数据文件,是去做验证。
  如果是INSTANT_APP,目标sdk不能低于Build.VERSION_CODES.O。并且不能声明sharedUserId属性值。不然会报异常。
  如果解析包是静态分享库,需要将它的包名改成静态分享库的名字格式,它的格式为packageName + “_” + libraryVersion。并且静态分享库不能放在外部存储中。
  接着会为参数res.name赋值为解析包的包名。
  如果解析包是配置了android:testOnly=“true”,但是安装标识里面没有INSTALL_ALLOW_TEST,也会报PrepareFailure异常。
  如果参数args.signingDetails不为PackageParser.SigningDetails.UNKNOWN,说明它已经有签名信息了。所以直接将它设置在解析包对象parsedPackage中。如果没有,这时需要调用ParsingPackageUtils.getSigningDetails()得到对应的签名信息,然后设置到解析包对象parsedPackage中。
  如果是INSTANT_APP,并且签名方式版本比SIGNING_BLOCK_V2小,则会报PrepareFailure异常。

分段二

  分段二:

        boolean systemApp = false;         boolean replace = false;         synchronized (mLock) {             // Check if installing already existing package             if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {                 String oldName = mSettings.getRenamedPackageLPr(pkgName);                 if (parsedPackage.getOriginalPackages().contains(oldName)                         && mPackages.containsKey(oldName)) {                     // This package is derived from an original package,                     // and this device has been updating from that original                     // name.  We must continue using the original name, so                     // rename the new package here.                     parsedPackage.setPackageName(oldName);                     pkgName = parsedPackage.getPackageName();                     replace = true;                     if (DEBUG_INSTALL) {                         Slog.d(TAG, "Replacing existing renamed package: oldName="                                 + oldName + " pkgName=" + pkgName);                     }                 } else if (mPackages.containsKey(pkgName)) {                     // This package, under its official name, already exists                     // on the device; we should replace it.                     replace = true;                     if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);                 }                  if (replace) {                     // Prevent apps opting out from runtime permissions                     AndroidPackage oldPackage = mPackages.get(pkgName);                     final int oldTargetSdk = oldPackage.getTargetSdkVersion();                     final int newTargetSdk = parsedPackage.getTargetSdkVersion();                     if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1                             && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {                         throw new PrepareFailure(                                 PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,                                 "Package " + parsedPackage.getPackageName()                                         + " new target SDK " + newTargetSdk                                         + " doesn't support runtime permissions but the old"                                         + " target SDK " + oldTargetSdk + " does.");                     }                     // Prevent persistent apps from being updated                     if (oldPackage.isPersistent()                             && ((installFlags & PackageManager.INSTALL_STAGED) == 0)) {                         throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,                                 "Package " + oldPackage.getPackageName() + " is a persistent app. "                                         + "Persistent apps are not updateable.");                     }                 }             }              PackageSetting ps = mSettings.getPackageLPr(pkgName);             if (ps != null) {                 if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);                  // Static shared libs have same package with different versions where                 // we internally use a synthetic package name to allow multiple versions                 // of the same package, therefore we need to compare signatures against                 // the package setting for the latest library version.                 PackageSetting signatureCheckPs = ps;                 if (parsedPackage.isStaticSharedLibrary()) {                     SharedLibraryInfo libraryInfo = getLatestSharedLibraVersionLPr(parsedPackage);                     if (libraryInfo != null) {                         signatureCheckPs = mSettings.getPackageLPr(libraryInfo.getPackageName());                     }                 }                  // Quick validity check that we're signed correctly if updating;                 // we'll check this again later when scanning, but we want to                 // bail early here before tripping over redefined permissions.                 final KeySetManagerService ksms = mSettings.getKeySetManagerService();                 if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {                     if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {                         throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "                                 + parsedPackage.getPackageName() + " upgrade keys do not match the "                                 + "previously installed version");                     }                 } else {                     try {                         final boolean compareCompat = isCompatSignatureUpdateNeeded(parsedPackage);                         final boolean compareRecover = isRecoverSignatureUpdateNeeded(                                 parsedPackage);                         // We don't care about disabledPkgSetting on install for now.                         final boolean compatMatch = verifySignatures(signatureCheckPs, null,                                 parsedPackage.getSigningDetails(), compareCompat, compareRecover,                                 isRollback);                         // The new KeySets will be re-added later in the scanning process.                         if (compatMatch) {                             synchronized (mLock) {                                 ksms.removeAppKeySetDataLPw(parsedPackage.getPackageName());                             }                         }                     } catch (PackageManagerException e) {                         throw new PrepareFailure(e.error, e.getMessage());                     }                 }                  if (ps.pkg != null) {                     systemApp = ps.pkg.isSystem();                 }                 res.origUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);             }              final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups());             for (int groupNum = 0; groupNum < numGroups; groupNum++) {                 final ParsedPermissionGroup group =                         parsedPackage.getPermissionGroups().get(groupNum);                 final PermissionGroupInfo sourceGroup = getPermissionGroupInfo(group.getName(), 0);                  if (sourceGroup != null                         && cannotInstallWithBadPermissionGroups(parsedPackage)) {                     final String sourcePackageName = sourceGroup.packageName;                      if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName))                             && !doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,                             scanFlags)) {                         EventLog.writeEvent(0x534e4554, "146211400", -1,                                 parsedPackage.getPackageName());                          throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP,                                 "Package "                                         + parsedPackage.getPackageName()                                         + " attempting to redeclare permission group "                                         + group.getName() + " already owned by "                                         + sourcePackageName);                     }                 }             }             // TODO: Move logic for checking permission compatibility into PermissionManagerService             final int N = ArrayUtils.size(parsedPackage.getPermissions());             for (int i = N - 1; i >= 0; i--) {                 final ParsedPermission perm = parsedPackage.getPermissions().get(i);                 final Permission bp = mPermissionManager.getPermissionTEMP(perm.getName());                  // Don't allow anyone but the system to define ephemeral permissions.                 if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0                         && !systemApp) {                     Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName()                             + " attempting to delcare ephemeral permission "                             + perm.getName() + "; Removing ephemeral.");                     perm.setProtectionLevel(perm.getProtectionLevel() & ~PermissionInfo.PROTECTION_FLAG_INSTANT);                 }                  // Check whether the newly-scanned package wants to define an already-defined perm                 if (bp != null) {                     final String sourcePackageName = bp.getPackageName();                      if (!doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,                             scanFlags)) {                         // If the owning package is the system itself, we log but allow                         // install to proceed; we fail the install on all other permission                         // redefinitions.                         if (!sourcePackageName.equals("android")) {                             throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "                                     + parsedPackage.getPackageName()                                     + " attempting to redeclare permission "                                     + perm.getName() + " already owned by "                                     + sourcePackageName)                                     .conflictsWithExistingPermission(perm.getName(),                                             sourcePackageName);                         } else {                             Slog.w(TAG, "Package " + parsedPackage.getPackageName()                                     + " attempting to redeclare system permission "                                     + perm.getName() + "; ignoring new declaration");                             parsedPackage.removePermission(i);                         }                     } else if (!PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())) {                         // Prevent apps to change protection level to dangerous from any other                         // type as this would allow a privilege escalation where an app adds a                         // normal/signature permission in other app's group and later redefines                         // it as dangerous leading to the group auto-grant.                         if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE)                                 == PermissionInfo.PROTECTION_DANGEROUS) {                             if (bp != null && !bp.isRuntime()) {                                 Slog.w(TAG, "Package " + parsedPackage.getPackageName()                                         + " trying to change a non-runtime permission "                                         + perm.getName()                                         + " to runtime; keeping old protection level");                                 perm.setProtectionLevel(bp.getProtectionLevel());                             }                         }                     }                 }                  if (perm.getGroup() != null                         && cannotInstallWithBadPermissionGroups(parsedPackage)) {                     boolean isPermGroupDefinedByPackage = false;                     for (int groupNum = 0; groupNum < numGroups; groupNum++) {                         if (parsedPackage.getPermissionGroups().get(groupNum).getName()                                 .equals(perm.getGroup())) {                             isPermGroupDefinedByPackage = true;                             break;                         }                     }                      if (!isPermGroupDefinedByPackage) {                         final PermissionGroupInfo sourceGroup =                                 getPermissionGroupInfo(perm.getGroup(), 0);                          if (sourceGroup == null) {                             EventLog.writeEvent(0x534e4554, "146211400", -1,                                     parsedPackage.getPackageName());                              throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,                                     "Package "                                             + parsedPackage.getPackageName()                                             + " attempting to declare permission "                                             + perm.getName() + " in non-existing group "                                             + perm.getGroup());                         } else {                             String groupSourcePackageName = sourceGroup.packageName;                              if (!PLATFORM_PACKAGE_NAME.equals(groupSourcePackageName)                                     && !doesSignatureMatchForPermissions(groupSourcePackageName,                                     parsedPackage, scanFlags)) {                                 EventLog.writeEvent(0x534e4554, "146211400", -1,                                         parsedPackage.getPackageName());                                  throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,                                         "Package "                                                 + parsedPackage.getPackageName()                                                 + " attempting to declare permission "                                                 + perm.getName() + " in group "                                                 + perm.getGroup() + " owned by package "                                                 + groupSourcePackageName                                                 + " with incompatible certificate");                             }                         }                     }                 }             }         } 

  下面要进入一段同步锁的代码段,变量systemApp代表是系统APP,replace代表是要替换安装。
  我们在文章Android安装过程二 系统进程中PackageInstallerSession对象的创建 中代码分段二 中可以看到,会将PackageManager.INSTALL_REPLACE_EXISTING添加到SessionParams 对象中成员变量installFlags中,而我们这里FileInstallArgs对象成员变量installFlags的值就是来自SessionParams 对象中成员变量installFlags。所以,这里会进入这个if语句中。
  先看一下,应用包名变更的一种情况。如果包名变更,需要在配置文件(Manifest文件)中,配置application同级别original-package标签 的属性 "name"的值。变更之后,再更新安装时,会将包名变更的关系保存在PackageManagerService对象成员mSettings(Settings类对象)的成员变量mRenamedPackages中,它是WatchedArrayMap类型,key是变更之后包名,值为变更之前的包名。
  所以在这里,它先检查是否符合包名变更的情况,如果是,将解析包对象的包名设置为它的原来的包名。并且将临时变量pkgName设置为原来的包名,replace = true,代表它是替换更新。
  如果不是包名变更,但是正常包更新安装的情况,就是mPackages.containsKey(pkgName)为true的情况。mPackages中包括所有安装成功应用的包信息。如果是这种情况,将replace = true,代表它是替换更新。
  其他的就不是替换安装的情况,replace的值还是为false。
  接下来就是处理替换安装的情况下,如果旧安装包的目标sdk版本大于Build.VERSION_CODES.LOLLIPOP_MR1,但是新包的目标sdk版本小于等于它,则报异常。如果旧包时持久包,并且安装标识没有PackageManager.INSTALL_STAGED,则也会报异常。
  下面是从成员mSettings中通过包名取得PackageSetting对象,在安装成功之后,每个应用在mSettings中也会通过包名维持PackageSetting对象。如果它存在,下面主要会是去检查签名匹配。
  如果解析包是静态分享库,它会通过名字存在多个版本,这里是去找一个最近版本的库信息。
  之后拿到KeySetManagerService对象ksms。它里面维护这签名公钥和对应的key的关系。
  ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)这种是对于APK里面Manifest文件中配置了"key-sets"标签的应用来说的。一般的应用不会配置它。
  其他的会走else这种情况,它需要验证签名。变量compareCompat代表它会兼容签名升级之前和之后的格式。变量compareRecover代表它会考虑签名有可能会编码格式稍有错误的情况。这俩都代表一种回退的验证,如果正常验证没有通过,满足这俩条件,会进行回退验证。像compareCompat为true的情况下,这里需要理解一下PackageParser.SigningDetails这个类,它有一个成员变量Signature[] signatures,以前旧版本,如果多个证书,都会存在它里面。而现在,它们也会存在Signature类的成员变量Certificate[] mCertificateChain中,所以像这种情况,需要将mCertificateChain中的证书都取出来和之前的signatures进行比较。
  这里调用verifySignatures()方法,是得验证通过的(和返回值compatMatch为true不完全一样),如果没通过,会扔出PackageManagerException异常。
  如果compatMatch为true,需要删除该表对应的公钥相关的信息。
  如果ps.pkg != null,会检查它是否是系统app。
  res.origUsers则是已经安装的用户id。
  接下来会检查解析包的PermissionGroup,它来自Manifest中的"permission-group"标签(和uses-permission同级)。如果某个PermissionGroup在被其他的应用声明过,或者该应用自己本身升级使用。如果它们的证书不相互符合,会报PrepareFailure异常。
  下面是用来检查解析包的Permission,它来自Manifest中的"permission"标签(和uses-permission同级)。
  如果声明的权限等级有PermissionInfo.PROTECTION_FLAG_INSTANT,但是它不是系统APP,需要将该等级去除。
  如果该权限已经声明过(可能是应用本身声明也可能是其他应用),如果证书验证不能通过,加入原来声明权限的包名不是系统自己,会报异常。如果是系统自己声明的权限,需要将解析包的权限去除。
  权限声明过,并且证书验证通过,但是解析包不是平台系统包名,如果它声明的权限是危险级,并且该权限之前不是运行时,需要将解析包的保护等级设置成它之前的等级。
  下面接着处理的是,如果权限所属的权限组不是该解析包声明的权限组会做什么。isPermGroupDefinedByPackage为true,则说明它的权限组为解析包声明的,反之,则不是。
  在它不是解析包声明的权限组的情况下。如果找不到权限组,则报PrepareFailure异常,是说该权限没有对应的权限组。如果存在权限组,但是权限组所属的应用包不为系统,并且它和解析包的证书验证不通过的情况下,也会报不一致签名证书的PrepareFailure异常。

分段三

  分段三:

        if (systemApp) {             if (onExternal) {                 // Abort update; system app can't be replaced with app on sdcard                 throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,                         "Cannot install updates to system apps on sdcard");             } else if (instantApp) {                 // Abort update; system app can't be replaced with an instant app                 throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,                         "Cannot update a system app with an instant app");             }         }          if (args.move != null) {             // We did an in-place move, so dex is ready to roll             scanFlags |= SCAN_NO_DEX;             scanFlags |= SCAN_MOVE;              synchronized (mLock) {                 final PackageSetting ps = mSettings.getPackageLPr(pkgName);                 if (ps == null) {                     res.setError(INSTALL_FAILED_INTERNAL_ERROR,                             "Missing settings for moved package " + pkgName);                 }                  // We moved the entire application as-is, so bring over the                 // previously derived ABI information.                 parsedPackage.setPrimaryCpuAbi(ps.primaryCpuAbiString)                         .setSecondaryCpuAbi(ps.secondaryCpuAbiString);             }          } else {             // Enable SCAN_NO_DEX flag to skip dexopt at a later stage             scanFlags |= SCAN_NO_DEX;              try {                 PackageSetting pkgSetting;                 synchronized (mLock) {                     pkgSetting = mSettings.getPackageLPr(pkgName);                 }                 boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null                         && pkgSetting.getPkgState().isUpdatedSystemApp();                 final String abiOverride = deriveAbiOverride(args.abiOverride);                 AndroidPackage oldPackage = mPackages.get(pkgName);                 boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();                 final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>                         derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage,                         isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,                         abiOverride, mAppLib32InstallDir);                 derivedAbi.first.applyTo(parsedPackage);                 derivedAbi.second.applyTo(parsedPackage);             } catch (PackageManagerException pme) {                 Slog.e(TAG, "Error deriving application ABI", pme);                 throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,                         "Error deriving application ABI: " + pme.getMessage());             }         }          if (!args.doRename(res.returnCode, parsedPackage)) {             throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");         }          try {             setUpFsVerityIfPossible(parsedPackage);         } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) {             throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,                     "Failed to set up verity: " + e);         }          final PackageFreezer freezer =                 freezePackageForInstall(pkgName, installFlags, "installPackageLI");         boolean shouldCloseFreezerBeforeReturn = true; 

  如果是系统app,不能更新到外部空间,不能使用instant app方式更新。
  args.move != null代表是需要移动应用包,这里先不说。
  接着将scanFlags添加上SCAN_NO_DEX标识。
  再接下来就是处理本地库和确定使用的指令集。mInjector.getAbiHelper()在这里是PackageAbiHelperImpl对象,调用它的derivePackageAbi()方法,就是确定使用的指令abi,还会将对应的so包文件拷贝出来,放到对应的文件夹中。这块参看 Android 提取出Apk的本地库 该篇文章。使用的指令abi就在结果derivedAbi的first中,复制出来的so文件所在文件信息在derivedAbi.second中,所以将它们设置到解析包对象parsedPackage中。看下对应的类的apply方法如下:

    final class Abis {         public void applyTo(ParsedPackage pkg) {             pkg.setPrimaryCpuAbi(primary)                     .setSecondaryCpuAbi(secondary);         }        } 	…………     final class NativeLibraryPaths {         public void applyTo(ParsedPackage pkg) {             pkg.setNativeLibraryRootDir(nativeLibraryRootDir)                     .setNativeLibraryRootRequiresIsa(nativeLibraryRootRequiresIsa)                     .setNativeLibraryDir(nativeLibraryDir)                     .setSecondaryNativeLibraryDir(secondaryNativeLibraryDir);         }     }              

  可见这里是将对应值设置到pkg对象(实际是PackageImpl对象)的primaryCpuAbi、secondaryCpuAbi、nativeLibraryRootDir、nativeLibraryRootRequiresIsa、nativeLibraryDir、secondaryNativeLibraryDir中。
  接下来调用args.doRename(res.returnCode, parsedPackage),这里args是FileInstallArgs对象,所以会调用FileInstallArgs类的doRename(int status, ParsedPackage parsedPackage)方法,这里是重命名安装文件的目录。
  接下来就是处理安装文件,如果可能,将它enable fs-verity。
  下面会调用freezePackageForInstall(pkgName, installFlags, “installPackageLI”)创建一个PackageFreezer。如果installFlags没有INSTALL_DONT_KILL_APP标识,并且之前已经安装过该应用,在创建PackageFreezer时,会将该应用杀掉,以防止它继续运行,导致混乱。
  shouldCloseFreezerBeforeReturn变量是说该方法执行完返回时,是否应该关闭刚才创建的PackageFreezer对象。
  下面先看看重命名安装文件的目录,也即FileInstallArgs类的doRename()方法,再看看文件使能fs-verity,也即setUpFsVerityIfPossible(parsedPackage)方法。

重命名安装文件的目录

    class FileInstallArgs extends InstallArgs {     	……         boolean doRename(int status, ParsedPackage parsedPackage) {             if (status != PackageManager.INSTALL_SUCCEEDED) {                 cleanUp();                 return false;             }              final File targetDir = resolveTargetDir();             final File beforeCodeFile = codeFile;             final File afterCodeFile = getNextCodePath(targetDir, parsedPackage.getPackageName());              if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);             final boolean onIncremental = mIncrementalManager != null                     && isIncrementalPath(beforeCodeFile.getAbsolutePath());             try {                 makeDirRecursive(afterCodeFile.getParentFile(), 0775);                 if (onIncremental) {                     // Just link files here. The stage dir will be removed when the installation                     // session is completed.                     mIncrementalManager.linkCodePath(beforeCodeFile, afterCodeFile);                 } else {                     Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());                 }             } catch (IOException | ErrnoException e) {                 Slog.w(TAG, "Failed to rename", e);                 return false;             }              if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) {                 Slog.w(TAG, "Failed to restorecon");                 return false;             }              // Reflect the rename internally             codeFile = afterCodeFile;              // Reflect the rename in scanned details             try {                 parsedPackage.setCodePath(afterCodeFile.getCanonicalPath());             } catch (IOException e) {                 Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);                 return false;             }             parsedPackage.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,                     afterCodeFile, parsedPackage.getBaseApkPath()));             parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,                     afterCodeFile, parsedPackage.getSplitCodePaths()));              return true;         }          ……     }           	 

  如果当前结果已经是失败了,就调用cleanUp()执行清理。
  紧接着是调用resolveTargetDir()得到目标目录,看一下它的实现,

        private File resolveTargetDir() {             boolean isStagedInstall = (installFlags & INSTALL_STAGED) != 0;             if (isStagedInstall) {                 return Environment.getDataAppDirectory(null);             } else {                 return codeFile.getParentFile();             }         } 

  如果是staged安装,则目录为“/data/app”。如果是正常安装,得到成员变量codeFile的父目录。我们知道我们这里codeFile也是安装文件的目录,它里面包含安装文件。像我们的例子中如果是内置存储位置,codeFile为"/data/app/vmdl" + sessionId + “.tmp”,里面有安装文件为“base.apk”。所以这里得到的目标目录为"/data/app/“。
  回到doRename()中,将beforeCodeFile为之前的安装目录。
  再通过getNextCodePath()得到之后安装文件的目录文件,赋值给afterCodeFile。它的格式为 targetDir/~~[randomStrA]/[packageName]-[randomStrB],其中randomStrA、randomStrB都是随机数。
  接着会创建新生成目录。之后,会调用Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath())将之前的安装目录重命名为新生成的目录。像我们的例子即将”/data/app/vmdl" + sessionId + “.tmp”目录重命名为 /data/app/~~[randomStrA]/[packageName]-[randomStrB]。
  之后,会将codeFile指向新生成的目录。
  再之后,会将解析包对象的path设置为新生成的目录。
  下面也是设置mBaseApkPath为新生成的目录+安装包的名字。splitCodePaths为新生成的目录+其他安装包的名字。

文件设置fs-verity

    /**      * Set up fs-verity for the given package if possible.  This requires a feature flag of system      * property to be enabled only if the kernel supports fs-verity.      *      * <p>When the feature flag is set to legacy mode, only APK is supported (with some experimental      * kernel patches). In normal mode, all file format can be supported.      */     private void setUpFsVerityIfPossible(AndroidPackage pkg) throws InstallerException,             PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {         final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();         final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();         if (!standardMode && !legacyMode) {             return;         }          if (isIncrementalPath(pkg.getPath()) && IncrementalManager.getVersion()                 < IncrementalManager.MIN_VERSION_TO_SUPPORT_FSVERITY) {             return;         }          // Collect files we care for fs-verity setup.         ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();         if (legacyMode) {             synchronized (mLock) {                 final PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName());                 if (ps != null && ps.isPrivileged()) {                     fsverityCandidates.put(pkg.getBaseApkPath(), null);                     if (pkg.getSplitCodePaths() != null) {                         for (String splitPath : pkg.getSplitCodePaths()) {                             fsverityCandidates.put(splitPath, null);                         }                     }                 }             }         } else {             // NB: These files will become only accessible if the signing key is loaded in kernel's             // .fs-verity keyring.             fsverityCandidates.put(pkg.getBaseApkPath(),                     VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath()));              final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(                     pkg.getBaseApkPath());             if (new File(dmPath).exists()) {                 fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));             }              if (pkg.getSplitCodePaths() != null) {                 for (String path : pkg.getSplitCodePaths()) {                     fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));                      final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);                     if (new File(splitDmPath).exists()) {                         fsverityCandidates.put(splitDmPath,                                 VerityUtils.getFsveritySignatureFilePath(splitDmPath));                     }                 }             }         }          for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {             final String filePath = entry.getKey();             final String signaturePath = entry.getValue();              if (!legacyMode) {                 // fs-verity is optional for now.  Only set up if signature is provided.                 if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {                     try {                         VerityUtils.setUpFsverity(filePath, signaturePath);                     } catch (IOException e) {                         throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,                                 "Failed to enable fs-verity: " + e);                     }                 }                 continue;             }              // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN.             final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(filePath);             if (result.isOk()) {                 if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath);                 final FileDescriptor fd = result.getUnownedFileDescriptor();                 try {                     final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);                     try {                         // A file may already have fs-verity, e.g. when reused during a split                         // install. If the measurement succeeds, no need to attempt to set up.                         mInstaller.assertFsverityRootHashMatches(filePath, rootHash);                     } catch (InstallerException e) {                         mInstaller.installApkVerity(filePath, fd, result.getContentSize());                         mInstaller.assertFsverityRootHashMatches(filePath, rootHash);                     }                 } finally {                     IoUtils.closeQuietly(fd);                 }             } else if (result.isFailed()) {                 throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,                         "Failed to generate verity");             }         }     } 

  变量standardMode、legacyMode代表两种模式:标准模式、遗留模式。看一下标准模式、遗留模式的条件:

    /** Returns true if standard APK Verity is enabled. */     static boolean isApkVerityEnabled() {         return Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.R                 || SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED)                         == FSVERITY_ENABLED;     }      static boolean isLegacyApkVerityEnabled() {         return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY;     } 

  可见和系统SDK版本和系统属性"ro.apk_verity.mode"的值有关。如果SDK版本大于等于R时或者"ro.apk_verity.mode"的值为FSVERITY_ENABLED时,就为标准模式。如果"ro.apk_verity.mode"的值为FSVERITY_LEGACY时,为遗留模式。所谓标准模式,就是以后新的都使用这种模式,而遗留模式则是之前实现的,现在需要考虑兼容的。
  如果这两种模式都不满足,就结束执行。
  下面就要收集哪些文件需要建立fs-verity。fsverityCandidates就是收集的文件集合。
  如果在遗留模式下,当前解析包的应用是特权应用,会将它的基本包和分包安装文件收集到fsverityCandidates中的key。fsverityCandidates的value值是文件对应使用的证书文件,之前是不用的,所以设置为null。

标准模式fsverity

  如果是在标准模式下,会将解析包的基本安装文件路径和证书文件路径放到fsverityCandidates中。证书文件是安装文件名+".fsv_sig"的文件,它们在一个文件夹中。
  如果对应安装文件的dex元数据文件(它的名字是安装文件名字的“.apk”替换成“.dm”)存在,也会将它放到fsverityCandidates集合中。
  同样如果存在分包,也和基础安装文件是同样的处理。
  再往下就是循环fsverityCandidates集合中的文件,进行设置fsverity。
  可以看到,如果是在标准模式下,fsverity也不是必定进行的,也得提供证书的情况下,才会调用VerityUtils.setUpFsverity(filePath, signaturePath)设置fsverity。并且使用的这个公钥需要在.fs-verity内核密钥环中。

遗留模式fsverity

  如果是在遗留模式下,看一下如何设置fsverity。
  VerityUtils.generateApkVeritySetupData(filePath)方法,它会生成设置fsverity的数据,并且它里面会进行一些验证。如果数据生成成功了,它生成设置fsverity的数据会放在一个共享内存中,result.getUnownedFileDescriptor()得到文件描述符即指向共享内存。
  VerityUtils.generateApkVerityRootHash(filePath)是生成摘要hash。它是用来判断文件是否已经设置fsverity。mInstaller.assertFsverityRootHashMatches(filePath, rootHash)就是为了验证文件是否已经设置fsverity,因为有的文件可能已经设置过fsverity,所以如果验证通过,接着就关闭共享内存。如果文件没有设置fsverity,则会调用mInstaller.installApkVerity(filePath, fd, result.getContentSize())是执行设置fsverity,设置之后,再调用一遍mInstaller.assertFsverityRootHashMatches(filePath, rootHash)执行验证。
  如果生成设置fsverity的数据的方法返回失败了,会扔出PrepareFailure异常。
  下面来看看生成设置fsverity的数据,然后再看看生成验证摘要数据,之后看它如何设置fsverity。

生成设置fsverity的数据

  它的实现是VerityUtils.generateApkVeritySetupData(filePath),看一下它的代码:

    /**      * Generates legacy Merkle tree and fs-verity metadata with Signing Block skipped.      *      * @deprecated This is only used for previous fs-verity implementation, and should never be used      *             on new devices.      * @return {@code SetupResult} that contains the result code, and when success, the      *         {@code FileDescriptor} to read all the data from.      */     @Deprecated     public static SetupResult generateApkVeritySetupData(@NonNull String apkPath) {         if (DEBUG) {             Slog.d(TAG, "Trying to install legacy apk verity to " + apkPath);         }         SharedMemory shm = null;         try {             final byte[] signedVerityHash = ApkSignatureVerifier.getVerityRootHash(apkPath);             if (signedVerityHash == null) {                 if (DEBUG) {                     Slog.d(TAG, "Skip verity tree generation since there is no signed root hash");                 }                 return SetupResult.skipped();             }              Pair<SharedMemory, Integer> result =                     generateFsVerityIntoSharedMemory(apkPath, signedVerityHash);             shm = result.first;             int contentSize = result.second;             FileDescriptor rfd = shm.getFileDescriptor();             if (rfd == null || !rfd.valid()) {                 return SetupResult.failed();             }             return SetupResult.ok(Os.dup(rfd), contentSize);         } catch (IOException | SecurityException | DigestException | NoSuchAlgorithmException                 | SignatureNotFoundException | ErrnoException e) {             Slog.e(TAG, "Failed to set up apk verity: ", e);             return SetupResult.failed();         } finally {             if (shm != null) {                 shm.close();             }         }     } 

  ApkSignatureVerifier.getVerityRootHash(apkPath)主要就是从Apk文件中的签名分块中找到验证的根hash。这些内容可以参考一下该篇博客 Android APK文件的签名V2查找、验证。如果没有该验证摘要,就返回SetupResult.skipped(),代表跳过。
  接着是调用generateFsVerityIntoSharedMemory(apkPath, signedVerityHash)来生成建立FsVerity的数据并将它们放入共享内存中。他返回的结果是一个Pair对象result,result.first是一个共享内存对象,result.second则代表其中数据的长度。如果没有问题,会返回SetupResult.ok(Os.dup(rfd), contentSize)。其中rfd是共享内存的文件描述符,contentSize是数据的长度。
  看一下generateFsVerityIntoSharedMemory(apkPath, signedVerityHash)方法:

    /**      * Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains      * Merkle tree and fsverity headers for the given apk, in the form that can immediately be used      * for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has      * length equals to the returned {@code Integer}.      */     private static Pair<SharedMemory, Integer> generateFsVerityIntoSharedMemory(String apkPath,             @NonNull byte[] expectedRootHash)             throws IOException, DigestException, NoSuchAlgorithmException,                    SignatureNotFoundException {         TrackedShmBufferFactory shmBufferFactory = new TrackedShmBufferFactory();         byte[] generatedRootHash =                 ApkSignatureVerifier.generateApkVerity(apkPath, shmBufferFactory);         // We only generate Merkle tree once here, so it's important to make sure the root hash         // matches the signed one in the apk.         if (!Arrays.equals(expectedRootHash, generatedRootHash)) {             throw new SecurityException("verity hash mismatch: "                     + bytesToString(generatedRootHash) + " != " + bytesToString(expectedRootHash));         }          int contentSize = shmBufferFactory.getBufferLimit();         SharedMemory shm = shmBufferFactory.releaseSharedMemory();         if (shm == null) {             throw new IllegalStateException("Failed to generate verity tree into shared memory");         }         if (!shm.setProtect(OsConstants.PROT_READ)) {             throw new SecurityException("Failed to set up shared memory correctly");         }         return Pair.create(shm, contentSize);     } 

  TrackedShmBufferFactory是用来创建共享内存的类。ApkSignatureVerifier.generateApkVerity(apkPath, shmBufferFactory)是用来生成Merkle树根的摘要值,同时生成设置fsverity的数据也在刚生成的参数shmBufferFactory中。
  接着拿生成Merkle树根的摘要值和参数中的摘要值expectedRootHash相比,如果不相等,会报异常。
  shmBufferFactory.getBufferLimit()是设置fsverity的数据的长度,shmBufferFactory.releaseSharedMemory()是得到共享内存对象shm 。最后是构造成Pair对象返回。
  再看一下ApkSignatureVerifier.generateApkVerity(apkPath, shmBufferFactory)的实现:

    public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)             throws IOException, SignatureNotFoundException, SecurityException, DigestException,             NoSuchAlgorithmException {         // first try v3         try {             return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory);         } catch (SignatureNotFoundException e) {             // try older version         }         return ApkSignatureSchemeV2Verifier.generateApkVerity(apkPath, bufferFactory);     } 

  它还是先按照V3签名的方式处理,如果没找到V3签名就会去按照V2签名的方式生成根的hash。这里就按照V2签名的方式来说,它的实现在ApkSignatureSchemeV2Verifier类中:

    static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)             throws IOException, SignatureNotFoundException, SecurityException, DigestException,                    NoSuchAlgorithmException {         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {             SignatureInfo signatureInfo = findSignature(apk);             return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);         }     } 

  这里是找到签名的信息对象signatureInfo,然后调用VerityBuilder类的generateApkVerity(apkPath, bufferFactory, signatureInfo)方法

    /**      * Generates the apk-verity header and hash tree to be used by kernel for the given apk. This      * method does not check whether the root hash exists in the Signing Block or not.      *      * <p>The output is stored in the {@link ByteBuffer} created by the given {@link      * ByteBufferFactory}.      *      * @return the root hash of the generated hash tree.      */     @NonNull     static byte[] generateApkVerity(@NonNull String apkPath,             @NonNull ByteBufferFactory bufferFactory, @NonNull SignatureInfo signatureInfo)             throws IOException, SignatureNotFoundException, SecurityException, DigestException,                    NoSuchAlgorithmException {         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {             VerityResult result = generateVerityTreeInternal(apk, bufferFactory, signatureInfo);             ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,                     result.verityData.limit());             generateApkVerityFooter(apk, signatureInfo, footer);             // Put the reverse offset to apk-verity header at the end.             footer.putInt(footer.position() + 4);             result.verityData.limit(result.merkleTreeSize + footer.position());             return result.rootHash;         }     } 

  generateVerityTreeInternal(apk, bufferFactory, signatureInfo)是通过构建merkle树来计算出来树根的摘要值。它还会计算出来merkle树的大小和对应的数据。这块参考一下该篇博客 Android APK文件完整性验证
  接下来会处理设置fsverity的所需数据的尾部数据footer。它是通过generateApkVerityFooter(apk, signatureInfo, footer)实现的,接着会在尾部数据添加上数据的位置footer.position() + 4。
  VerityResult类型result是上面构造merkle树方法计算出来的结果,result.verityData即是设置fsverity的所需数据。它在这里会将它的数据所在的位置设置为result.merkleTreeSize + footer.position()。result.merkleTreeSize即为merkle树的大小,footer.position()为数据尾部数据大小。
  result.rootHash即为计算出来树根的摘要值,将它返回。

fsverity的所需数据的尾部数据的生成

  咱们看下尾部数据footer的生成,

    static void generateApkVerityFooter(@NonNull RandomAccessFile apk,             @NonNull SignatureInfo signatureInfo, @NonNull ByteBuffer footerOutput)             throws IOException {         footerOutput.order(ByteOrder.LITTLE_ENDIAN);         generateApkVerityHeader(footerOutput, apk.length(), DEFAULT_SALT);         long signingBlockSize =                 signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;         generateApkVerityExtensions(footerOutput, signatureInfo.apkSigningBlockOffset,                 signingBlockSize, signatureInfo.eocdOffset);     } 

  先是调用generateApkVerityHeader(footerOutput, apk.length(), DEFAULT_SALT)生成尾部数据的头数据,之后调用generateApkVerityExtensions(footerOutput, signatureInfo.apkSigningBlockOffset, signingBlockSize, signatureInfo.eocdOffset)生成尾部数据的扩展数据。signingBlockSize 是签名块的大小。
   先看下头数据的生成:

    private static ByteBuffer generateApkVerityHeader(ByteBuffer buffer, long fileSize,             byte[] salt) {         if (salt.length != 8) {             throw new IllegalArgumentException("salt is not 8 bytes long");         }          // TODO(b/30972906): update the reference when there is a better one in public.         buffer.put("TrueBrew".getBytes());  // magic          buffer.put((byte) 1);               // major version         buffer.put((byte) 0);               // minor version         buffer.put((byte) 12);              // log2(block-size): log2(4096)         buffer.put((byte) 7);               // log2(leaves-per-node): log2(4096 / 32)          buffer.putShort((short) 1);         // meta algorithm, SHA256 == 1         buffer.putShort((short) 1);         // data algorithm, SHA256 == 1          buffer.putInt(0);                   // flags         buffer.putInt(0);                   // reserved          buffer.putLong(fileSize);           // original file size          buffer.put((byte) 2);               // authenticated extension count         buffer.put((byte) 0);               // unauthenticated extension count         buffer.put(salt);                   // salt (8 bytes)         skip(buffer, 22);                   // reserved          return buffer;     } 

  可以根据注释看一下每个字段的意思,总共64个字节的数据。魔数为"TrueBrew",相关版本、对应算法、文件大小、盐等数据。这里的盐是8个数值为0的byte数组。
   先看下扩展数据的生成:

    private static ByteBuffer generateApkVerityExtensions(ByteBuffer buffer,             long signingBlockOffset, long signingBlockSize, long eocdOffset) {         // Snapshot of the experimental fs-verity structs (different from upstream).         //         // struct fsverity_extension_elide {         //   __le64 offset;         //   __le64 length;         // }         //         // struct fsverity_extension_patch {         //   __le64 offset;         //   u8 databytes[];         // };          final int kSizeOfFsverityExtensionHeader = 8;         final int kExtensionSizeAlignment = 8;          {             // struct fsverity_extension #1             final int kSizeOfFsverityElidedExtension = 16;              // First field is total size of extension, padded to 64-bit alignment             buffer.putInt(kSizeOfFsverityExtensionHeader + kSizeOfFsverityElidedExtension);             buffer.putShort((short) 1);  // ID of elide extension             skip(buffer, 2);             // reserved              // struct fsverity_extension_elide             buffer.putLong(signingBlockOffset);             buffer.putLong(signingBlockSize);         }          {             // struct fsverity_extension #2             final int kTotalSize = kSizeOfFsverityExtensionHeader                     + 8 // offset size                     + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;              buffer.putInt(kTotalSize);   // Total size of extension, padded to 64-bit alignment             buffer.putShort((short) 2);  // ID of patch extension             skip(buffer, 2);             // reserved              // struct fsverity_extension_patch             buffer.putLong(eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET);  // offset             buffer.putInt(Math.toIntExact(signingBlockOffset));  // databytes              // The extension needs to be 0-padded at the end, since the length may not be multiple             // of 8.             int kPadding = kExtensionSizeAlignment - kTotalSize % kExtensionSizeAlignment;             if (kPadding == kExtensionSizeAlignment) {                 kPadding = 0;             }             skip(buffer, kPadding);      // padding         }          return buffer;     } 

  看这注释,它这数据是按照fsverity_extension_elide、fsverity_extension_patch结构来进行填充的。
  其中方法参数signingBlockOffset是签名块的偏移值、signingBlockSize是签名块的大小、eocdOffset是中央目录尾部的数据在apk文件中的偏移量。
  这样我们就看完了fsverity的所需数据。前面是Merkle树的数据,后面是尾部数据,尾部数据包括头和扩展数据。头数据是一些魔数为"TrueBrew",相关版本、对应算法、文件大小、盐等数据。扩展数据是和fsverity_extension_elide、fsverity_extension_patch结构相关的数据。

设置文件fsverity

  设置文件fsverity是Installer对象的installApkVerity(String filePath, FileDescriptor verityInput, int contentSize)实现的,Installer对象又通过AIDL与installd进程进行交互。它最终会调用到InstalldNativeService.cpp文件中的installApkVerity(const std::string& filePath, android::base::unique_fd verityInputAshmem, int32_t contentSize)方法:

binder::Status InstalldNativeService::installApkVerity(const std::string& filePath,         android::base::unique_fd verityInputAshmem, int32_t contentSize) {     ENFORCE_UID(AID_SYSTEM);     CHECK_ARGUMENT_PATH(filePath);     std::lock_guard<std::recursive_mutex> lock(mLock);      if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {         return ok();     } #ifndef NDEBUG     ASSERT_PAGE_SIZE_4K(); #endif     // TODO: also check fsverity support in the current file system if compiled with DEBUG.     // TODO: change ashmem to some temporary file to support huge apk.     if (!ashmem_valid(verityInputAshmem.get())) {         return error("FD is not an ashmem");     }      // 1. Seek to the next page boundary beyond the end of the file.     ::android::base::unique_fd wfd(open(filePath.c_str(), O_WRONLY));     if (wfd.get() < 0) {         return error("Failed to open " + filePath);     }     struct stat st;     if (fstat(wfd.get(), &st) < 0) {         return error("Failed to stat " + filePath);     }     // fsverity starts from the block boundary.     off_t padding = kVerityPageSize - st.st_size % kVerityPageSize;     if (padding == kVerityPageSize) {         padding = 0;     }     if (lseek(wfd.get(), st.st_size + padding, SEEK_SET) < 0) {         return error("Failed to lseek " + filePath);     }      // 2. Write everything in the ashmem to the file.  Note that allocated     //    ashmem size is multiple of page size, which is different from the     //    actual content size.     int shmSize = ashmem_get_size_region(verityInputAshmem.get());     if (shmSize < 0) {         return error("Failed to get ashmem size: " + std::to_string(shmSize));     }     if (contentSize < 0) {         return error("Invalid content size: " + std::to_string(contentSize));     }     if (contentSize > shmSize) {         return error("Content size overflow: " + std::to_string(contentSize) + " > " +                      std::to_string(shmSize));     }     auto data = std::unique_ptr<void, std::function<void (void *)>>(         mmap(nullptr, contentSize, PROT_READ, MAP_SHARED, verityInputAshmem.get(), 0),         [contentSize] (void* ptr) {           if (ptr != MAP_FAILED) {             munmap(ptr, contentSize);           }         });      if (data.get() == MAP_FAILED) {         return error("Failed to mmap the ashmem");     }     char* cursor = reinterpret_cast<char*>(data.get());     int remaining = contentSize;     while (remaining > 0) {         int ret = TEMP_FAILURE_RETRY(write(wfd.get(), cursor, remaining));         if (ret < 0) {             return error("Failed to write to " + filePath + " (" + std::to_string(remaining) +                          + "/" + std::to_string(contentSize) + ")");         }         cursor += ret;         remaining -= ret;     }     wfd.reset();      // 3. Enable fsverity (needs readonly fd. Once it's done, the file becomes immutable.     ::android::base::unique_fd rfd(open(filePath.c_str(), O_RDONLY));     if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, nullptr) < 0) {         return error("Failed to enable fsverity on " + filePath);     }     return ok(); } 

  参数verityInputAshmem是共享内存的文件描述符,contentSize是设置fsverity数据的长度,filePath是文件的路径。
  先打开文件filePath,得到wfd文件描述符。然后调用fstat,得到文件信息,如果文件长度不是kVerityPageSize(4KB)的整数倍,需要得到填充数据的数量padding。
  接着调用lseek,将文件设置到文件长度+padding的位置。
  下面shmSize是共享内存的大小,如果数据大小contentSize大于共享内存大小会报溢出错误。
  接着data是通过mmap映射到共享内存的数据。
  然后通过while循环将数据写入文件的文件长度+padding的位置之后。
  再打开文件,通过ioctl调用FS_IOC_ENABLE_VERITY命令,将文件使能fsverity。
  这样我们就将遗留模式的文件使能fsverity说完了。

  下面我们接着返回到preparePackageLI(InstallArgs args, PackageInstalledInfo res)方法中,继续看代码

分段四

  分段四:

        try {             final AndroidPackage existingPackage;             String renamedPackage = null;             boolean sysPkg = false;             int targetScanFlags = scanFlags;             int targetParseFlags = parseFlags;             final PackageSetting ps;             final PackageSetting disabledPs;             if (replace) {                 if (parsedPackage.isStaticSharedLibrary()) {                     // Static libs have a synthetic package name containing the version                     // and cannot be updated as an update would get a new package name,                     // unless this is installed from adb which is useful for development.                     AndroidPackage existingPkg = mPackages.get(parsedPackage.getPackageName());                     if (existingPkg != null                             && (installFlags & PackageManager.INSTALL_FROM_ADB) == 0) {                         throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE,                                 "Packages declaring "                                         + "static-shared libs cannot be updated");                     }                 }                  final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;                  final AndroidPackage oldPackage;                 final String pkgName11 = parsedPackage.getPackageName();                 final int[] allUsers;                 final int[] installedUsers;                 final int[] uninstalledUsers;                  synchronized (mLock) {                     oldPackage = mPackages.get(pkgName11);                     existingPackage = oldPackage;                     if (DEBUG_INSTALL) {                         Slog.d(TAG,                                 "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage);                     }                      ps = mSettings.getPackageLPr(pkgName11);                     disabledPs = mSettings.getDisabledSystemPkgLPr(ps);                      // verify signatures are valid                     final KeySetManagerService ksms = mSettings.getKeySetManagerService();                     if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {                         if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) {                             throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,                                     "New package not signed by keys specified by upgrade-keysets: "                                             + pkgName11);                         }                     } else {                         SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails();                         SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails();                         // default to original signature matching                         if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails,                                 SigningDetails.CertCapabilities.INSTALLED_DATA)                                 && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails,                                 SigningDetails.CertCapabilities.ROLLBACK)) {                             // Allow the update to proceed if this is a rollback and the parsed                             // package's current signing key is the current signer or in the lineage                             // of the old package; this allows a rollback to a previously installed                             // version after an app's signing key has been rotated without requiring                             // the rollback capability on the previous signing key.                             if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf(                                     parsedPkgSigningDetails)) {                                 throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,                                         "New package has a different signature: " + pkgName11);                             }                         }                     }                      // don't allow a system upgrade unless the upgrade hash matches                     if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) {                         final byte[] digestBytes;                         try {                             final MessageDigest digest = MessageDigest.getInstance("SHA-512");                             updateDigest(digest, new File(parsedPackage.getBaseApkPath()));                             if (!ArrayUtils.isEmpty(parsedPackage.getSplitCodePaths())) {                                 for (String path : parsedPackage.getSplitCodePaths()) {                                     updateDigest(digest, new File(path));                                 }                             }                             digestBytes = digest.digest();                         } catch (NoSuchAlgorithmException | IOException e) {                             throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,                                     "Could not compute hash: " + pkgName11);                         }                         if (!Arrays.equals(oldPackage.getRestrictUpdateHash(), digestBytes)) {                             throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,                                     "New package fails restrict-update check: " + pkgName11);                         }                         // retain upgrade restriction                         parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash());                     }                      // Check for shared user id changes                     String invalidPackageName = null;                     if (!Objects.equals(oldPackage.getSharedUserId(),                             parsedPackage.getSharedUserId())) {                         invalidPackageName = parsedPackage.getPackageName();                     }                      if (invalidPackageName != null) {                         throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,                                 "Package " + invalidPackageName + " tried to change user "                                         + oldPackage.getSharedUserId());                     }                      // In case of rollback, remember per-user/profile install state                     allUsers = mUserManager.getUserIds();                     installedUsers = ps.queryInstalledUsers(allUsers, true);                     uninstalledUsers = ps.queryInstalledUsers(allUsers, false);                       // don't allow an upgrade from full to ephemeral                     if (isInstantApp) {                         if (args.user == null || args.user.getIdentifier() == UserHandle.USER_ALL) {                             for (int currentUser : allUsers) {                                 if (!ps.getInstantApp(currentUser)) {                                     // can't downgrade from full to instant                                     Slog.w(TAG,                                             "Can't replace full app with instant app: " + pkgName11                                                     + " for user: " + currentUser);                                     throw new PrepareFailure(                                             PackageManager.INSTALL_FAILED_SESSION_INVALID);                                 }                             }                         } else if (!ps.getInstantApp(args.user.getIdentifier())) {                             // can't downgrade from full to instant                             Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11                                     + " for user: " + args.user.getIdentifier());                             throw new PrepareFailure(                                     PackageManager.INSTALL_FAILED_SESSION_INVALID);                         }                     }                 }                  // Update what is removed                 res.removedInfo = new PackageRemovedInfo(this);                 res.removedInfo.uid = oldPackage.getUid();                 res.removedInfo.removedPackage = oldPackage.getPackageName();                 res.removedInfo.installerPackageName = ps.installSource.installerPackageName;                 res.removedInfo.isStaticSharedLib = parsedPackage.getStaticSharedLibName() != null;                 res.removedInfo.isUpdate = true;                 res.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));                 }                 res.removedInfo.uninstallReasons = new SparseArray<>(uninstalledUsers.length);                 for (int i = 0; i < uninstalledUsers.length; i++) {                     final int userId = uninstalledUsers[i];                     res.removedInfo.uninstallReasons.put(userId, ps.getUninstallReason(userId));                 }                  sysPkg = oldPackage.isSystem();                 if (sysPkg) {                     // Set the system/privileged/oem/vendor/product flags as needed                     final boolean privileged = oldPackage.isPrivileged();                     final boolean oem = oldPackage.isOem();                     final boolean vendor = oldPackage.isVendor();                     final boolean product = oldPackage.isProduct();                     final boolean odm = oldPackage.isOdm();                     final boolean systemExt = oldPackage.isSystemExt();                     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)                             | (odm ? SCAN_AS_ODM : 0)                             | (systemExt ? SCAN_AS_SYSTEM_EXT : 0);                      if (DEBUG_INSTALL) {                         Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage                                 + ", old=" + oldPackage);                     }                     res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);                     targetParseFlags = systemParseFlags;                     targetScanFlags = systemScanFlags;                 } else { // non system replace                     replace = true;                     if (DEBUG_INSTALL) {                         Slog.d(TAG,                                 "replaceNonSystemPackageLI: new=" + parsedPackage + ", old="                                         + oldPackage);                     }                 }             } else { // new package install 

  如果replace为true,代表是一个更新安装。下面这段代码都是处理更新安装的情况。
  如果安装包是一个静态分享库,它的名字会包含版本号,如果允许更新会生成一个新的包名,这是不允许,除非是使用adb安装形式,是可以的。
  ps是旧的PackageSetting对象,代码分段二中说过了KeySetManagerService对象,ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)这种是对于APK里面Manifest文件中配置了"key-sets"标签的应用来说的。一般的应用不会配置它。
  进入到else分支后,是检查签名验证的。如果parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails, SigningDetails.CertCapabilities.INSTALLED_DATA)和oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails, SigningDetails.CertCapabilities.ROLLBACK)都不满足的情况下,isRollback为true并且oldPkgSigningDetails.hasAncestorOrSelf( parsedPkgSigningDetails)为true才会不报PrepareFailure异常。
  系统包一般不允许更新,除非更新文件的hash摘要也旧包的相同。而新文件的hash摘要是使用文件的内容进行"SHA-512"摘要算法得到的。
  如果解析包和旧包的SharedUserId不同,会报INSTALL_FAILED_SHARED_USER_INCOMPATIBLE PrepareFailure异常。
  installedUsers变量是旧包的安装用户,uninstalledUsers是旧包的卸载用户。
  如果现在是InstantApp安装,而之前不是,则会报INSTALL_FAILED_SESSION_INVALID PrepareFailure异常。它不允许从完全安装向instant安装降级。
   接着会更新删除信息,它是一个PackageRemovedInfo对象。它包含uid,删除包名、安装者的包名、静态共享库是否、安装用户、还有安装用户的原因、卸载用户的原因。
   再接下来,如果是系统包更新,会根据旧包的状态,来更新浏览标识,这些标识包括SCAN_AS_PRIVILEGED、SCAN_AS_OEM、SCAN_AS_VENDOR、SCAN_AS_PRODUCT、SCAN_AS_ODM、SCAN_AS_SYSTEM_EXT。
  接着会设置res的返回code为PackageManager.INSTALL_SUCCEEDED。
  接着会更新变量targetParseFlags、targetScanFlags的值。
  如果不是系统更新则不进行处理了。

分段五

  分段五:

            } else { // new package install                 ps = null;                 disabledPs = null;                 replace = false;                 existingPackage = null;                 // Remember this for later, in case we need to rollback this install                 String pkgName1 = parsedPackage.getPackageName();                  if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + parsedPackage);                  // TODO(patb): MOVE TO RECONCILE                 synchronized (mLock) {                     renamedPackage = mSettings.getRenamedPackageLPr(pkgName1);                     if (renamedPackage != null) {                         // A package with the same name is already installed, though                         // it has been renamed to an older name.  The package we                         // are trying to install should be installed as an update to                         // the existing one, but that has not been requested, so bail.                         throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,                                 "Attempt to re-install " + pkgName1                                         + " without first uninstalling package running as "                                         + renamedPackage);                     }                     if (mPackages.containsKey(pkgName1)) {                         // Don't allow installation over an existing package with the same name.                         throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,                                 "Attempt to re-install " + pkgName1                                         + " without first uninstalling.");                     }                 }             }             // we're passing the freezer back to be closed in a later phase of install             shouldCloseFreezerBeforeReturn = false;              return new PrepareResult(replace, targetScanFlags, targetParseFlags,                     existingPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,                     ps, disabledPs);         } finally {             res.freezer = freezer;             if (shouldCloseFreezerBeforeReturn) {                 freezer.close();             }         }     } 

  这个else里面的处理是针对初次安装的应用来说的。
  可见ps、disabledPs、existingPackage都设置为null,因为现在还没有旧包。replace自然为false。
  接着是处理,包改过名字,但是不是按照正常的改包的步骤来做的(配置文件(Manifest文件)中,配置application同级别original-package标签 的属性 "name"的值),这时renamedPackage != null,所以会报异常INSTALL_FAILED_ALREADY_EXISTS异常。如果mPackages.containsKey(pkgName1)为true,也代表不是首次安装,也会报异常。
  shouldCloseFreezerBeforeReturn = false,说明关闭freezer在之后的某个安装阶段执行。 如果 shouldCloseFreezerBeforeReturn 为true,代表需要关闭freezer,直接执行freezer.close()关闭它。
  最后将是否替换包、浏览标识、解析标识、存在的旧解析包、新的安装解析包、清除代码缓存、是否系统包、旧的PackSetting对象,替换的系统PackSetting对象封装到PrepareResult对象中,返回它。

总结

  准备阶段做的事情,咱们在这里做一下总结:
  1、解析安装包,并且这里是以Cluster方式解析的。
  2、如果是更新安装,会检查签名。还会检查安装文件中声明的权限和权限组。
  3、解析包的so包复制到对应目录中,并将使用的ABI和对应的so包路径设置到解析包对象中。
  4、修改解析包的安装路径和安装位置。
  5、如果可能,会将安装文件设置FsVerity
  6、如果更新安装,还会检查签名,这次比上次严格。
  7、如果更新安装,会设置删除包的信息。
  8、最后将信息封装到PrepareResult对象中返回。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!