diff options
| author | Jackal Guo <jackalguo@google.com> | 2020-11-05 13:24:00 +0800 |
|---|---|---|
| committer | Jackal Guo <jackalguo@google.com> | 2020-11-30 18:16:12 +0800 |
| commit | f2404ee37b0038f9d328155bbb062562bc333980 (patch) | |
| tree | 97f8dd10b48c6be0e7ace7e7ee69801e565b6476 /core/java/android | |
| parent | 23a1669512cc2d827949c72d75acc3446bfdc007 (diff) | |
Reduce package parsing times
After committing the install session, a single APK would be parsed
several times even if this APK doesn't change. We should store the
parsed result, and then reuse it to save unnecessary parsing calls
during the installation.
Bug: 138292426
Test: atest PackageManagerTest
Test: atest PackageManagerTests
Test: atest PackageManagerServiceTest
Test: atest PackageManagerShellCommandTest
Test: atest PackageParserTest
Test: manually both staged and non-staged install APK from adb
Test: manually install APK from Play Store and Android Studio
Change-Id: Ie786d964ac96536015e83e36865505a1f34e3cbf
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/content/pm/PackageParser.java | 30 | ||||
| -rw-r--r-- | core/java/android/content/pm/parsing/ApkLiteParseUtils.java | 69 |
2 files changed, 82 insertions, 17 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index cb0decfa4982..4dfbd75a9d67 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -406,9 +406,15 @@ public class PackageParser { public final boolean extractNativeLibs; public final boolean isolatedSplits; - public PackageLite(String codePath, ApkLite baseApk, String[] splitNames, - boolean[] isFeatureSplits, String[] usesSplitNames, String[] configForSplit, - String[] splitCodePaths, int[] splitRevisionCodes) { + // This does not represent the actual manifest structure since the 'profilable' tag + // could be used with attributes other than 'shell'. Extend if necessary. + public final boolean profilableByShell; + public final boolean isSplitRequired; + public final boolean useEmbeddedDex; + + public PackageLite(String codePath, String baseCodePath, ApkLite baseApk, + String[] splitNames, boolean[] isFeatureSplits, String[] usesSplitNames, + String[] configForSplit, String[] splitCodePaths, int[] splitRevisionCodes) { this.packageName = baseApk.packageName; this.versionCode = baseApk.versionCode; this.versionCodeMajor = baseApk.versionCodeMajor; @@ -418,8 +424,10 @@ public class PackageParser { this.isFeatureSplits = isFeatureSplits; this.usesSplitNames = usesSplitNames; this.configForSplit = configForSplit; + // The following paths may be different from the path in ApkLite because we + // move or rename the APK files. Use parameters to indicate the correct paths. this.codePath = codePath; - this.baseCodePath = baseApk.codePath; + this.baseCodePath = baseCodePath; this.splitCodePaths = splitCodePaths; this.baseRevisionCode = baseApk.revisionCode; this.splitRevisionCodes = splitRevisionCodes; @@ -429,6 +437,9 @@ public class PackageParser { this.use32bitAbi = baseApk.use32bitAbi; this.extractNativeLibs = baseApk.extractNativeLibs; this.isolatedSplits = baseApk.isolatedSplits; + this.useEmbeddedDex = baseApk.useEmbeddedDex; + this.isSplitRequired = baseApk.isSplitRequired; + this.profilableByShell = baseApk.profilableByShell; } public List<String> getAllCodePaths() { @@ -439,6 +450,10 @@ public class PackageParser { } return paths; } + + public long getLongVersionCode() { + return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode); + } } /** @@ -941,7 +956,8 @@ public class PackageParser { final ApkLite baseApk = parseApkLite(packageFile, flags); final String packagePath = packageFile.getAbsolutePath(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - return new PackageLite(packagePath, baseApk, null, null, null, null, null, null); + return new PackageLite(packagePath, baseApk.codePath, baseApk, null, null, null, null, null, + null); } static PackageLite parseClusterPackageLite(File packageDir, int flags) @@ -1031,8 +1047,8 @@ public class PackageParser { } final String codePath = packageDir.getAbsolutePath(); - return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames, - configForSplits, splitCodePaths, splitRevisionCodes); + return new PackageLite(codePath, baseApk.codePath, baseApk, splitNames, isFeatureSplits, + usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes); } /** diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index 72a66ed4d9fe..f583e2597855 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -18,9 +18,11 @@ package android.content.pm.parsing; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; +import static android.content.pm.PackageParser.APK_FILE_EXTENSION; import static android.content.pm.parsing.ParsingPackageUtils.validateName; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import android.annotation.NonNull; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageParser; @@ -50,6 +52,7 @@ import java.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** @hide */ public class ApkLiteParseUtils { @@ -95,8 +98,8 @@ public class ApkLiteParseUtils { final PackageParser.ApkLite baseApk = result.getResult(); final String packagePath = packageFile.getAbsolutePath(); return input.success( - new PackageParser.PackageLite(packagePath, baseApk, null, null, null, null, - null, null)); + new PackageParser.PackageLite(packagePath, baseApk.codePath, baseApk, null, + null, null, null, null, null)); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -159,13 +162,43 @@ public class ApkLiteParseUtils { } final PackageParser.ApkLite baseApk = apks.remove(null); + return composePackageLiteFromApks(input, packageDir, baseApk, apks); + } + + /** + * Utility method that retrieves lightweight details about the package by given location, + * base APK, and split APKs. + * + * @param packageDir Path to the package + * @param baseApk Parsed base APK + * @param splitApks Parsed split APKs + * @return PackageLite + */ + public static ParseResult<PackageParser.PackageLite> composePackageLiteFromApks( + ParseInput input, File packageDir, PackageParser.ApkLite baseApk, + ArrayMap<String, PackageParser.ApkLite> splitApks) { + return composePackageLiteFromApks(input, packageDir, baseApk, splitApks, false); + } + + /** + * Utility method that retrieves lightweight details about the package by given location, + * base APK, and split APKs. + * + * @param packageDir Path to the package + * @param baseApk Parsed base APK + * @param splitApks Parsed split APKs + * @param apkRenamed Indicate whether the APKs are renamed after parsed. + * @return PackageLite + */ + public static ParseResult<PackageParser.PackageLite> composePackageLiteFromApks( + ParseInput input, File packageDir, PackageParser.ApkLite baseApk, + ArrayMap<String, PackageParser.ApkLite> splitApks, boolean apkRenamed) { if (baseApk == null) { return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, "Missing base APK in " + packageDir); } - // Always apply deterministic ordering based on splitName - final int size = apks.size(); + final int size = ArrayUtils.size(splitApks); String[] splitNames = null; boolean[] isFeatureSplits = null; @@ -181,23 +214,39 @@ public class ApkLiteParseUtils { splitCodePaths = new String[size]; splitRevisionCodes = new int[size]; - splitNames = apks.keySet().toArray(splitNames); + splitNames = splitApks.keySet().toArray(splitNames); Arrays.sort(splitNames, PackageParser.sSplitNameComparator); for (int i = 0; i < size; i++) { - final PackageParser.ApkLite apk = apks.get(splitNames[i]); + final PackageParser.ApkLite apk = splitApks.get(splitNames[i]); usesSplitNames[i] = apk.usesSplitName; isFeatureSplits[i] = apk.isFeatureSplit; configForSplits[i] = apk.configForSplit; - splitCodePaths[i] = apk.codePath; + splitCodePaths[i] = apkRenamed ? new File(packageDir, + splitNameToFileName(apk)).getAbsolutePath() : apk.codePath; splitRevisionCodes[i] = apk.revisionCode; } } final String codePath = packageDir.getAbsolutePath(); - return input.success(new PackageParser.PackageLite(codePath, baseApk, splitNames, - isFeatureSplits, usesSplitNames, configForSplits, splitCodePaths, - splitRevisionCodes)); + final String baseCodePath = apkRenamed ? new File(packageDir, + splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.codePath; + return input.success( + new PackageParser.PackageLite(codePath, baseCodePath, baseApk, splitNames, + isFeatureSplits, usesSplitNames, configForSplits, splitCodePaths, + splitRevisionCodes)); + } + + /** + * Utility method that retrieves canonical file name by given split name from parsed APK. + * + * @param apk Parsed APK + * @return The canonical file name + */ + public static String splitNameToFileName(@NonNull PackageParser.ApkLite apk) { + Objects.requireNonNull(apk); + final String fileName = apk.splitName == null ? "base" : "split_" + apk.splitName; + return fileName + APK_FILE_EXTENSION; } /** |
