Merge changes from topic "apks_in_apex"
* changes:
PackageAbiHepler no longer modifies Package
Adds scan unit tests
diff --git a/services/core/java/com/android/server/pm/InstructionSets.java b/services/core/java/com/android/server/pm/InstructionSets.java
index f326f1d..ec48713 100644
--- a/services/core/java/com/android/server/pm/InstructionSets.java
+++ b/services/core/java/com/android/server/pm/InstructionSets.java
@@ -22,11 +22,11 @@
import android.text.TextUtils;
import android.util.ArraySet;
+import dalvik.system.VMRuntime;
+
import java.util.ArrayList;
import java.util.List;
-import dalvik.system.VMRuntime;
-
/**
* Provides various methods for obtaining and converting of instruction sets.
*
@@ -113,12 +113,15 @@
return allInstructionSets;
}
- public static String getPrimaryInstructionSet(ApplicationInfo info) {
- if (info.primaryCpuAbi == null) {
+ /**
+ * Calculates the primary instruction set based on the computed Abis of a given package.
+ */
+ public static String getPrimaryInstructionSet(PackageAbiHelper.Abis abis) {
+ if (abis.primary == null) {
return getPreferredInstructionSet();
}
- return VMRuntime.getInstructionSet(info.primaryCpuAbi);
+ return VMRuntime.getInstructionSet(abis.primary);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
new file mode 100644
index 0000000..6f46564
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.util.Set;
+
+@VisibleForTesting
+interface PackageAbiHelper {
+ /**
+ * Derive and get the location of native libraries for the given package,
+ * which varies depending on where and how the package was installed.
+ */
+ NativeLibraryPaths getNativeLibraryPaths(
+ PackageParser.Package pkg, File appLib32InstallDir);
+
+ /**
+ * Calculate the abis for a bundled app. These can uniquely be determined from the contents of
+ * the system partition, i.e whether it contains 64 or 32 bit shared libraries etc. We do not
+ * validate any of this information, and instead assume that the system was built sensibly.
+ */
+ Abis getBundledAppAbis(PackageParser.Package pkg);
+
+ /**
+ * Derive the ABI of a non-system package located at {@code pkg}. This information
+ * is derived purely on the basis of the contents of {@code pkg} and {@code cpuAbiOverride}.
+ *
+ * If {@code extractLibs} is true, native libraries are extracted from the app if required.
+ */
+ Pair<Abis, NativeLibraryPaths> derivePackageAbi(
+ PackageParser.Package pkg, String cpuAbiOverride, boolean extractLibs)
+ throws PackageManagerException;
+
+ /**
+ * Calculates adjusted 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 scannedPackage} 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.
+ *
+ * @return the calculated primary abi that should be set for all non-specified packages
+ * belonging to the shared user.
+ */
+ @Nullable
+ String getAdjustedAbiForSharedUser(
+ Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage);
+
+ /**
+ * The native library paths and related properties that should be set on a
+ * {@link android.content.pm.PackageParser.Package}.
+ */
+ final class NativeLibraryPaths {
+ public final String nativeLibraryRootDir;
+ public final boolean nativeLibraryRootRequiresIsa;
+ public final String nativeLibraryDir;
+ public final String secondaryNativeLibraryDir;
+
+ @VisibleForTesting
+ NativeLibraryPaths(String nativeLibraryRootDir,
+ boolean nativeLibraryRootRequiresIsa, String nativeLibraryDir,
+ String secondaryNativeLibraryDir) {
+ this.nativeLibraryRootDir = nativeLibraryRootDir;
+ this.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
+ this.nativeLibraryDir = nativeLibraryDir;
+ this.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
+ }
+
+ public void applyTo(PackageParser.Package pkg) {
+ pkg.applicationInfo.nativeLibraryRootDir = nativeLibraryRootDir;
+ pkg.applicationInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
+ pkg.applicationInfo.nativeLibraryDir = nativeLibraryDir;
+ pkg.applicationInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
+ }
+ }
+
+ /**
+ * The primary and secondary ABIs that should be set on a package and its package setting.
+ */
+ final class Abis {
+ public final String primary;
+ public final String secondary;
+
+ @VisibleForTesting
+ Abis(String primary, String secondary) {
+ this.primary = primary;
+ this.secondary = secondary;
+ }
+
+ Abis(PackageParser.Package pkg) {
+ this(pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi);
+ }
+
+ public void applyTo(PackageParser.Package pkg) {
+ pkg.applicationInfo.primaryCpuAbi = primary;
+ pkg.applicationInfo.secondaryCpuAbi = secondary;
+ }
+ public void applyTo(PackageSetting pkgSetting) {
+ // pkgSetting might be null during rescan following uninstall of updates
+ // to a bundled app, so accommodate that possibility. The settings in
+ // that case will be established later from the parsed package.
+ //
+ // If the settings aren't null, sync them up with what we've derived.
+ if (pkgSetting != null) {
+ pkgSetting.primaryCpuAbiString = primary;
+ pkgSetting.secondaryCpuAbiString = secondary;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
new file mode 100644
index 0000000..1d3d24c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+import static android.content.pm.PackageParser.isApkFile;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
+import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
+import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
+
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.os.Build;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Trace;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.util.ArrayUtils;
+
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+
+final class PackageAbiHelperImpl implements PackageAbiHelper {
+
+ private static String calculateBundledApkRoot(final String codePathString) {
+ final File codePath = new File(codePathString);
+ final File codeRoot;
+ if (FileUtils.contains(Environment.getRootDirectory(), codePath)) {
+ codeRoot = Environment.getRootDirectory();
+ } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) {
+ codeRoot = Environment.getOemDirectory();
+ } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) {
+ codeRoot = Environment.getVendorDirectory();
+ } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
+ codeRoot = Environment.getOdmDirectory();
+ } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) {
+ codeRoot = Environment.getProductDirectory();
+ } else if (FileUtils.contains(Environment.getSystemExtDirectory(), codePath)) {
+ codeRoot = Environment.getSystemExtDirectory();
+ } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
+ codeRoot = Environment.getOdmDirectory();
+ } else {
+ // Unrecognized code path; take its top real segment as the apk root:
+ // e.g. /something/app/blah.apk => /something
+ try {
+ File f = codePath.getCanonicalFile();
+ File parent = f.getParentFile(); // non-null because codePath is a file
+ File tmp;
+ while ((tmp = parent.getParentFile()) != null) {
+ f = parent;
+ parent = tmp;
+ }
+ codeRoot = f;
+ Slog.w(PackageManagerService.TAG, "Unrecognized code path "
+ + codePath + " - using " + codeRoot);
+ } catch (IOException e) {
+ // Can't canonicalize the code path -- shenanigans?
+ Slog.w(PackageManagerService.TAG, "Can't canonicalize code path " + codePath);
+ return Environment.getRootDirectory().getPath();
+ }
+ }
+ return codeRoot.getPath();
+ }
+
+ // Utility method that returns the relative package path with respect
+ // to the installation directory. Like say for /data/data/com.test-1.apk
+ // string com.test-1 is returned.
+ private static String deriveCodePathName(String codePath) {
+ if (codePath == null) {
+ return null;
+ }
+ final File codeFile = new File(codePath);
+ final String name = codeFile.getName();
+ if (codeFile.isDirectory()) {
+ return name;
+ } else if (name.endsWith(".apk") || name.endsWith(".tmp")) {
+ final int lastDot = name.lastIndexOf('.');
+ return name.substring(0, lastDot);
+ } else {
+ Slog.w(PackageManagerService.TAG, "Odd, " + codePath + " doesn't look like an APK");
+ return null;
+ }
+ }
+
+ private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws
+ PackageManagerException {
+ if (copyRet < 0) {
+ if (copyRet != PackageManager.NO_NATIVE_LIBRARIES
+ && copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
+ throw new PackageManagerException(copyRet, message);
+ }
+ }
+ }
+
+ @Override
+ public NativeLibraryPaths getNativeLibraryPaths(
+ PackageParser.Package pkg, File appLib32InstallDir) {
+ return getNativeLibraryPaths(new Abis(pkg), appLib32InstallDir, pkg.codePath,
+ pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(),
+ pkg.applicationInfo.isUpdatedSystemApp());
+ }
+
+ private static NativeLibraryPaths getNativeLibraryPaths(final Abis abis,
+ final File appLib32InstallDir, final String codePath, final String sourceDir,
+ final boolean isSystemApp, final boolean isUpdatedSystemApp) {
+ final File codeFile = new File(codePath);
+ final boolean bundledApp = isSystemApp && !isUpdatedSystemApp;
+
+ final String nativeLibraryRootDir;
+ final boolean nativeLibraryRootRequiresIsa;
+ final String nativeLibraryDir;
+ final String secondaryNativeLibraryDir;
+
+ if (isApkFile(codeFile)) {
+ // Monolithic install
+ if (bundledApp) {
+ // If "/system/lib64/apkname" exists, assume that is the per-package
+ // native library directory to use; otherwise use "/system/lib/apkname".
+ final String apkRoot = calculateBundledApkRoot(sourceDir);
+ final boolean is64Bit = VMRuntime.is64BitInstructionSet(
+ getPrimaryInstructionSet(abis));
+
+ // This is a bundled system app so choose the path based on the ABI.
+ // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
+ // is just the default path.
+ final String apkName = deriveCodePathName(codePath);
+ final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
+ nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
+ apkName).getAbsolutePath();
+
+ if (abis.secondary != null) {
+ final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
+ secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
+ secondaryLibDir, apkName).getAbsolutePath();
+ } else {
+ secondaryNativeLibraryDir = null;
+ }
+ } else {
+ final String apkName = deriveCodePathName(codePath);
+ nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
+ .getAbsolutePath();
+ secondaryNativeLibraryDir = null;
+ }
+
+ nativeLibraryRootRequiresIsa = false;
+ nativeLibraryDir = nativeLibraryRootDir;
+ } else {
+ // Cluster install
+ nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
+ nativeLibraryRootRequiresIsa = true;
+
+ nativeLibraryDir = new File(nativeLibraryRootDir,
+ getPrimaryInstructionSet(abis)).getAbsolutePath();
+
+ if (abis.secondary != null) {
+ secondaryNativeLibraryDir = new File(nativeLibraryRootDir,
+ VMRuntime.getInstructionSet(abis.secondary)).getAbsolutePath();
+ } else {
+ secondaryNativeLibraryDir = null;
+ }
+ }
+ return new NativeLibraryPaths(nativeLibraryRootDir, nativeLibraryRootRequiresIsa,
+ nativeLibraryDir, secondaryNativeLibraryDir);
+ }
+
+ @Override
+ public Abis getBundledAppAbis(PackageParser.Package pkg) {
+ final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
+
+ // If "/system/lib64/apkname" exists, assume that is the per-package
+ // native library directory to use; otherwise use "/system/lib/apkname".
+ final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);
+ final Abis abis = getBundledAppAbi(pkg, apkRoot, apkName);
+ return abis;
+ }
+
+ /**
+ * Deduces the ABI of a bundled app and sets the relevant fields on the
+ * parsed pkg object.
+ *
+ * @param apkRoot the root of the installed apk, something like {@code /system} or
+ * {@code /oem} under which system libraries are installed.
+ * @param apkName the name of the installed package.
+ */
+ private Abis getBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) {
+ final File codeFile = new File(pkg.codePath);
+
+ final boolean has64BitLibs;
+ final boolean has32BitLibs;
+
+ final String primaryCpuAbi;
+ final String secondaryCpuAbi;
+ if (isApkFile(codeFile)) {
+ // Monolithic install
+ has64BitLibs =
+ (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists();
+ has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists();
+ } else {
+ // Cluster install
+ final File rootDir = new File(codeFile, LIB_DIR_NAME);
+ if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS)
+ && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) {
+ final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]);
+ has64BitLibs = (new File(rootDir, isa)).exists();
+ } else {
+ has64BitLibs = false;
+ }
+ if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS)
+ && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) {
+ final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]);
+ has32BitLibs = (new File(rootDir, isa)).exists();
+ } else {
+ has32BitLibs = false;
+ }
+ }
+
+ if (has64BitLibs && !has32BitLibs) {
+ // The package has 64 bit libs, but not 32 bit libs. Its primary
+ // ABI should be 64 bit. We can safely assume here that the bundled
+ // native libraries correspond to the most preferred ABI in the list.
+
+ primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
+ secondaryCpuAbi = null;
+ } else if (has32BitLibs && !has64BitLibs) {
+ // The package has 32 bit libs but not 64 bit libs. Its primary
+ // ABI should be 32 bit.
+
+ primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
+ secondaryCpuAbi = null;
+ } else if (has32BitLibs && has64BitLibs) {
+ // The application has both 64 and 32 bit bundled libraries. We check
+ // here that the app declares multiArch support, and warn if it doesn't.
+ //
+ // We will be lenient here and record both ABIs. The primary will be the
+ // ABI that's higher on the list, i.e, a device that's configured to prefer
+ // 64 bit apps will see a 64 bit primary ABI,
+
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) {
+ Slog.e(PackageManagerService.TAG,
+ "Package " + pkg + " has multiple bundled libs, but is not multiarch.");
+ }
+
+ if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) {
+ primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
+ secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
+ } else {
+ primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
+ secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
+ }
+ } else {
+ primaryCpuAbi = null;
+ secondaryCpuAbi = null;
+ }
+ return new Abis(primaryCpuAbi, secondaryCpuAbi);
+ }
+
+ @Override
+ public Pair<Abis, NativeLibraryPaths> derivePackageAbi(
+ PackageParser.Package pkg, String cpuAbiOverride, boolean extractLibs)
+ throws PackageManagerException {
+ // Give ourselves some initial paths; we'll come back for another
+ // pass once we've determined ABI below.
+ final NativeLibraryPaths initialLibraryPaths = getNativeLibraryPaths(new Abis(pkg),
+ PackageManagerService.sAppLib32InstallDir, pkg.codePath,
+ pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(),
+ pkg.applicationInfo.isUpdatedSystemApp());
+
+ // We shouldn't attempt to extract libs from system app when it was not updated.
+ if (PackageManagerService.isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
+ extractLibs = false;
+ }
+
+ final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir;
+ final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa;
+
+ String primaryCpuAbi = null;
+ String secondaryCpuAbi = null;
+
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(pkg);
+ // TODO(multiArch): This can be null for apps that didn't go through the
+ // usual installation process. We can calculate it again, like we
+ // do during install time.
+ //
+ // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
+ // unnecessary.
+ final File nativeLibraryRoot = new File(nativeLibraryRootStr);
+
+ // Null out the abis so that they can be recalculated.
+ primaryCpuAbi = null;
+ secondaryCpuAbi = null;
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0) {
+ // Warn if we've set an abiOverride for multi-lib packages..
+ // By definition, we need to copy both 32 and 64 bit libraries for
+ // such packages.
+ if (pkg.cpuAbiOverride != null
+ && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
+ Slog.w(PackageManagerService.TAG,
+ "Ignoring abiOverride for multi arch application.");
+ }
+
+ int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
+ int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
+ if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+ if (extractLibs) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+ abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
+ useIsaSpecificSubdirs);
+ } else {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
+ abi32 = NativeLibraryHelper.findSupportedAbi(
+ handle, Build.SUPPORTED_32_BIT_ABIS);
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Shared library native code should be in the APK zip aligned
+ if (abi32 >= 0 && pkg.isLibrary() && extractLibs) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Shared library native lib extraction not supported");
+ }
+
+ maybeThrowExceptionForMultiArchCopy(
+ "Error unpackaging 32 bit native libs for multiarch app.", abi32);
+
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+ if (extractLibs) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+ abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
+ useIsaSpecificSubdirs);
+ } else {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
+ abi64 = NativeLibraryHelper.findSupportedAbi(
+ handle, Build.SUPPORTED_64_BIT_ABIS);
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ maybeThrowExceptionForMultiArchCopy(
+ "Error unpackaging 64 bit native libs for multiarch app.", abi64);
+
+ if (abi64 >= 0) {
+ // Shared library native libs should be in the APK zip aligned
+ if (extractLibs && pkg.isLibrary()) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Shared library native lib extraction not supported");
+ }
+ primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
+ }
+
+ if (abi32 >= 0) {
+ final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
+ if (abi64 >= 0) {
+ if (pkg.use32bitAbi) {
+ secondaryCpuAbi = primaryCpuAbi;
+ primaryCpuAbi = abi;
+ } else {
+ secondaryCpuAbi = abi;
+ }
+ } else {
+ primaryCpuAbi = abi;
+ }
+ }
+ } else {
+ String[] abiList = (cpuAbiOverride != null)
+ ? new String[]{cpuAbiOverride} : Build.SUPPORTED_ABIS;
+
+ // Enable gross and lame hacks for apps that are built with old
+ // SDK tools. We must scan their APKs for renderscript bitcode and
+ // not launch them if it's present. Don't bother checking on devices
+ // that don't have 64 bit support.
+ boolean needsRenderScriptOverride = false;
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null
+ && NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ needsRenderScriptOverride = true;
+ }
+
+ final int copyRet;
+ if (extractLibs) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+ copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
+ } else {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
+ copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+ if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Error unpackaging native libs for app, errorCode=" + copyRet);
+ }
+
+ if (copyRet >= 0) {
+ // Shared libraries that have native libs must be multi-architecture
+ if (pkg.isLibrary()) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Shared library with native libs must be multiarch");
+ }
+ primaryCpuAbi = abiList[copyRet];
+ } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES
+ && cpuAbiOverride != null) {
+ primaryCpuAbi = cpuAbiOverride;
+ } else if (needsRenderScriptOverride) {
+ primaryCpuAbi = abiList[0];
+ }
+ }
+ } catch (IOException ioe) {
+ Slog.e(PackageManagerService.TAG, "Unable to get canonical file " + ioe.toString());
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
+
+ // Now that we've calculated the ABIs and determined if it's an internal app,
+ // we will go ahead and populate the nativeLibraryPath.
+
+ final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi);
+ return new Pair<>(abis,
+ getNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir,
+ pkg.codePath, pkg.applicationInfo.sourceDir,
+ pkg.applicationInfo.isSystemApp(),
+ pkg.applicationInfo.isUpdatedSystemApp()));
+ }
+
+ /**
+ * 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, PackageParser.Package scannedPackage) {
+ String requiredInstructionSet = null;
+ if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
+ requiredInstructionSet = VMRuntime.getInstructionSet(
+ scannedPackage.applicationInfo.primaryCpuAbi);
+ }
+
+ 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.packageName.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 = scannedPackage.applicationInfo.primaryCpuAbi;
+ }
+ return adjustedAbi;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 54051ba..a77b728 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -93,14 +93,12 @@
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
-import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
-import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
@@ -278,6 +276,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
@@ -433,7 +432,7 @@
// user, but by default initialize to this.
public static final boolean DEBUG_DEXOPT = false;
- private static final boolean DEBUG_ABI_SELECTION = false;
+ static final boolean DEBUG_ABI_SELECTION = false;
private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
private static final boolean DEBUG_APP_DATA = false;
@@ -663,7 +662,8 @@
private static final File sAppInstallDir =
new File(Environment.getDataDirectory(), "app");
/** Directory where installed application's 32-bit native libraries are copied. */
- private static final File sAppLib32InstallDir =
+ @VisibleForTesting
+ static final File sAppLib32InstallDir =
new File(Environment.getDataDirectory(), "app-lib");
// ----------------------------------------------------------------
@@ -754,6 +754,30 @@
private final ApexManager mApexManager;
+ private final Injector mInjector;
+
+ /**
+ * Unit tests will instantiate and / or extend to mock dependencies / behaviors.
+ */
+ @VisibleForTesting
+ static class Injector {
+ private final UserManagerInternal mUserManager;
+ private final PackageAbiHelper mAbiHelper;
+
+ Injector(UserManagerInternal userManager, PackageAbiHelper abiHelper) {
+ mUserManager = userManager;
+ mAbiHelper = abiHelper;
+ }
+
+ public UserManagerInternal getUserManager() {
+ return mUserManager;
+ }
+
+ public PackageAbiHelper getAbiHelper() {
+ return mAbiHelper;
+ }
+ }
+
class PackageParserCallback implements PackageParser.Callback {
@Override public final boolean hasFeature(String feature) {
return PackageManagerService.this.hasSystemFeature(feature, 0);
@@ -2093,8 +2117,8 @@
// survive long enough to benefit of background optimizations.
for (int userId : firstUserIds) {
PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
- // There's a race currently where some install events may interleave with an uninstall.
- // This can lead to package info being null (b/36642664).
+ // There's a race currently where some install events may interleave with an
+ // uninstall. This can lead to package info being null (b/36642664).
if (info != null) {
mDexManager.notifyPackageInstalled(info, userId);
}
@@ -2164,6 +2188,7 @@
* external/removable/unprotected storage.
* @return {@link StorageEnum#TYPE_UNKNOWN} if the package is not stored externally or the
* corresponding {@link StorageEnum} storage type value if it is.
+ * corresponding {@link StorageEnum} storage type value if it is.
*/
private static int getPackageExternalStorageType(VolumeInfo packageVolume,
boolean packageIsExternal) {
@@ -2421,6 +2446,11 @@
mPermissionManager.getPermissionSettings(), mPackages);
}
}
+
+ // TODO(b/137961986): We should pass this via constructor, but would first need to create
+ // a packages lock that could also be passed in.
+ mInjector = new Injector(getUserManagerInternal(), new PackageAbiHelperImpl());
+
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
@@ -3128,7 +3158,9 @@
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
final List<String> changedAbiCodePath =
- adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/);
+ applyAdjustedAbiToSharedUser(setting, null /*scannedPackage*/,
+ mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
+ setting.packages, null /*scannedPackage*/));
if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
final String codePathString = changedAbiCodePath.get(i);
@@ -9397,7 +9429,8 @@
null /* originalPkgSetting */, null, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
- final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L);
+ final ScanResult scanResult =
+ scanPackageOnlyLI(request, mInjector, mFactoryTest, -1L);
if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) {
scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting);
}
@@ -10759,7 +10792,8 @@
}
/** The result of a package scan. */
- private static class ScanResult {
+ @VisibleForTesting
+ static class ScanResult {
/** The request that initiated the scan that produced this result. */
public final ScanRequest request;
/** Whether or not the package scan was successful */
@@ -10798,7 +10832,8 @@
}
/** A package to be scanned */
- private static class ScanRequest {
+ @VisibleForTesting
+ static class ScanRequest {
/** The parsed package */
@NonNull public final PackageParser.Package pkg;
/** The package this package replaces */
@@ -10991,7 +11026,7 @@
pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
originalPkgSetting, realPkgName, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
- return scanPackageOnlyLI(request, mFactoryTest, currentTime);
+ return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime);
}
}
@@ -11216,20 +11251,70 @@
}
/**
+ * Applies the adjusted ABI calculated by
+ * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, PackageParser.Package)} 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,
+ PackageParser.Package scannedPackage, String adjustedAbi) {
+ if (scannedPackage != null) {
+ scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi;
+ }
+ List<String> changedAbiCodePath = null;
+ for (PackageSetting ps : sharedUserSetting.packages) {
+ if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
+ if (ps.primaryCpuAbiString != null) {
+ continue;
+ }
+
+ ps.primaryCpuAbiString = adjustedAbi;
+ if (ps.pkg != null && ps.pkg.applicationInfo != null
+ && !TextUtils.equals(
+ adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) {
+ ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
+ 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.codePathString);
+ }
+ }
+ }
+ return changedAbiCodePath;
+ }
+
+
+ /**
* Just scans the package without any side effects.
* <p>Not entirely true at the moment. There is still one side effect -- this
* method potentially modifies a live {@link PackageSetting} object representing
* the package being scanned. This will be resolved in the future.
*
+ * @param injector injector for acquiring dependencies
* @param request Information about the package to be scanned
* @param isUnderFactoryTest Whether or not the device is under factory test
* @param currentTime The current time, in millis
* @return The results of the scan
*/
@GuardedBy("mInstallLock")
- private static @NonNull ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+ @VisibleForTesting
+ @NonNull
+ static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+ Injector injector,
boolean isUnderFactoryTest, long currentTime)
- throws PackageManagerException {
+ throws PackageManagerException {
+ final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
+ final UserManagerInternal userManager = injector.getUserManager();
final PackageParser.Package pkg = request.pkg;
PackageSetting pkgSetting = request.pkgSetting;
final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
@@ -11335,7 +11420,7 @@
if (!createNewPackage) {
final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
- setInstantAppForUser(pkgSetting, userId, instantApp, fullApp);
+ setInstantAppForUser(userManager, pkgSetting, userId, instantApp, fullApp);
}
// TODO(patb): see if we can do away with disabled check here.
if (disabledPkgSetting != null
@@ -11381,7 +11466,10 @@
if (needToDeriveAbi) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
final boolean extractNativeLibs = !pkg.isLibrary();
- derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs);
+ final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
+ packageAbiHelper.derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs);
+ derivedAbi.first.applyTo(pkg);
+ derivedAbi.second.applyTo(pkg);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Some system apps still use directory structure for native libraries
@@ -11389,8 +11477,13 @@
// structure. Try to detect abi based on directory structure.
if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
pkg.applicationInfo.primaryCpuAbi == null) {
- setBundledAppAbisAndRoots(pkg, pkgSetting);
- setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+ final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
+ pkg);
+ abis.applyTo(pkg);
+ abis.applyTo(pkgSetting);
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir);
+ nativeLibraryPaths.applyTo(pkg);
}
} else {
// This is not a first boot or an upgrade, don't bother deriving the
@@ -11399,7 +11492,9 @@
pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;
pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;
- setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir);
+ nativeLibraryPaths.applyTo(pkg);
if (DEBUG_ABI_SELECTION) {
Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
@@ -11420,7 +11515,9 @@
// 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).
- setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir);
+ nativeLibraryPaths.applyTo(pkg);
}
// This is a special case for the "system" package, where the ABI is
@@ -11474,8 +11571,9 @@
// 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 =
- adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg);
+ changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, pkg,
+ packageAbiHelper.getAdjustedAbiForSharedUser(
+ pkgSetting.sharedUser.packages, pkg));
}
if (isUnderFactoryTest && pkg.requestedPermissions.contains(
@@ -12325,264 +12423,6 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- /**
- * Derive the ABI of a non-system package located at {@code scanFile}. This information
- * is derived purely on the basis of the contents of {@code scanFile} and
- * {@code cpuAbiOverride}.
- *
- * If {@code extractLibs} is true, native libraries are extracted from the app if required.
- */
- private static void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride,
- boolean extractLibs)
- throws PackageManagerException {
- // Give ourselves some initial paths; we'll come back for another
- // pass once we've determined ABI below.
- setNativeLibraryPaths(pkg, sAppLib32InstallDir);
-
- // We shouldn't attempt to extract libs from system app when it was not updated.
- if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
- extractLibs = false;
- }
-
- final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
- final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;
-
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(pkg);
- // TODO(multiArch): This can be null for apps that didn't go through the
- // usual installation process. We can calculate it again, like we
- // do during install time.
- //
- // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
- // unnecessary.
- final File nativeLibraryRoot = new File(nativeLibraryRootStr);
-
- // Null out the abis so that they can be recalculated.
- pkg.applicationInfo.primaryCpuAbi = null;
- pkg.applicationInfo.secondaryCpuAbi = null;
- if (isMultiArch(pkg.applicationInfo)) {
- // Warn if we've set an abiOverride for multi-lib packages..
- // By definition, we need to copy both 32 and 64 bit libraries for
- // such packages.
- if (pkg.cpuAbiOverride != null
- && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
- Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
- }
-
- int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
- int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
- if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
- if (extractLibs) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
- abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
- useIsaSpecificSubdirs);
- } else {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
- abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
- }
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- // Shared library native code should be in the APK zip aligned
- if (abi32 >= 0 && pkg.isLibrary() && extractLibs) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Shared library native lib extraction not supported");
- }
-
- maybeThrowExceptionForMultiArchCopy(
- "Error unpackaging 32 bit native libs for multiarch app.", abi32);
-
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
- if (extractLibs) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
- abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
- useIsaSpecificSubdirs);
- } else {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
- abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
- }
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- maybeThrowExceptionForMultiArchCopy(
- "Error unpackaging 64 bit native libs for multiarch app.", abi64);
-
- if (abi64 >= 0) {
- // Shared library native libs should be in the APK zip aligned
- if (extractLibs && pkg.isLibrary()) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Shared library native lib extraction not supported");
- }
- pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
- }
-
- if (abi32 >= 0) {
- final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
- if (abi64 >= 0) {
- if (pkg.use32bitAbi) {
- pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
- pkg.applicationInfo.primaryCpuAbi = abi;
- } else {
- pkg.applicationInfo.secondaryCpuAbi = abi;
- }
- } else {
- pkg.applicationInfo.primaryCpuAbi = abi;
- }
- }
- } else {
- String[] abiList = (cpuAbiOverride != null) ?
- new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
-
- // Enable gross and lame hacks for apps that are built with old
- // SDK tools. We must scan their APKs for renderscript bitcode and
- // not launch them if it's present. Don't bother checking on devices
- // that don't have 64 bit support.
- boolean needsRenderScriptOverride = false;
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
- NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
- abiList = Build.SUPPORTED_32_BIT_ABIS;
- needsRenderScriptOverride = true;
- }
-
- final int copyRet;
- if (extractLibs) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
- copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
- } else {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
- copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
- }
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
- if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Error unpackaging native libs for app, errorCode=" + copyRet);
- }
-
- if (copyRet >= 0) {
- // Shared libraries that have native libs must be multi-architecture
- if (pkg.isLibrary()) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Shared library with native libs must be multiarch");
- }
- pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
- } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
- pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
- } else if (needsRenderScriptOverride) {
- pkg.applicationInfo.primaryCpuAbi = abiList[0];
- }
- }
- } catch (IOException ioe) {
- Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
- } finally {
- IoUtils.closeQuietly(handle);
- }
-
- // Now that we've calculated the ABIs and determined if it's an internal app,
- // we will go ahead and populate the nativeLibraryPath.
- setNativeLibraryPaths(pkg, sAppLib32InstallDir);
- }
-
- /**
- * 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.
- */
- private static @Nullable List<String> adjustCpuAbisForSharedUserLPw(
- Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) {
- List<String> changedAbiCodePath = null;
- String requiredInstructionSet = null;
- if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
- requiredInstructionSet = VMRuntime.getInstructionSet(
- scannedPackage.applicationInfo.primaryCpuAbi);
- }
-
- 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.packageName.equals(ps.name)) {
- if (ps.primaryCpuAbiString == null) {
- continue;
- }
-
- final String instructionSet = VMRuntime.getInstructionSet(ps.primaryCpuAbiString);
- if (requiredInstructionSet != null && !instructionSet.equals(requiredInstructionSet)) {
- // 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(TAG, errorMessage);
- }
-
- if (requiredInstructionSet == null) {
- requiredInstructionSet = instructionSet;
- requirer = ps;
- }
- }
- }
-
- if (requiredInstructionSet != null) {
- 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;
- if (scannedPackage != null) {
- scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi;
- }
- } else {
- // requirer == null implies that we're updating all ABIs in the set to
- // match scannedPackage.
- adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi;
- }
-
- for (PackageSetting ps : packagesForUser) {
- if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
- if (ps.primaryCpuAbiString != null) {
- continue;
- }
-
- ps.primaryCpuAbiString = adjustedAbi;
- if (ps.pkg != null && ps.pkg.applicationInfo != null &&
- !TextUtils.equals(adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) {
- ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi
- + " (requirer="
- + (requirer != null ? requirer.pkg : "null")
- + ", scannedPackage="
- + (scannedPackage != null ? scannedPackage : "null")
- + ")");
- }
- if (changedAbiCodePath == null) {
- changedAbiCodePath = new ArrayList<>();
- }
- changedAbiCodePath.add(ps.codePathString);
- }
- }
- }
- }
- return changedAbiCodePath;
- }
-
private void setUpCustomResolverActivity(PackageParser.Package pkg) {
synchronized (mPackages) {
mResolverReplaced = true;
@@ -12634,207 +12474,6 @@
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
}
- private static String calculateBundledApkRoot(final String codePathString) {
- final File codePath = new File(codePathString);
- final File codeRoot;
- if (FileUtils.contains(Environment.getRootDirectory(), codePath)) {
- codeRoot = Environment.getRootDirectory();
- } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) {
- codeRoot = Environment.getOemDirectory();
- } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) {
- codeRoot = Environment.getVendorDirectory();
- } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
- codeRoot = Environment.getOdmDirectory();
- } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) {
- codeRoot = Environment.getProductDirectory();
- } else if (FileUtils.contains(Environment.getSystemExtDirectory(), codePath)) {
- codeRoot = Environment.getSystemExtDirectory();
- } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
- codeRoot = Environment.getOdmDirectory();
- } else {
- // Unrecognized code path; take its top real segment as the apk root:
- // e.g. /something/app/blah.apk => /something
- try {
- File f = codePath.getCanonicalFile();
- File parent = f.getParentFile(); // non-null because codePath is a file
- File tmp;
- while ((tmp = parent.getParentFile()) != null) {
- f = parent;
- parent = tmp;
- }
- codeRoot = f;
- Slog.w(TAG, "Unrecognized code path "
- + codePath + " - using " + codeRoot);
- } catch (IOException e) {
- // Can't canonicalize the code path -- shenanigans?
- Slog.w(TAG, "Can't canonicalize code path " + codePath);
- return Environment.getRootDirectory().getPath();
- }
- }
- return codeRoot.getPath();
- }
-
- /**
- * Derive and set the location of native libraries for the given package,
- * which varies depending on where and how the package was installed.
- */
- private static void setNativeLibraryPaths(PackageParser.Package pkg, File appLib32InstallDir) {
- final ApplicationInfo info = pkg.applicationInfo;
- final String codePath = pkg.codePath;
- final File codeFile = new File(codePath);
- final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
-
- info.nativeLibraryRootDir = null;
- info.nativeLibraryRootRequiresIsa = false;
- info.nativeLibraryDir = null;
- info.secondaryNativeLibraryDir = null;
-
- if (isApkFile(codeFile)) {
- // Monolithic install
- if (bundledApp) {
- // If "/system/lib64/apkname" exists, assume that is the per-package
- // native library directory to use; otherwise use "/system/lib/apkname".
- final String apkRoot = calculateBundledApkRoot(info.sourceDir);
- final boolean is64Bit = VMRuntime.is64BitInstructionSet(
- getPrimaryInstructionSet(info));
-
- // This is a bundled system app so choose the path based on the ABI.
- // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
- // is just the default path.
- final String apkName = deriveCodePathName(codePath);
- final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
- info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
- apkName).getAbsolutePath();
-
- if (info.secondaryCpuAbi != null) {
- final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
- info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
- secondaryLibDir, apkName).getAbsolutePath();
- }
- } else {
- final String apkName = deriveCodePathName(codePath);
- info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
- .getAbsolutePath();
- }
-
- info.nativeLibraryRootRequiresIsa = false;
- info.nativeLibraryDir = info.nativeLibraryRootDir;
- } else {
- // Cluster install
- info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
- info.nativeLibraryRootRequiresIsa = true;
-
- info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
- getPrimaryInstructionSet(info)).getAbsolutePath();
-
- if (info.secondaryCpuAbi != null) {
- info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
- VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath();
- }
- }
- }
-
- /**
- * Calculate the abis and roots for a bundled app. These can uniquely
- * be determined from the contents of the system partition, i.e whether
- * it contains 64 or 32 bit shared libraries etc. We do not validate any
- * of this information, and instead assume that the system was built
- * sensibly.
- */
- private static void setBundledAppAbisAndRoots(PackageParser.Package pkg,
- PackageSetting pkgSetting) {
- final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
-
- // If "/system/lib64/apkname" exists, assume that is the per-package
- // native library directory to use; otherwise use "/system/lib/apkname".
- final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);
- setBundledAppAbi(pkg, apkRoot, apkName);
- // pkgSetting might be null during rescan following uninstall of updates
- // to a bundled app, so accommodate that possibility. The settings in
- // that case will be established later from the parsed package.
- //
- // If the settings aren't null, sync them up with what we've just derived.
- // note that apkRoot isn't stored in the package settings.
- if (pkgSetting != null) {
- pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
- pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
- }
- }
-
- /**
- * Deduces the ABI of a bundled app and sets the relevant fields on the
- * parsed pkg object.
- *
- * @param apkRoot the root of the installed apk, something like {@code /system} or {@code /oem}
- * under which system libraries are installed.
- * @param apkName the name of the installed package.
- */
- private static void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) {
- final File codeFile = new File(pkg.codePath);
-
- final boolean has64BitLibs;
- final boolean has32BitLibs;
- if (isApkFile(codeFile)) {
- // Monolithic install
- has64BitLibs = (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists();
- has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists();
- } else {
- // Cluster install
- final File rootDir = new File(codeFile, LIB_DIR_NAME);
- if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS)
- && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) {
- final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]);
- has64BitLibs = (new File(rootDir, isa)).exists();
- } else {
- has64BitLibs = false;
- }
- if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS)
- && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) {
- final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]);
- has32BitLibs = (new File(rootDir, isa)).exists();
- } else {
- has32BitLibs = false;
- }
- }
-
- if (has64BitLibs && !has32BitLibs) {
- // The package has 64 bit libs, but not 32 bit libs. Its primary
- // ABI should be 64 bit. We can safely assume here that the bundled
- // native libraries correspond to the most preferred ABI in the list.
-
- pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
- pkg.applicationInfo.secondaryCpuAbi = null;
- } else if (has32BitLibs && !has64BitLibs) {
- // The package has 32 bit libs but not 64 bit libs. Its primary
- // ABI should be 32 bit.
-
- pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
- pkg.applicationInfo.secondaryCpuAbi = null;
- } else if (has32BitLibs && has64BitLibs) {
- // The application has both 64 and 32 bit bundled libraries. We check
- // here that the app declares multiArch support, and warn if it doesn't.
- //
- // We will be lenient here and record both ABIs. The primary will be the
- // ABI that's higher on the list, i.e, a device that's configured to prefer
- // 64 bit apps will see a 64 bit primary ABI,
-
- if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) {
- Slog.e(TAG, "Package " + pkg + " has multiple bundled libs, but is not multiarch.");
- }
-
- if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) {
- pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
- pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
- } else {
- pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
- pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
- }
- } else {
- pkg.applicationInfo.primaryCpuAbi = null;
- pkg.applicationInfo.secondaryCpuAbi = null;
- }
- }
-
private void killApplication(String pkgName, int appId, String reason) {
killApplication(pkgName, appId, UserHandle.USER_ALL, reason);
}
@@ -13556,7 +13195,8 @@
// upgrade app from instant to full; we don't allow app downgrade
installed = true;
}
- setInstantAppForUser(pkgSetting, userId, instantApp, fullApp);
+ setInstantAppForUser(
+ getUserManagerInternal(), pkgSetting, userId, instantApp, fullApp);
}
if (installed) {
@@ -13604,8 +13244,8 @@
}
}
- static void setInstantAppForUser(PackageSetting pkgSetting, int userId,
- boolean instantApp, boolean fullApp) {
+ static void setInstantAppForUser(UserManagerInternal userManager, PackageSetting pkgSetting,
+ int userId, boolean instantApp, boolean fullApp) {
// no state specified; do nothing
if (!instantApp && !fullApp) {
return;
@@ -13617,7 +13257,7 @@
pkgSetting.setInstantApp(false /*instantApp*/, userId);
}
} else {
- for (int currentUserId : sUserManager.getUserIds()) {
+ for (int currentUserId : userManager.getUserIds()) {
if (instantApp && !pkgSetting.getInstantApp(currentUserId)) {
pkgSetting.setInstantApp(true /*instantApp*/, currentUserId);
} else if (fullApp && pkgSetting.getInstantApp(currentUserId)) {
@@ -15891,16 +15531,6 @@
}
}
- private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws
- PackageManagerException {
- if (copyRet < 0) {
- if (copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
- copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
- throw new PackageManagerException(copyRet, message);
- }
- }
- }
-
/**
* Logic to handle movement of existing installed applications.
*/
@@ -16027,26 +15657,6 @@
return result;
}
- // Utility method that returns the relative package path with respect
- // to the installation directory. Like say for /data/data/com.test-1.apk
- // string com.test-1 is returned.
- static String deriveCodePathName(String codePath) {
- if (codePath == null) {
- return null;
- }
- final File codeFile = new File(codePath);
- final String name = codeFile.getName();
- if (codeFile.isDirectory()) {
- return name;
- } else if (name.endsWith(".apk") || name.endsWith(".tmp")) {
- final int lastDot = name.lastIndexOf('.');
- return name.substring(0, lastDot);
- } else {
- Slog.w(TAG, "Odd, " + codePath + " doesn't look like an APK");
- return null;
- }
- }
-
static class PackageInstalledInfo {
String name;
int uid;
@@ -16979,7 +16589,8 @@
final PrepareResult prepareResult;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
- prepareResult = preparePackageLI(request.args, request.installResult);
+ prepareResult =
+ preparePackageLI(request.args, request.installResult);
} catch (PrepareFailure prepareFailure) {
request.installResult.setError(prepareFailure.error,
prepareFailure.getMessage());
@@ -17639,7 +17250,11 @@
String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
args.abiOverride : pkg.cpuAbiOverride);
final boolean extractNativeLibs = !pkg.isLibrary();
- derivePackageAbi(pkg, abiOverride, extractNativeLibs);
+ final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
+ derivedAbi = mInjector.getAbiHelper().derivePackageAbi(
+ pkg, abiOverride, extractNativeLibs);
+ derivedAbi.first.applyTo(pkg);
+ derivedAbi.second.applyTo(pkg);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
@@ -18168,10 +17783,6 @@
}
}
- private static boolean isMultiArch(ApplicationInfo info) {
- return (info.flags & ApplicationInfo.FLAG_MULTIARCH) != 0;
- }
-
private static boolean isExternal(PackageParser.Package pkg) {
return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
@@ -18180,7 +17791,7 @@
return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
- private static boolean isSystemApp(PackageParser.Package pkg) {
+ static boolean isSystemApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 58f262c..029673f 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -270,7 +270,8 @@
updateAvailable = orig.updateAvailable;
}
- private PackageUserState modifyUserState(int userId) {
+ @VisibleForTesting
+ PackageUserState modifyUserState(int userId) {
PackageUserState state = mUserState.get(userId);
if (state == null) {
state = new PackageUserState();
@@ -463,6 +464,18 @@
state.harmfulAppWarning = harmfulAppWarning;
}
+ void setUserState(int userId, PackageUserState otherState) {
+ setUserState(userId, otherState.ceDataInode, otherState.enabled, otherState.installed,
+ otherState.stopped, otherState.notLaunched, otherState.hidden,
+ otherState.distractionFlags, otherState.suspended, otherState.suspendingPackage,
+ otherState.dialogInfo, otherState.suspendedAppExtras,
+ otherState.suspendedLauncherExtras, otherState.instantApp,
+ otherState.virtualPreload, otherState.lastDisableAppCaller,
+ otherState.enabledComponents, otherState.disabledComponents,
+ otherState.domainVerificationStatus, otherState.appLinkGeneration,
+ otherState.installReason, otherState.harmfulAppWarning);
+ }
+
ArraySet<String> getEnabledComponents(int userId) {
return readUserState(userId).enabledComponents;
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 15dc6ae..0101366 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -8,6 +8,20 @@
},
{
"name": "CtsCompilationTestCases"
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.pm."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
],
"imports": [
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java
new file mode 100644
index 0000000..470d4fa
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.PackageParser;
+
+import com.android.internal.util.ArrayUtils;
+
+class PackageBuilder {
+ final PackageParser.Package mPkg;
+
+ PackageBuilder(String packageName) {
+ mPkg = new PackageParser.Package(packageName);
+ }
+
+ PackageBuilder setApplicationInfoCodePath(String codePath) {
+ mPkg.applicationInfo.setCodePath(codePath);
+ return this;
+ }
+
+ PackageBuilder setApplicationInfoResourcePath(String resourcePath) {
+ mPkg.applicationInfo.setResourcePath(resourcePath);
+ return this;
+ }
+
+ PackageBuilder setCodePath(String codePath) {
+ mPkg.codePath = codePath;
+ return this;
+ }
+
+ PackageBuilder setBaseCodePath(String baseCodePath) {
+ mPkg.baseCodePath = baseCodePath;
+ return this;
+ }
+
+ PackageBuilder addUsesStaticLibrary(String name, long version) {
+ mPkg.usesStaticLibraries = ArrayUtils.add(mPkg.usesStaticLibraries, name);
+ mPkg.usesStaticLibrariesVersions =
+ ArrayUtils.appendLong(mPkg.usesStaticLibrariesVersions, version);
+ return this;
+ }
+
+ PackageBuilder setApplicationInfoNativeLibraryRootDir(String dir) {
+ mPkg.applicationInfo.nativeLibraryRootDir = dir;
+ return this;
+ }
+
+ PackageBuilder setStaticSharedLib(String staticSharedLibName, long staticSharedLibVersion) {
+ mPkg.staticSharedLibVersion = staticSharedLibVersion;
+ mPkg.staticSharedLibName = staticSharedLibName;
+ return this;
+ }
+
+ PackageBuilder setManifestPackageName(String manifestPackageName) {
+ mPkg.manifestPackageName = manifestPackageName;
+ return this;
+ }
+
+ PackageBuilder setVersionCodeMajor(int versionCodeMajor) {
+ mPkg.mVersionCodeMajor = versionCodeMajor;
+ return this;
+ }
+
+ PackageBuilder setVersionCode(int versionCode) {
+ mPkg.mVersionCode = versionCode;
+ return this;
+ }
+
+ PackageBuilder addSplitCodePath(String splitCodePath) {
+ mPkg.splitCodePaths =
+ ArrayUtils.appendElement(String.class, mPkg.splitCodePaths, splitCodePath);
+ return this;
+ }
+
+ PackageBuilder setApplicationInfoVolumeUuid(String volumeUuid) {
+ mPkg.applicationInfo.volumeUuid = volumeUuid;
+ return this;
+ }
+
+ PackageBuilder addLibraryName(String libraryName) {
+ mPkg.libraryNames = ArrayUtils.add(mPkg.libraryNames, libraryName);
+ return this;
+ }
+
+ PackageBuilder setRealPackageName(String realPackageName) {
+ mPkg.mRealPackage = realPackageName;
+ return this;
+ }
+
+ PackageBuilder setCpuAbiOVerride(String cpuAbiOverride) {
+ mPkg.cpuAbiOverride = cpuAbiOverride;
+ return this;
+ }
+
+ PackageBuilder addPermissionRequest(String permissionName) {
+ mPkg.requestedPermissions.add(permissionName);
+ return this;
+ }
+
+ PackageParser.Package build() {
+ return mPkg;
+ }
+
+ public PackageBuilder addApplicationInfoFlag(int flag) {
+ mPkg.applicationInfo.flags |= flag;
+ return this;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
new file mode 100644
index 0000000..b42cfd8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.PackageUserState;
+import android.util.SparseArray;
+
+import java.io.File;
+import java.util.List;
+
+class PackageSettingBuilder {
+ private String mName;
+ private String mRealName;
+ private String mCodePath;
+ private String mResourcePath;
+ private String mLegacyNativeLibraryPathString;
+ private String mPrimaryCpuAbiString;
+ private String mSecondaryCpuAbiString;
+ private String mCpuAbiOverrideString;
+ private long mPVersionCode;
+ private int mPkgFlags;
+ private int mPrivateFlags;
+ private String mParentPackageName;
+ private List<String> mChildPackageNames;
+ private int mSharedUserId;
+ private String[] mUsesStaticLibraries;
+ private long[] mUsesStaticLibrariesVersions;
+ private String mVolumeUuid;
+ private SparseArray<PackageUserState> mUserStates = new SparseArray<>();
+
+ public PackageSettingBuilder setName(String name) {
+ this.mName = name;
+ return this;
+ }
+
+ public PackageSettingBuilder setRealName(String realName) {
+ this.mRealName = realName;
+ return this;
+ }
+
+ public PackageSettingBuilder setCodePath(String codePath) {
+ this.mCodePath = codePath;
+ return this;
+ }
+
+ public PackageSettingBuilder setResourcePath(String resourcePath) {
+ this.mResourcePath = resourcePath;
+ return this;
+ }
+
+ public PackageSettingBuilder setLegacyNativeLibraryPathString(
+ String legacyNativeLibraryPathString) {
+ this.mLegacyNativeLibraryPathString = legacyNativeLibraryPathString;
+ return this;
+ }
+
+ public PackageSettingBuilder setPrimaryCpuAbiString(String primaryCpuAbiString) {
+ this.mPrimaryCpuAbiString = primaryCpuAbiString;
+ return this;
+ }
+
+ public PackageSettingBuilder setSecondaryCpuAbiString(String secondaryCpuAbiString) {
+ this.mSecondaryCpuAbiString = secondaryCpuAbiString;
+ return this;
+ }
+
+ public PackageSettingBuilder setCpuAbiOverrideString(String cpuAbiOverrideString) {
+ this.mCpuAbiOverrideString = cpuAbiOverrideString;
+ return this;
+ }
+
+ public PackageSettingBuilder setPVersionCode(long pVersionCode) {
+ this.mPVersionCode = pVersionCode;
+ return this;
+ }
+
+ public PackageSettingBuilder setPkgFlags(int pkgFlags) {
+ this.mPkgFlags = pkgFlags;
+ return this;
+ }
+
+ public PackageSettingBuilder setPrivateFlags(int privateFlags) {
+ this.mPrivateFlags = privateFlags;
+ return this;
+ }
+
+ public PackageSettingBuilder setParentPackageName(String parentPackageName) {
+ this.mParentPackageName = parentPackageName;
+ return this;
+ }
+
+ public PackageSettingBuilder setChildPackageNames(List<String> childPackageNames) {
+ this.mChildPackageNames = childPackageNames;
+ return this;
+ }
+
+ public PackageSettingBuilder setSharedUserId(int sharedUserId) {
+ this.mSharedUserId = sharedUserId;
+ return this;
+ }
+
+ public PackageSettingBuilder setUsesStaticLibraries(String[] usesStaticLibraries) {
+ this.mUsesStaticLibraries = usesStaticLibraries;
+ return this;
+ }
+
+ public PackageSettingBuilder setUsesStaticLibrariesVersions(
+ long[] usesStaticLibrariesVersions) {
+ this.mUsesStaticLibrariesVersions = usesStaticLibrariesVersions;
+ return this;
+ }
+
+ public PackageSettingBuilder setVolumeUuid(String volumeUuid) {
+ this.mVolumeUuid = volumeUuid;
+ return this;
+ }
+
+ public PackageSettingBuilder setInstantAppUserState(int userId, boolean isInstant) {
+ if (mUserStates.indexOfKey(userId) < 0) {
+ mUserStates.put(userId, new PackageUserState());
+ }
+ mUserStates.get(userId).instantApp = isInstant;
+ return this;
+ }
+
+ public PackageSetting build() {
+ final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
+ new File(mCodePath), new File(mResourcePath),
+ mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString,
+ mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mParentPackageName,
+ mChildPackageNames, mSharedUserId, mUsesStaticLibraries,
+ mUsesStaticLibrariesVersions);
+ packageSetting.volumeUuid = this.mVolumeUuid;
+ for (int i = 0; i < mUserStates.size(); i++) {
+ packageSetting.setUserState(mUserStates.keyAt(i), mUserStates.valueAt(i));
+ }
+ return packageSetting;
+
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
new file mode 100644
index 0000000..34a3f86
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.PackageParser;
+import android.os.UserHandle;
+
+class ScanRequestBuilder {
+ private final PackageParser.Package mPkg;
+ private PackageParser.Package mOldPkg;
+ private SharedUserSetting mSharedUserSetting;
+ private PackageSetting mPkgSetting;
+ private PackageSetting mDisabledPkgSetting;
+ private PackageSetting mOriginalPkgSetting;
+ private String mRealPkgName;
+ private int mParseFlags;
+ private int mScanFlags;
+ private UserHandle mUser;
+ private boolean mIsPlatformPackage;
+
+ ScanRequestBuilder(PackageParser.Package pkg) {
+ this.mPkg = pkg;
+ }
+
+ public ScanRequestBuilder setOldPkg(PackageParser.Package oldPkg) {
+ this.mOldPkg = oldPkg;
+ return this;
+ }
+
+ public ScanRequestBuilder setSharedUserSetting(SharedUserSetting sharedUserSetting) {
+ this.mSharedUserSetting = sharedUserSetting;
+ return this;
+ }
+
+ public ScanRequestBuilder setPkgSetting(PackageSetting pkgSetting) {
+ this.mPkgSetting = pkgSetting;
+ return this;
+ }
+
+ public ScanRequestBuilder setDisabledPkgSetting(PackageSetting disabledPkgSetting) {
+ this.mDisabledPkgSetting = disabledPkgSetting;
+ return this;
+ }
+
+ public ScanRequestBuilder setOriginalPkgSetting(PackageSetting originalPkgSetting) {
+ this.mOriginalPkgSetting = originalPkgSetting;
+ return this;
+ }
+
+ public ScanRequestBuilder setRealPkgName(String realPkgName) {
+ this.mRealPkgName = realPkgName;
+ return this;
+ }
+
+ public ScanRequestBuilder setParseFlags(int parseFlags) {
+ this.mParseFlags = parseFlags;
+ return this;
+ }
+
+ public ScanRequestBuilder addParseFlag(int parseFlag) {
+ this.mParseFlags |= parseFlag;
+ return this;
+ }
+
+ public ScanRequestBuilder setScanFlags(int scanFlags) {
+ this.mScanFlags = scanFlags;
+ return this;
+ }
+
+ public ScanRequestBuilder addScanFlag(int scanFlag) {
+ this.mScanFlags |= scanFlag;
+ return this;
+ }
+
+ public ScanRequestBuilder setUser(UserHandle user) {
+ this.mUser = user;
+ return this;
+ }
+
+ public ScanRequestBuilder setIsPlatformPackage(boolean isPlatformPackage) {
+ this.mIsPlatformPackage = isPlatformPackage;
+ return this;
+ }
+
+ PackageManagerService.ScanRequest build() {
+ return new PackageManagerService.ScanRequest(
+ mPkg, mSharedUserSetting, mOldPkg, mPkgSetting, mDisabledPkgSetting,
+ mOriginalPkgSetting, mRealPkgName, mParseFlags, mScanFlags, mIsPlatformPackage,
+ mUser);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
new file mode 100644
index 0000000..dd3d8b9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.SharedLibraryInfo.TYPE_DYNAMIC;
+import static android.content.pm.SharedLibraryInfo.TYPE_STATIC;
+import static android.content.pm.SharedLibraryInfo.VERSION_UNDEFINED;
+
+import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
+import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.collection.IsArrayContainingInOrder.arrayContaining;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertNotSame;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.SharedLibraryInfo;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.os.UserManagerInternal;
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.File;
+
+@RunWith(MockitoJUnitRunner.class)
+@Presubmit
+// TODO: shared user tests
+public class ScanTests {
+
+ private static final String DUMMY_PACKAGE_NAME = "some.app.to.test";
+
+ @Mock
+ PackageAbiHelper mMockPackageAbiHelper;
+ @Mock
+ UserManagerInternal mMockUserManager;
+
+ @Before
+ public void setupDefaultUser() {
+ when(mMockUserManager.getUserIds()).thenReturn(new int[]{0});
+ }
+
+ @Before
+ public void setupDefaultAbiBehavior() throws Exception {
+ when(mMockPackageAbiHelper.derivePackageAbi(
+ any(PackageParser.Package.class), nullable(String.class), anyBoolean()))
+ .thenReturn(new Pair<>(
+ new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"),
+ new PackageAbiHelper.NativeLibraryPaths(
+ "derivedRootDir", true, "derivedNativeDir", "derivedNativeDir2")));
+ when(mMockPackageAbiHelper.getNativeLibraryPaths(
+ any(PackageParser.Package.class), any(File.class)))
+ .thenReturn(new PackageAbiHelper.NativeLibraryPaths(
+ "getRootDir", true, "getNativeDir", "getNativeDir2"
+ ));
+ when(mMockPackageAbiHelper.getBundledAppAbis(
+ any(PackageParser.Package.class)))
+ .thenReturn(new PackageAbiHelper.Abis("bundledPrimary", "bundledSecondary"));
+ }
+
+ @Test
+ public void newInstallSimpleAllNominal() throws Exception {
+ final PackageManagerService.ScanRequest scanRequest =
+ createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+ .addScanFlag(PackageManagerService.SCAN_NEW_INSTALL)
+ .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
+ .build();
+
+ final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+ assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/);
+ assertThat(scanResult.existingSettingCopied, is(false));
+ assertPathsNotDerived(scanResult);
+ }
+
+ @Test
+ public void newInstallForAllUsers() throws Exception {
+ final int[] userIds = {0, 10, 11};
+ when(mMockUserManager.getUserIds()).thenReturn(userIds);
+
+ final PackageManagerService.ScanRequest scanRequest =
+ createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+ .setRealPkgName(null)
+ .addScanFlag(PackageManagerService.SCAN_NEW_INSTALL)
+ .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
+ .build();
+ final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+ for (int uid : userIds) {
+ assertThat(scanResult.pkgSetting.readUserState(uid).installed, is(true));
+ }
+ }
+
+ @Test
+ public void installRealPackageName() throws Exception {
+ final PackageManagerService.ScanRequest scanRequest =
+ createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+ .setRealPkgName("com.package.real")
+ .build();
+
+ final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+ assertThat(scanResult.pkgSetting.realName, is("com.package.real"));
+
+ final PackageManagerService.ScanRequest scanRequestNoRealPkg =
+ createBasicScanRequestBuilder(
+ createBasicPackage(DUMMY_PACKAGE_NAME)
+ .setRealPackageName("com.package.real").build())
+ .build();
+
+ final PackageManagerService.ScanResult scanResultNoReal = executeScan(scanRequestNoRealPkg);
+ assertThat(scanResultNoReal.pkgSetting.realName, nullValue());
+ }
+
+ @Test
+ public void updateSimpleNominal() throws Exception {
+ when(mMockUserManager.getUserIds()).thenReturn(new int[]{0});
+
+ final PackageSetting pkgSetting = createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+ .setPrimaryCpuAbiString("primaryCpuAbi")
+ .setSecondaryCpuAbiString("secondaryCpuAbi")
+ .build();
+ final PackageManagerService.ScanRequest scanRequest =
+ createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+ .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
+ .setPkgSetting(pkgSetting)
+ .build();
+
+
+ final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+ assertThat(scanResult.existingSettingCopied, is(true));
+
+ // ensure we don't overwrite the existing pkgSetting, in case something post-scan fails
+ assertNotSame(pkgSetting, scanResult.pkgSetting);
+
+ assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/);
+
+ assertThat(scanResult.pkgSetting.primaryCpuAbiString, is("primaryCpuAbi"));
+ assertThat(scanResult.pkgSetting.secondaryCpuAbiString, is("secondaryCpuAbi"));
+ assertThat(scanResult.pkgSetting.cpuAbiOverrideString, nullValue());
+
+ assertPathsNotDerived(scanResult);
+ }
+
+ @Test
+ public void updateInstantSimpleNominal() throws Exception {
+ when(mMockUserManager.getUserIds()).thenReturn(new int[]{0});
+
+ final PackageSetting existingPkgSetting =
+ createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+ .setInstantAppUserState(0, true)
+ .build();
+
+ final PackageManagerService.ScanRequest scanRequest =
+ createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+ .setPkgSetting(existingPkgSetting)
+ .build();
+
+
+ final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+ assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, true /*isInstant*/);
+ }
+
+ @Test
+ public void installStaticSharedLibrary() throws Exception {
+ final PackageParser.Package pkg = createBasicPackage("static.lib.pkg.123")
+ .setStaticSharedLib("static.lib", 123L)
+ .setManifestPackageName("static.lib.pkg")
+ .setVersionCodeMajor(1)
+ .setVersionCode(234)
+ .setBaseCodePath("/some/path.apk")
+ .addSplitCodePath("/some/other/path.apk")
+ .build();
+
+ final PackageManagerService.ScanRequest scanRequest = new ScanRequestBuilder(
+ pkg).setUser(UserHandle.of(0)).build();
+
+
+ final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+ assertThat(scanResult.staticSharedLibraryInfo.getPackageName(), is("static.lib.pkg.123"));
+ assertThat(scanResult.staticSharedLibraryInfo.getName(), is("static.lib"));
+ assertThat(scanResult.staticSharedLibraryInfo.getLongVersion(), is(123L));
+ assertThat(scanResult.staticSharedLibraryInfo.getType(), is(TYPE_STATIC));
+ assertThat(scanResult.staticSharedLibraryInfo.getDeclaringPackage().getPackageName(),
+ is("static.lib.pkg"));
+ assertThat(scanResult.staticSharedLibraryInfo.getDeclaringPackage().getLongVersionCode(),
+ is(pkg.getLongVersionCode()));
+ assertThat(scanResult.staticSharedLibraryInfo.getAllCodePaths(),
+ hasItems("/some/path.apk", "/some/other/path.apk"));
+ assertThat(scanResult.staticSharedLibraryInfo.getDependencies(), nullValue());
+ assertThat(scanResult.staticSharedLibraryInfo.getDependentPackages(), empty());
+ }
+
+ @Test
+ public void installDynamicLibraries() throws Exception {
+ final PackageParser.Package pkg = createBasicPackage("dynamic.lib.pkg")
+ .setManifestPackageName("dynamic.lib.pkg")
+ .addLibraryName("liba")
+ .addLibraryName("libb")
+ .setVersionCodeMajor(1)
+ .setVersionCode(234)
+ .setBaseCodePath("/some/path.apk")
+ .addSplitCodePath("/some/other/path.apk")
+ .build();
+
+ final PackageManagerService.ScanRequest scanRequest =
+ new ScanRequestBuilder(pkg).setUser(UserHandle.of(0)).build();
+
+
+ final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+ final SharedLibraryInfo dynamicLib0 = scanResult.dynamicSharedLibraryInfos.get(0);
+ assertThat(dynamicLib0.getPackageName(), is("dynamic.lib.pkg"));
+ assertThat(dynamicLib0.getName(), is("liba"));
+ assertThat(dynamicLib0.getLongVersion(), is((long) VERSION_UNDEFINED));
+ assertThat(dynamicLib0.getType(), is(TYPE_DYNAMIC));
+ assertThat(dynamicLib0.getDeclaringPackage().getPackageName(), is("dynamic.lib.pkg"));
+ assertThat(dynamicLib0.getDeclaringPackage().getLongVersionCode(),
+ is(pkg.getLongVersionCode()));
+ assertThat(dynamicLib0.getAllCodePaths(),
+ hasItems("/some/path.apk", "/some/other/path.apk"));
+ assertThat(dynamicLib0.getDependencies(), nullValue());
+ assertThat(dynamicLib0.getDependentPackages(), empty());
+
+ final SharedLibraryInfo dynamicLib1 = scanResult.dynamicSharedLibraryInfos.get(1);
+ assertThat(dynamicLib1.getPackageName(), is("dynamic.lib.pkg"));
+ assertThat(dynamicLib1.getName(), is("libb"));
+ assertThat(dynamicLib1.getLongVersion(), is((long) VERSION_UNDEFINED));
+ assertThat(dynamicLib1.getType(), is(TYPE_DYNAMIC));
+ assertThat(dynamicLib1.getDeclaringPackage().getPackageName(), is("dynamic.lib.pkg"));
+ assertThat(dynamicLib1.getDeclaringPackage().getLongVersionCode(),
+ is(pkg.getLongVersionCode()));
+ assertThat(dynamicLib1.getAllCodePaths(),
+ hasItems("/some/path.apk", "/some/other/path.apk"));
+ assertThat(dynamicLib1.getDependencies(), nullValue());
+ assertThat(dynamicLib1.getDependentPackages(), empty());
+ }
+
+ @Test
+ public void volumeUuidChangesOnUpdate() throws Exception {
+ final PackageSetting pkgSetting =
+ createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+ .setVolumeUuid("someUuid")
+ .build();
+
+ final PackageParser.Package basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
+ .setApplicationInfoVolumeUuid("someNewUuid")
+ .build();
+
+
+ final PackageManagerService.ScanResult scanResult = executeScan(
+ new ScanRequestBuilder(basicPackage).setPkgSetting(pkgSetting).build());
+
+ assertThat(scanResult.pkgSetting.volumeUuid, is("someNewUuid"));
+ }
+
+ @Test
+ public void scanFirstBoot_derivesAbis() throws Exception {
+ final PackageSetting pkgSetting =
+ createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME).build();
+
+ final PackageParser.Package basicPackage =
+ createBasicPackage(DUMMY_PACKAGE_NAME)
+ .setCpuAbiOVerride("testOverride")
+ .build();
+
+
+ final PackageManagerService.ScanResult scanResult = executeScan(new ScanRequestBuilder(
+ basicPackage)
+ .setPkgSetting(pkgSetting)
+ .addScanFlag(SCAN_FIRST_BOOT_OR_UPGRADE)
+ .build());
+
+ assertAbiAndPathssDerived(scanResult);
+ }
+
+ @Test
+ public void scanWithOriginalPkgSetting_packageNameChanges() throws Exception {
+ final PackageSetting originalPkgSetting =
+ createBasicPackageSettingBuilder("original.package").build();
+
+ final PackageParser.Package basicPackage =
+ createBasicPackage(DUMMY_PACKAGE_NAME)
+ .build();
+
+
+ final PackageManagerService.ScanResult result =
+ executeScan(new ScanRequestBuilder(basicPackage)
+ .setOriginalPkgSetting(originalPkgSetting)
+ .build());
+
+ assertThat(result.request.pkg.packageName, is("original.package"));
+ }
+
+ @Test
+ public void updateInstant_changeToFull() throws Exception {
+ when(mMockUserManager.getUserIds()).thenReturn(new int[]{0});
+
+ final PackageSetting existingPkgSetting =
+ createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+ .setInstantAppUserState(0, true)
+ .build();
+
+ final PackageManagerService.ScanRequest scanRequest =
+ createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+ .setPkgSetting(existingPkgSetting)
+ .addScanFlag(SCAN_AS_FULL_APP)
+ .build();
+
+
+ final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+ assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/);
+ }
+
+ @Test
+ public void updateFull_changeToInstant() throws Exception {
+ when(mMockUserManager.getUserIds()).thenReturn(new int[]{0});
+
+ final PackageSetting existingPkgSetting =
+ createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+ .setInstantAppUserState(0, false)
+ .build();
+
+ final PackageManagerService.ScanRequest scanRequest =
+ createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+ .setPkgSetting(existingPkgSetting)
+ .addScanFlag(SCAN_AS_INSTANT_APP)
+ .build();
+
+
+ final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+ assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, true /*isInstant*/);
+ }
+
+ @Test
+ public void updateSystemApp_applicationInfoFlagSet() throws Exception {
+ final PackageSetting existingPkgSetting =
+ createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
+ .build();
+
+ final PackageManagerService.ScanRequest scanRequest =
+ createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+ .setPkgSetting(existingPkgSetting)
+ .setDisabledPkgSetting(existingPkgSetting)
+ .addScanFlag(SCAN_NEW_INSTALL)
+ .build();
+
+ final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+ assertThat(scanResult.request.pkg.applicationInfo.flags,
+ hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
+ }
+
+ @Test
+ public void factoryTestFlagSet() throws Exception {
+ final PackageParser.Package basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
+ .addPermissionRequest(Manifest.permission.FACTORY_TEST)
+ .build();
+
+ final PackageManagerService.ScanResult scanResult = PackageManagerService.scanPackageOnlyLI(
+ createBasicScanRequestBuilder(basicPackage).build(),
+ new PackageManagerService.Injector(mMockUserManager, mMockPackageAbiHelper),
+ true /*isUnderFactoryTest*/,
+ System.currentTimeMillis());
+
+ assertThat(scanResult.request.pkg.applicationInfo.flags,
+ hasFlag(ApplicationInfo.FLAG_FACTORY_TEST));
+ }
+
+ @Test
+ public void scanSystemApp_isOrphanedTrue() throws Exception {
+ final PackageParser.Package pkg = createBasicPackage(DUMMY_PACKAGE_NAME)
+ .addApplicationInfoFlag(ApplicationInfo.FLAG_SYSTEM)
+ .build();
+
+ final PackageManagerService.ScanRequest scanRequest =
+ createBasicScanRequestBuilder(pkg)
+ .build();
+
+ final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+ assertThat(scanResult.pkgSetting.isOrphaned, is(true));
+ }
+
+ private static Matcher<Integer> hasFlag(final int flag) {
+ return new BaseMatcher<Integer>() {
+ @Override public void describeTo(Description description) {
+ description.appendText("flags ");
+ }
+
+ @Override public boolean matches(Object item) {
+ return ((int) item & flag) != 0;
+ }
+
+ @Override
+ public void describeMismatch(Object item, Description mismatchDescription) {
+ mismatchDescription
+ .appendValue(item)
+ .appendText(" does not contain flag ")
+ .appendValue(flag);
+ }
+ };
+ }
+
+ private PackageManagerService.ScanResult executeScan(
+ PackageManagerService.ScanRequest scanRequest) throws PackageManagerException {
+ return PackageManagerService.scanPackageOnlyLI(
+ scanRequest,
+ new PackageManagerService.Injector(mMockUserManager, mMockPackageAbiHelper),
+ false /*isUnderFactoryTest*/,
+ System.currentTimeMillis());
+ }
+
+ private static String createResourcePath(String packageName) {
+ return "/data/app/" + packageName + "-randompath/base.apk";
+ }
+
+ private static String createCodePath(String packageName) {
+ return "/data/app/" + packageName + "-randompath";
+ }
+
+ private static PackageSettingBuilder createBasicPackageSettingBuilder(String packageName) {
+ return new PackageSettingBuilder()
+ .setName(packageName)
+ .setCodePath(createCodePath(packageName))
+ .setResourcePath(createResourcePath(packageName));
+ }
+
+ private static ScanRequestBuilder createBasicScanRequestBuilder(PackageParser.Package pkg) {
+ return new ScanRequestBuilder(pkg)
+ .setUser(UserHandle.of(0));
+ }
+
+
+ private static PackageBuilder createBasicPackage(String packageName) {
+ return new PackageBuilder(packageName)
+ .setCodePath("/data/tmp/randompath")
+ .setApplicationInfoCodePath(createCodePath(packageName))
+ .setApplicationInfoResourcePath(createResourcePath(packageName))
+ .setApplicationInfoVolumeUuid("volumeUuid")
+ .setBaseCodePath("/data/tmp/randompath/base.apk")
+ .addUsesStaticLibrary("some.static.library", 234L)
+ .addUsesStaticLibrary("some.other.static.library", 456L)
+ .setApplicationInfoNativeLibraryRootDir("/data/tmp/randompath/base.apk:/lib")
+ .setVersionCodeMajor(1)
+ .setVersionCode(2345);
+ }
+
+ private static void assertBasicPackageScanResult(
+ PackageManagerService.ScanResult scanResult, String packageName, boolean isInstant) {
+ assertThat(scanResult.success, is(true));
+
+ final PackageSetting pkgSetting = scanResult.pkgSetting;
+ assertBasicPackageSetting(scanResult, packageName, isInstant, pkgSetting);
+
+ final ApplicationInfo applicationInfo = pkgSetting.pkg.applicationInfo;
+ assertBasicApplicationInfo(scanResult, applicationInfo);
+
+ }
+
+ private static void assertBasicPackageSetting(PackageManagerService.ScanResult scanResult,
+ String packageName, boolean isInstant, PackageSetting pkgSetting) {
+ assertThat(pkgSetting.pkg.packageName, is(packageName));
+ assertThat(pkgSetting.getInstantApp(0), is(isInstant));
+ assertThat(pkgSetting.usesStaticLibraries,
+ arrayContaining("some.static.library", "some.other.static.library"));
+ assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
+ assertThat(pkgSetting.pkg, is(scanResult.request.pkg));
+ assertThat(pkgSetting.pkg.mExtras, is(pkgSetting));
+ assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName))));
+ assertThat(pkgSetting.resourcePath, is(new File(createResourcePath(packageName))));
+ assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345)));
+ }
+
+ private static void assertBasicApplicationInfo(PackageManagerService.ScanResult scanResult,
+ ApplicationInfo applicationInfo) {
+ assertThat(applicationInfo.processName, is(scanResult.request.pkg.packageName));
+
+ final int uid = applicationInfo.uid;
+ assertThat(UserHandle.getUserId(uid), is(UserHandle.USER_SYSTEM));
+
+ final String calculatedCredentialId = Environment.getDataUserCePackageDirectory(
+ applicationInfo.volumeUuid, UserHandle.USER_SYSTEM,
+ scanResult.request.pkg.packageName).getAbsolutePath();
+ assertThat(applicationInfo.credentialProtectedDataDir, is(calculatedCredentialId));
+ assertThat(applicationInfo.dataDir, is(applicationInfo.credentialProtectedDataDir));
+ }
+
+ private static void assertAbiAndPathssDerived(PackageManagerService.ScanResult scanResult) {
+ final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.applicationInfo;
+ assertThat(applicationInfo.primaryCpuAbi, is("derivedPrimary"));
+ assertThat(applicationInfo.secondaryCpuAbi, is("derivedSecondary"));
+
+ assertThat(applicationInfo.nativeLibraryRootDir, is("derivedRootDir"));
+ assertThat(scanResult.pkgSetting.legacyNativeLibraryPathString, is("derivedRootDir"));
+ assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true));
+ assertThat(applicationInfo.nativeLibraryDir, is("derivedNativeDir"));
+ assertThat(applicationInfo.secondaryNativeLibraryDir, is("derivedNativeDir2"));
+ }
+
+ private static void assertPathsNotDerived(PackageManagerService.ScanResult scanResult) {
+ final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.applicationInfo;
+ assertThat(applicationInfo.nativeLibraryRootDir, is("getRootDir"));
+ assertThat(scanResult.pkgSetting.legacyNativeLibraryPathString, is("getRootDir"));
+ assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true));
+ assertThat(applicationInfo.nativeLibraryDir, is("getNativeDir"));
+ assertThat(applicationInfo.secondaryNativeLibraryDir, is("getNativeDir2"));
+ }
+}