diff options
author | Jeff Sharkey <jsharkey@android.com> | 2014-08-19 16:18:28 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2014-08-20 12:47:27 -0700 |
commit | bb7b7bea19223c1eba74f525c7fe87ca3911813b (patch) | |
tree | 9477730dcb2db25d345a9845188b48a01647446c | |
parent | 80030f90b61cc5479382264966bf1b7bf66f10c8 (diff) | |
download | frameworks_base-bb7b7bea19223c1eba74f525c7fe87ca3911813b.zip frameworks_base-bb7b7bea19223c1eba74f525c7fe87ca3911813b.tar.gz frameworks_base-bb7b7bea19223c1eba74f525c7fe87ca3911813b.tar.bz2 |
More progress towards split APKs in ASECs.
Teach DefaultContainerService to install split APKs, which will be
needed when moving to/from ASECs. Also support forward locking for
testing purposes, even though its deprecated.
Move native library unpacking code to NativeLibraryHelper location
where it can be shared by both DCS and PMS. Also update footprint
calculation logic to mirror the later unpack codepaths.
Immediately persist sealed sessions. When resolving install
locations, prefer location of any existing install of that
package. Lightweight parse requesting certificates now always
verifies that all contents are signed correctly.
Bug: 16514385
Change-Id: Ida1c4eb0f95b065104dd971e19126d4085ebf1f0
9 files changed, 475 insertions, 428 deletions
diff --git a/api/current.txt b/api/current.txt index 8f75785..e995fc9 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8667,7 +8667,7 @@ package android.content.pm { method public void removeSessionCallback(android.content.pm.PackageInstaller.SessionCallback); method public void uninstall(java.lang.String, android.content.IntentSender); field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS"; - field public static final java.lang.String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES"; + field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME"; field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID"; field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS"; field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE"; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 4ac701f..7419ebc 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -105,10 +105,14 @@ public class PackageInstaller { public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE"; /** - * List of package names that are relevant to a status. + * Package name relevant to a status. * - * @see Intent#getStringArrayExtra(String) + * @see Intent#getStringExtra(String) */ + public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME"; + + /** {@hide} */ + @Deprecated public static final String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES"; /** {@hide} */ @@ -178,8 +182,8 @@ public class PackageInstaller { * permission, incompatible certificates, etc. The user may be able to * uninstall another app to fix the issue. * <p> - * The result may also contain {@link #EXTRA_PACKAGE_NAMES} with the - * specific packages identified as the cause of the conflict. + * The result may also contain {@link #EXTRA_PACKAGE_NAME} with the + * specific package identified as the cause of the conflict. * * @see #EXTRA_STATUS_MESSAGE */ diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 2c50f25..eb8b762 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -596,7 +596,7 @@ public class PackageParser { public final static int PARSE_ON_SDCARD = 1<<5; public final static int PARSE_IS_SYSTEM_DIR = 1<<6; public final static int PARSE_IS_PRIVILEGED = 1<<7; - public final static int PARSE_GET_SIGNATURES = 1<<8; + public final static int PARSE_COLLECT_CERTIFICATES = 1<<8; public final static int PARSE_TRUSTED_OVERLAY = 1<<9; private static final Comparator<String> sSplitNameComparator = new SplitNameComparator(); @@ -1087,34 +1087,6 @@ public class PackageParser { } } - /** - * Only collect certificates on the manifest; does not validate signatures - * across remainder of package. - */ - private static Signature[] collectManifestCertificates(File apkFile) - throws PackageParserException { - final String apkPath = apkFile.getAbsolutePath(); - try { - final StrictJarFile jarFile = new StrictJarFile(apkPath); - try { - final ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); - if (jarEntry == null) { - throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, - "Package " + apkPath + " has no manifest"); - } - - final Certificate[][] certs = loadCertificates(jarFile, jarEntry); - return convertToSignatures(certs); - - } finally { - jarFile.close(); - } - } catch (GeneralSecurityException | IOException | RuntimeException e) { - throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, - "Failed to collect certificates from " + apkPath, e); - } - } - private static Signature[] convertToSignatures(Certificate[][] certs) throws CertificateEncodingException { final Signature[] res = new Signature[certs.length]; @@ -1129,7 +1101,8 @@ public class PackageParser { * file, including package name, split name, and install location. * * @param apkFile path to a single APK - * @param flags optional parse flags, such as {@link #PARSE_GET_SIGNATURES} + * @param flags optional parse flags, such as + * {@link #PARSE_COLLECT_CERTIFICATES} */ public static ApkLite parseApkLite(File apkFile, int flags) throws PackageParserException { @@ -1154,11 +1127,12 @@ public class PackageParser { final Resources res = new Resources(assets, metrics, null); parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); - // Only collect certificates on the manifest; does not validate - // signatures across remainder of package. final Signature[] signatures; - if ((flags & PARSE_GET_SIGNATURES) != 0) { - signatures = collectManifestCertificates(apkFile); + if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { + // TODO: factor signature related items out of Package object + final Package tempPkg = new Package(null); + collectCertificates(tempPkg, apkFile, 0); + signatures = tempPkg.mSignatures; } else { signatures = null; } diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index d76d0d4..179d5e8 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -19,15 +19,25 @@ package com.android.internal.content; import static android.content.pm.PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.PackageManager.NO_NATIVE_LIBRARIES; +import static android.system.OsConstants.S_IRGRP; +import static android.system.OsConstants.S_IROTH; +import static android.system.OsConstants.S_IRWXU; +import static android.system.OsConstants.S_IXGRP; +import static android.system.OsConstants.S_IXOTH; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageParser.Package; import android.content.pm.PackageParser.PackageLite; import android.content.pm.PackageParser.PackageParserException; +import android.os.Build; +import android.os.SELinux; +import android.system.ErrnoException; +import android.system.Os; import android.util.Slog; import dalvik.system.CloseGuard; +import dalvik.system.VMRuntime; import java.io.Closeable; import java.io.File; @@ -44,6 +54,10 @@ public class NativeLibraryHelper { private static final boolean DEBUG_NATIVE = false; + // Special value for {@code PackageParser.Package#cpuAbiOverride} to indicate + // that the cpuAbiOverride must be clear. + public static final String CLEAR_ABI_OVERRIDE = "-"; + /** * A handle to an opened package, consisting of one or more APKs. Used as * input to the various NativeLibraryHelper methods. Allows us to scan and @@ -126,27 +140,17 @@ public class NativeLibraryHelper { private static native long nativeSumNativeBinaries(long handle, String cpuAbi); - /** - * Sums the size of native binaries in an APK for a given ABI. - * - * @return size of all native binary files in bytes - */ - public static long sumNativeBinariesLI(Handle handle, String[] abis) { + private native static int nativeCopyNativeBinaries(long handle, + String sharedLibraryPath, String abiToCopy); + + private static long sumNativeBinaries(Handle handle, String abi) { long sum = 0; for (long apkHandle : handle.apkHandles) { - // NOTE: For a given APK handle, we parse the central directory precisely - // once, but prefix matching of entries requires a CD traversal, which can - // take a while (even if it needs no additional I/O). - for (String abi : abis) { - sum += nativeSumNativeBinaries(apkHandle, abi); - } + sum += nativeSumNativeBinaries(apkHandle, abi); } return sum; } - private native static int nativeCopyNativeBinaries(long handle, - String sharedLibraryPath, String abiToCopy); - /** * Copies native binaries to a shared library directory. * @@ -244,9 +248,171 @@ public class NativeLibraryHelper { } } + private static void createNativeLibrarySubdir(File path) throws IOException { + if (!path.isDirectory()) { + path.delete(); + + if (!path.mkdir()) { + throw new IOException("Cannot create " + path.getPath()); + } + + try { + Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + } catch (ErrnoException e) { + throw new IOException("Cannot chmod native library directory " + + path.getPath(), e); + } + } else if (!SELinux.restorecon(path)) { + throw new IOException("Cannot set SELinux context for " + path.getPath()); + } + } + + private static long sumNativeBinaries(Handle handle, String[] abiList) { + int abi = findSupportedAbi(handle, abiList); + if (abi >= 0) { + return sumNativeBinaries(handle, abiList[abi]); + } else { + return 0; + } + } + + public static int copyNativeBinariesIfNeededLI(Handle handle, File libraryRoot, + String[] abiList, boolean useIsaSubdir) throws IOException { + createNativeLibrarySubdir(libraryRoot); + + /* + * If this is an internal application or our nativeLibraryPath points to + * the app-lib directory, unpack the libraries if necessary. + */ + int abi = findSupportedAbi(handle, abiList); + if (abi >= 0) { + /* + * If we have a matching instruction set, construct a subdir under the native + * library root that corresponds to this instruction set. + */ + final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]); + final File subDir; + if (useIsaSubdir) { + final File isaSubdir = new File(libraryRoot, instructionSet); + createNativeLibrarySubdir(isaSubdir); + subDir = isaSubdir; + } else { + subDir = libraryRoot; + } + + int copyRet = copyNativeBinariesIfNeededLI(handle, subDir, abiList[abi]); + if (copyRet != PackageManager.INSTALL_SUCCEEDED) { + return copyRet; + } + } + + return abi; + } + + public static int copyNativeBinariesIfNeededLI(Handle handle, File libraryRoot, + String abiOverride, boolean multiArch) { + try { + if (multiArch) { + // 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 (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) { + Slog.w(TAG, "Ignoring abiOverride for multi arch application."); + } + + int copyRet = PackageManager.NO_NATIVE_LIBRARIES; + if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { + copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot, + Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */); + if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES && + copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) { + Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet); + return copyRet; + } + } + + if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { + copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot, + Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */); + if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES && + copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) { + Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet); + return copyRet; + } + } + } else { + String cpuAbiOverride = null; + if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) { + cpuAbiOverride = null; + } else if (abiOverride != null) { + cpuAbiOverride = abiOverride; + } + + String[] abiList = (cpuAbiOverride != null) ? + new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS; + if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null && + hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + } + + int copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot, abiList, + true /* use isa specific subdirs */); + if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { + Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]"); + return copyRet; + } + } + + return PackageManager.INSTALL_SUCCEEDED; + } catch (IOException e) { + Slog.e(TAG, "Copying native libraries failed", e); + return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + } + } + + public static long sumNativeBinaries(Handle handle, String abiOverride, boolean multiArch) + throws IOException { + long sum = 0; + if (multiArch) { + // 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 (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) { + Slog.w(TAG, "Ignoring abiOverride for multi arch application."); + } + + if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { + sum += sumNativeBinaries(handle, Build.SUPPORTED_32_BIT_ABIS); + } + + if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { + sum += sumNativeBinaries(handle, Build.SUPPORTED_64_BIT_ABIS); + } + } else { + String cpuAbiOverride = null; + if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) { + cpuAbiOverride = null; + } else if (abiOverride != null) { + cpuAbiOverride = abiOverride; + } + + String[] abiList = (cpuAbiOverride != null) ? + new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS; + if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null && + hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + } + + sum += sumNativeBinaries(handle, abiList); + } + return sum; + } + // We don't care about the other return values for now. private static final int BITCODE_PRESENT = 1; + private static native int hasRenderscriptBitcode(long apkHandle); + public static boolean hasRenderscriptBitcode(Handle handle) throws IOException { for (long apkHandle : handle.apkHandles) { final int res = hasRenderscriptBitcode(apkHandle); @@ -258,6 +424,4 @@ public class NativeLibraryHelper { } return false; } - - private static native int hasRenderscriptBitcode(long apkHandle); } diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index fd96f64..a529cc3 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -17,13 +17,17 @@ package com.android.internal.content; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.Environment; +import android.os.Environment.UserEnvironment; import android.os.FileUtils; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.os.storage.IMountService; import android.os.storage.StorageManager; import android.os.storage.StorageResultCode; @@ -215,7 +219,7 @@ public class PackageHelper { /** * Extract public files for the single given APK. */ - public static int extractPublicFiles(String apkPath, File publicZipFile) + public static long extractPublicFiles(File apkFile, File publicZipFile) throws IOException { final FileOutputStream fstr; final ZipOutputStream publicZipOutStream; @@ -226,12 +230,13 @@ public class PackageHelper { } else { fstr = new FileOutputStream(publicZipFile); publicZipOutStream = new ZipOutputStream(fstr); + Log.d(TAG, "Extracting " + apkFile + " to " + publicZipFile); } - int size = 0; + long size = 0L; try { - final ZipFile privateZip = new ZipFile(apkPath); + final ZipFile privateZip = new ZipFile(apkFile.getAbsolutePath()); try { // Copy manifest, resources.arsc and res directory to public zip for (final ZipEntry zipEntry : Collections.list(privateZip.entries())) { @@ -308,8 +313,15 @@ public class PackageHelper { * Given a requested {@link PackageInfo#installLocation} and calculated * install size, pick the actual location to install the app. */ - public static int resolveInstallLocation(Context context, int installLocation, long sizeBytes, - int installFlags) { + public static int resolveInstallLocation(Context context, String packageName, + int installLocation, long sizeBytes, int installFlags) { + ApplicationInfo existingInfo = null; + try { + existingInfo = context.getPackageManager().getApplicationInfo(packageName, + PackageManager.GET_UNINSTALLED_PACKAGES); + } catch (NameNotFoundException ignored) { + } + final int prefer; final boolean checkBoth; if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) { @@ -325,7 +337,16 @@ public class PackageHelper { prefer = RECOMMEND_INSTALL_EXTERNAL; checkBoth = true; } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) { - prefer = RECOMMEND_INSTALL_INTERNAL; + // When app is already installed, prefer same medium + if (existingInfo != null) { + if ((existingInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { + prefer = RECOMMEND_INSTALL_EXTERNAL; + } else { + prefer = RECOMMEND_INSTALL_INTERNAL; + } + } else { + prefer = RECOMMEND_INSTALL_INTERNAL; + } checkBoth = true; } else { prefer = RECOMMEND_INSTALL_INTERNAL; @@ -337,14 +358,15 @@ public class PackageHelper { boolean fitsOnInternal = false; if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) { - fitsOnInternal = (sizeBytes - <= storage.getStorageBytesUntilLow(Environment.getDataDirectory())); + final File target = Environment.getDataDirectory(); + fitsOnInternal = (sizeBytes <= storage.getStorageBytesUntilLow(target)); } boolean fitsOnExternal = false; if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)) { - fitsOnExternal = (sizeBytes - <= storage.getStorageBytesUntilLow(Environment.getExternalStorageDirectory())); + final File target = new UserEnvironment(UserHandle.USER_OWNER) + .getExternalStorageDirectory(); + fitsOnExternal = (sizeBytes <= storage.getStorageBytesUntilLow(target)); } if (prefer == RECOMMEND_INSTALL_INTERNAL) { @@ -377,4 +399,12 @@ public class PackageHelper { return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } } + + public static String replaceEnd(String str, String before, String after) { + if (!str.endsWith(before)) { + throw new IllegalArgumentException( + "Expected " + str + " to end with " + before); + } + return str.substring(0, str.length() - before.length()) + after; + } } diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 4c225c1..fae30e5 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -30,7 +30,6 @@ import android.content.pm.PackageParser.PackageLite; import android.content.pm.PackageParser.PackageParserException; import android.content.res.ObbInfo; import android.content.res.ObbScanner; -import android.os.Build; import android.os.Environment; import android.os.Environment.UserEnvironment; import android.os.FileUtils; @@ -67,7 +66,6 @@ import java.io.OutputStream; */ public class DefaultContainerService extends IntentService { private static final String TAG = "DefContainer"; - private static final boolean localLOGV = false; private static final String LIB_DIR_NAME = "lib"; @@ -112,7 +110,7 @@ public class DefaultContainerService extends IntentService { return copyPackageToContainerInner(pkg, handle, containerId, key, isExternal, isForwardLocked, abiOverride); } catch (PackageParserException | IOException e) { - Slog.w(TAG, "Failed to parse package at " + packagePath); + Slog.w(TAG, "Failed to copy package at " + packagePath, e); return null; } finally { IoUtils.closeQuietly(handle); @@ -188,7 +186,7 @@ public class DefaultContainerService extends IntentService { ret.installLocation = pkg.installLocation; ret.verifiers = pkg.verifiers; ret.recommendedInstallLocation = PackageHelper.resolveInstallLocation(context, - pkg.installLocation, sizeBytes, flags); + pkg.packageName, pkg.installLocation, sizeBytes, flags); ret.multiArch = pkg.multiArch; return ret; @@ -313,161 +311,73 @@ public class DefaultContainerService extends IntentService { private String copyPackageToContainerInner(PackageLite pkg, NativeLibraryHelper.Handle handle, String newCid, String key, boolean isExternal, boolean isForwardLocked, - String abiOverride) { - // TODO: extend to support copying all split APKs - if (!ArrayUtils.isEmpty(pkg.splitNames)) { - throw new UnsupportedOperationException("Copying split APKs not yet supported"); - } - - final String resFileName = "pkg.apk"; - final String publicResFileName = "res.zip"; - - if (pkg.multiArch) { - // TODO: Support multiArch installs on ASEC. - throw new IllegalArgumentException("multiArch not supported on ASEC installs."); - } + String abiOverride) throws IOException { - // The .apk file - final String codePath = pkg.baseCodePath; - final File codeFile = new File(codePath); - final String[] abis; - try { - abis = calculateAbiList(handle, abiOverride, pkg.multiArch); - } catch (IOException ioe) { - Slog.w(TAG, "Problem determining app ABIS: " + ioe); - return null; - } - - // Calculate size of container needed to hold base APK. Round up to - // nearest MB, and tack on an extra MB for filesystem overhead. - final int sizeMb; - try { - final long sizeBytes = calculateInstalledSizeInner(pkg, handle, isForwardLocked, abis); - sizeMb = ((int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES)) + 1; - } catch (IOException e) { - Slog.w(TAG, "Problem when trying to copy " + codeFile.getPath()); - return null; - } + // Calculate container size, rounding up to nearest MB and adding an + // extra MB for filesystem overhead + final long sizeBytes = calculateInstalledSizeInner(pkg, handle, isForwardLocked, + abiOverride); + final int sizeMb = ((int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES)) + 1; // Create new container - final String newCachePath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid(), + final String newMountPath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid(), isExternal); - if (newCachePath == null) { - Slog.e(TAG, "Failed to create container " + newCid); - return null; - } - - if (localLOGV) { - Slog.i(TAG, "Created container for " + newCid + " at path : " + newCachePath); - } - - final File resFile = new File(newCachePath, resFileName); - if (FileUtils.copyFile(new File(codePath), resFile)) { - if (localLOGV) { - Slog.i(TAG, "Copied " + codePath + " to " + resFile); - } - } else { - Slog.e(TAG, "Failed to copy " + codePath + " to " + resFile); - // Clean up container - PackageHelper.destroySdDir(newCid); - return null; + if (newMountPath == null) { + throw new IOException("Failed to create container " + newCid); } + final File targetDir = new File(newMountPath); try { - Os.chmod(resFile.getAbsolutePath(), 0640); - } catch (ErrnoException e) { - Slog.e(TAG, "Could not chown APK: " + e.getMessage()); - PackageHelper.destroySdDir(newCid); - return null; - } - - if (isForwardLocked) { - File publicZipFile = new File(newCachePath, publicResFileName); - try { - PackageHelper.extractPublicFiles(resFile.getAbsolutePath(), publicZipFile); - if (localLOGV) { - Slog.i(TAG, "Copied resources to " + publicZipFile); + // Copy all APKs + copyFile(pkg.baseCodePath, targetDir, "base.apk", isForwardLocked); + if (!ArrayUtils.isEmpty(pkg.splitNames)) { + for (int i = 0; i < pkg.splitNames.length; i++) { + copyFile(pkg.splitCodePaths[i], targetDir, + "split_" + pkg.splitNames[i] + ".apk", isForwardLocked); } - } catch (IOException e) { - Slog.e(TAG, "Could not chown public APK " + publicZipFile.getAbsolutePath() + ": " - + e.getMessage()); - PackageHelper.destroySdDir(newCid); - return null; } - try { - Os.chmod(publicZipFile.getAbsolutePath(), 0644); - } catch (ErrnoException e) { - Slog.e(TAG, "Could not chown public resource file: " + e.getMessage()); - PackageHelper.destroySdDir(newCid); - return null; + // Extract native code + final File libraryRoot = new File(targetDir, LIB_DIR_NAME); + final int res = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, libraryRoot, + abiOverride, pkg.multiArch); + if (res != PackageManager.INSTALL_SUCCEEDED) { + throw new IOException("Failed to extract native code, res=" + res); } - } - final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME); - if (sharedLibraryDir.mkdir()) { - int ret = PackageManager.INSTALL_SUCCEEDED; - if (abis != null) { - // TODO(multiArch): Support multi-arch installs on asecs. Note that we are NOT - // using an ISA specific subdir here for now. - final String abi = abis[0]; - ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, - sharedLibraryDir, abi); - - if (ret != PackageManager.INSTALL_SUCCEEDED) { - Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath()); - PackageHelper.destroySdDir(newCid); - return null; - } + if (!PackageHelper.finalizeSdDir(newCid)) { + throw new IOException("Failed to finalize " + newCid); } - } else { - Slog.e(TAG, "Could not create native lib directory: " + sharedLibraryDir.getPath()); - PackageHelper.destroySdDir(newCid); - return null; - } - if (!PackageHelper.finalizeSdDir(newCid)) { - Slog.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath); - // Clean up container - PackageHelper.destroySdDir(newCid); - return null; - } - - if (localLOGV) { - Slog.i(TAG, "Finalized container " + newCid); - } - - if (PackageHelper.isContainerMounted(newCid)) { - if (localLOGV) { - Slog.i(TAG, "Unmounting " + newCid + " at path " + newCachePath); + if (PackageHelper.isContainerMounted(newCid)) { + PackageHelper.unMountSdDir(newCid); } - // Force a gc to avoid being killed. - Runtime.getRuntime().gc(); - PackageHelper.unMountSdDir(newCid); - } else { - if (localLOGV) { - Slog.i(TAG, "Container " + newCid + " not mounted"); - } + } catch (ErrnoException e) { + PackageHelper.destroySdDir(newCid); + throw e.rethrowAsIOException(); + } catch (IOException e) { + PackageHelper.destroySdDir(newCid); + throw e; } - return newCachePath; + return newMountPath; } private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target) throws IOException, RemoteException { - copyFile(pkg.baseCodePath, "base.apk", target); + copyFile(pkg.baseCodePath, target, "base.apk"); if (!ArrayUtils.isEmpty(pkg.splitNames)) { for (int i = 0; i < pkg.splitNames.length; i++) { - copyFile(pkg.splitCodePaths[i], "split_" + pkg.splitNames[i] + ".apk", target); + copyFile(pkg.splitCodePaths[i], target, "split_" + pkg.splitNames[i] + ".apk"); } } return PackageManager.INSTALL_SUCCEEDED; } - private void copyFile(String sourcePath, String targetName, - IParcelFileDescriptorFactory target) throws IOException, RemoteException { + private void copyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName) + throws IOException, RemoteException { Slog.d(TAG, "Copying " + sourcePath + " to " + targetName); InputStream in = null; OutputStream out = null; @@ -482,76 +392,58 @@ public class DefaultContainerService extends IntentService { } } + private void copyFile(String sourcePath, File targetDir, String targetName, + boolean isForwardLocked) throws IOException, ErrnoException { + final File sourceFile = new File(sourcePath); + final File targetFile = new File(targetDir, targetName); + + Slog.d(TAG, "Copying " + sourceFile + " to " + targetFile); + if (!FileUtils.copyFile(sourceFile, targetFile)) { + throw new IOException("Failed to copy " + sourceFile + " to " + targetFile); + } + + if (isForwardLocked) { + final String publicTargetName = PackageHelper.replaceEnd(targetName, + ".apk", ".zip"); + final File publicTargetFile = new File(targetDir, publicTargetName); + + PackageHelper.extractPublicFiles(sourceFile, publicTargetFile); + + Os.chmod(targetFile.getAbsolutePath(), 0640); + Os.chmod(publicTargetFile.getAbsolutePath(), 0644); + } else { + Os.chmod(targetFile.getAbsolutePath(), 0644); + } + } + private long calculateInstalledSizeInner(PackageLite pkg, boolean isForwardLocked, String abiOverride) throws IOException { NativeLibraryHelper.Handle handle = null; try { handle = NativeLibraryHelper.Handle.create(pkg); - return calculateInstalledSizeInner(pkg, handle, isForwardLocked, - calculateAbiList(handle, abiOverride, pkg.multiArch)); + return calculateInstalledSizeInner(pkg, handle, isForwardLocked, abiOverride); } finally { IoUtils.closeQuietly(handle); } } private long calculateInstalledSizeInner(PackageLite pkg, NativeLibraryHelper.Handle handle, - boolean isForwardLocked, String[] abis) throws IOException { + boolean isForwardLocked, String abiOverride) throws IOException { long sizeBytes = 0; // Include raw APKs, and possibly unpacked resources for (String codePath : pkg.getAllCodePaths()) { - sizeBytes += new File(codePath).length(); + final File codeFile = new File(codePath); + sizeBytes += codeFile.length(); if (isForwardLocked) { - sizeBytes += PackageHelper.extractPublicFiles(codePath, null); + sizeBytes += PackageHelper.extractPublicFiles(codeFile, null); } } // Include all relevant native code - if (!ArrayUtils.isEmpty(abis)) { - sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(handle, abis); - } + sizeBytes += NativeLibraryHelper.sumNativeBinaries(handle, abiOverride, pkg.multiArch); return sizeBytes; } - - private String[] calculateAbiList(NativeLibraryHelper.Handle handle, String abiOverride, - boolean isMultiArch) throws IOException { - if (isMultiArch) { - final int abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS); - final int abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS); - - if (abi32 >= 0 && abi64 >= 0) { - return new String[] { Build.SUPPORTED_64_BIT_ABIS[abi64], Build.SUPPORTED_32_BIT_ABIS[abi32] }; - } else if (abi64 >= 0) { - return new String[] { Build.SUPPORTED_64_BIT_ABIS[abi64] }; - } else if (abi32 >= 0) { - return new String[] { Build.SUPPORTED_32_BIT_ABIS[abi32] }; - } - - if (abi64 != PackageManager.NO_NATIVE_LIBRARIES || abi32 != PackageManager.NO_NATIVE_LIBRARIES) { - throw new IOException("Error determining ABI list: errorCode=[" + abi32 + "," + abi64 + "]"); - } - - } else { - String[] abiList = Build.SUPPORTED_ABIS; - if (abiOverride != null) { - abiList = new String[] { abiOverride }; - } else if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && - NativeLibraryHelper.hasRenderscriptBitcode(handle)) { - abiList = Build.SUPPORTED_32_BIT_ABIS; - } - - final int abi = NativeLibraryHelper.findSupportedAbi(handle,abiList); - if (abi >= 0) { - return new String[]{Build.SUPPORTED_ABIS[abi]}; - } - - if (abi != PackageManager.NO_NATIVE_LIBRARIES) { - throw new IOException("Error determining ABI list: errorCode=" + abi); - } - } - - return null; - } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 0393518..03cb2e9 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -56,6 +56,7 @@ import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Environment; +import android.os.Environment.UserEnvironment; import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; @@ -485,7 +486,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { // Brand new install, use best resolved location. This also verifies // that target has enough free space for the install. final int resolved = PackageHelper.resolveInstallLocation(mContext, - params.installLocation, params.sizeBytes, params.installFlags); + params.appPackageName, params.installLocation, params.sizeBytes, + params.installFlags); if (resolved == PackageHelper.RECOMMEND_INSTALL_INTERNAL) { stageInternal = true; } else if (resolved == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { @@ -568,7 +570,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { private void checkExternalStorage(long sizeBytes) throws IOException { if (sizeBytes <= 0) return; - final File target = Environment.getExternalStorageDirectory(); + final File target = new UserEnvironment(UserHandle.USER_OWNER) + .getExternalStorageDirectory(); final long targetBytes = sizeBytes + mStorage.getStorageLowBytes(target); if (target.getUsableSpace() < targetBytes) { @@ -696,7 +699,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstall"); final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, - statusReceiver); + statusReceiver, packageName); if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES) == PackageManager.PERMISSION_GRANTED) { // Sweet, call straight through! @@ -773,15 +776,19 @@ public class PackageInstallerService extends IPackageInstaller.Stub { static class PackageDeleteObserverAdapter extends PackageDeleteObserver { private final Context mContext; private final IntentSender mTarget; + private final String mPackageName; - public PackageDeleteObserverAdapter(Context context, IntentSender target) { + public PackageDeleteObserverAdapter(Context context, IntentSender target, + String packageName) { mContext = context; mTarget = target; + mPackageName = packageName; } @Override public void onUserActionRequired(Intent intent) { final Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName); fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION); fillIn.putExtra(Intent.EXTRA_INTENT, intent); @@ -794,6 +801,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { @Override public void onPackageDeleted(String basePackageName, int returnCode, String msg) { final Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName); fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageManager.deleteStatusToPublicStatus(returnCode)); fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, @@ -809,15 +817,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub { static class PackageInstallObserverAdapter extends PackageInstallObserver { private final Context mContext; private final IntentSender mTarget; + private final int mSessionId; - public PackageInstallObserverAdapter(Context context, IntentSender target) { + public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId) { mContext = context; mTarget = target; + mSessionId = sessionId; } @Override public void onUserActionRequired(Intent intent) { final Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId); fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION); fillIn.putExtra(Intent.EXTRA_INTENT, intent); @@ -831,6 +842,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { public void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) { final Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId); fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageManager.installStatusToPublicStatus(returnCode)); fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, @@ -840,8 +852,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { final String existing = extras.getString( PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE); if (!TextUtils.isEmpty(existing)) { - fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAMES, new String[] { - existing }); + fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, existing); } } try { @@ -986,5 +997,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } writeSessionsAsync(); } + + public void onSessionSealed(PackageInstallerSession session) { + // It's very important that we block until we've recorded the + // session as being sealed, since we never want to allow mutation + // after sealing. + writeSessionsLocked(); + } } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 0616460..38a2016 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -195,6 +195,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { this.internalStageDir = internalStageDir; this.externalStageCid = externalStageCid; + if ((internalStageDir == null) == (externalStageCid == null)) { + throw new IllegalArgumentException( + "Exactly one of internal or external stage must be set"); + } + mSealed = sealed; // Always derived at runtime @@ -395,7 +400,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Preconditions.checkNotNull(statusReceiver); final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext, - statusReceiver); + statusReceiver, sessionId); mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget(); } @@ -414,8 +419,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } mSealed = true; - // TODO: persist disabled mutations before going forward, since - // beyond this point we may have hardlinks to the valid install + // Persist the fact that we've sealed ourselves to prevent mutations + // of any hard links we create below. + mCallback.onSessionSealed(this); } final File stageDir; @@ -476,9 +482,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } }; - // TODO: send ASEC cid if that's where we staged things - mPm.installStage(mPackageName, this.internalStageDir, null, localObserver, params, - installerPackageName, installerUid, new UserHandle(userId)); + mPm.installStage(mPackageName, this.internalStageDir, this.externalStageCid, localObserver, + params, installerPackageName, installerUid, new UserHandle(userId)); } /** @@ -486,6 +491,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * consistent package name, version code, and signing certificates. * <p> * Renames package files in stage to match split names defined inside. + * <p> + * Note that upgrade compatibility is still performed by + * {@link PackageManagerService}. */ private void validateInstallLocked(File stageDir) throws PackageManagerException { mPackageName = null; @@ -498,13 +506,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); } - final ArraySet<String> seenSplits = new ArraySet<>(); - // Verify that all staged packages are internally consistent + final ArraySet<String> seenSplits = new ArraySet<>(); for (File file : files) { + + // Installers can't stage directories, so it's fine to ignore + // entries like "lost+found". + if (file.isDirectory()) continue; + final ApkLite info; try { - info = PackageParser.parseApkLite(file, PackageParser.PARSE_GET_SIGNATURES); + info = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES); } catch (PackageParserException e) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Failed to parse " + file + ": " + e); @@ -550,10 +562,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - // TODO: shift package signature verification to installer; we're - // currently relying on PMS to do this. - // TODO: teach about compatible upgrade keysets. - if (params.mode == SessionParams.MODE_FULL_INSTALL) { // Full installs must include a base package if (!seenSplits.contains(null)) { @@ -577,7 +585,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final ApkLite info; try { info = PackageParser.parseApkLite(new File(app.getBaseCodePath()), - PackageParser.PARSE_GET_SIGNATURES); + PackageParser.PARSE_COLLECT_CERTIFICATES); } catch (PackageParserException e) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Failed to parse existing base " + app.getBaseCodePath() + ": " + e); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f47e64f..aa49b27 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -48,11 +48,6 @@ import static android.os.Process.PACKAGE_INFO_GID; import static android.os.Process.SYSTEM_UID; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDWR; -import static android.system.OsConstants.S_IRGRP; -import static android.system.OsConstants.S_IROTH; -import static android.system.OsConstants.S_IRWXU; -import static android.system.OsConstants.S_IXGRP; -import static android.system.OsConstants.S_IXOTH; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER; import static com.android.internal.util.ArrayUtils.appendInt; @@ -70,7 +65,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; -import com.android.internal.util.Preconditions; import com.android.server.EventLogTags; import com.android.server.IntentResolver; import com.android.server.LocalServices; @@ -255,10 +249,6 @@ public class PackageManagerService extends IPackageManager.Stub { // package apks to install directory. private static final String INSTALL_PACKAGE_SUFFIX = "-"; - // Special value for {@code PackageParser.Package#cpuAbiOverride} to indicate - // that the cpuAbiOverride must be clear. - private static final String CLEAR_ABI_OVERRIDE = "-"; - static final int SCAN_MONITOR = 1<<0; static final int SCAN_NO_DEX = 1<<1; static final int SCAN_FORCE_DEX = 1<<2; @@ -5022,7 +5012,7 @@ public class PackageManagerService extends IPackageManager.Stub { private static String deriveAbiOverride(String abiOverride, PackageSetting settings) { String cpuAbiOverride = null; - if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) { + if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) { cpuAbiOverride = null; } else if (abiOverride != null) { cpuAbiOverride = abiOverride; @@ -5526,7 +5516,8 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 && !CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) { + if (pkg.cpuAbiOverride != null + && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) { Slog.w(TAG, "Ignoring abiOverride for multi arch application."); } @@ -5536,7 +5527,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (isAsec) { abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS); } else { - abi32 = copyNativeLibrariesForInternalApp(handle, + abi32 = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, useIsaSpecificSubdirs); } } @@ -5548,7 +5539,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (isAsec) { abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS); } else { - abi64 = copyNativeLibrariesForInternalApp(handle, + abi64 = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, useIsaSpecificSubdirs); } } @@ -5587,8 +5578,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (isAsec) { copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList); } else { - copyRet = copyNativeLibrariesForInternalApp(handle, nativeLibraryRoot, abiList, - useIsaSpecificSubdirs); + copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, + nativeLibraryRoot, abiList, useIsaSpecificSubdirs); } if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { @@ -6471,58 +6462,6 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private static void createNativeLibrarySubdir(File path) throws IOException { - if (!path.isDirectory()) { - path.delete(); - - if (!path.mkdir()) { - throw new IOException("Cannot create " + path.getPath()); - } - - try { - Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - } catch (ErrnoException e) { - throw new IOException("Cannot chmod native library directory " - + path.getPath(), e); - } - } else if (!SELinux.restorecon(path)) { - throw new IOException("Cannot set SELinux context for " + path.getPath()); - } - } - - private static int copyNativeLibrariesForInternalApp(NativeLibraryHelper.Handle handle, - final File nativeLibraryRoot, String[] abiList, boolean useIsaSubdir) throws IOException { - createNativeLibrarySubdir(nativeLibraryRoot); - - /* - * If this is an internal application or our nativeLibraryPath points to - * the app-lib directory, unpack the libraries if necessary. - */ - int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList); - if (abi >= 0) { - /* - * If we have a matching instruction set, construct a subdir under the native - * library root that corresponds to this instruction set. - */ - final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]); - final File subDir; - if (useIsaSubdir) { - final File isaSubdir = new File(nativeLibraryRoot, instructionSet); - createNativeLibrarySubdir(isaSubdir); - subDir = isaSubdir; - } else { - subDir = nativeLibraryRoot; - } - - int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, subDir, abiList[abi]); - if (copyRet != PackageManager.INSTALL_SUCCEEDED) { - return copyRet; - } - } - - return abi; - } - private void killApplication(String pkgName, int appId, String reason) { // Request the ActivityManager to kill the process(only for existing packages) // so that we do not end up in a confused state while the user is still using the older @@ -8667,6 +8606,20 @@ public class PackageManagerService extends IPackageManager.Stub { */ public void handleStartCopy() throws RemoteException { int ret = PackageManager.INSTALL_SUCCEEDED; + + // If we're already staged, we've firmly committed to an install location + if (originStaged) { + if (originFile != null) { + flags |= PackageManager.INSTALL_INTERNAL; + flags &= ~PackageManager.INSTALL_EXTERNAL; + } else if (originCid != null) { + flags |= PackageManager.INSTALL_EXTERNAL; + flags &= ~PackageManager.INSTALL_INTERNAL; + } else { + throw new IllegalStateException("Invalid stage location"); + } + } + final boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0; final boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0; PackageInfoLite pkgLite = null; @@ -8690,7 +8643,7 @@ public class PackageManagerService extends IPackageManager.Stub { * If we have too little free space, try to free cache * before giving up. */ - if (pkgLite.recommendedInstallLocation + if (!originStaged && pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { // TODO: focus freeing disk space on the target device final StorageManager storage = StorageManager.from(mContext); @@ -9287,49 +9240,11 @@ public class PackageManagerService extends IPackageManager.Stub { NativeLibraryHelper.Handle handle = null; try { handle = NativeLibraryHelper.Handle.create(codeFile); - if (multiArch) { - // 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 (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) { - Slog.w(TAG, "Ignoring abiOverride for multi arch application."); - } - - int copyRet = PackageManager.NO_NATIVE_LIBRARIES; - if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { - copyRet = copyNativeLibrariesForInternalApp(handle, libraryRoot, - Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */); - maybeThrowExceptionForMultiArchCopy("Failure copying 32 bit native libraries", copyRet); - } - - if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { - copyRet = copyNativeLibrariesForInternalApp(handle, libraryRoot, - Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */); - maybeThrowExceptionForMultiArchCopy("Failure copying 64 bit native libraries", copyRet); - } - } else { - final String cpuAbiOverride = deriveAbiOverride(this.abiOverride, null /* package setting */); - String[] abiList = (cpuAbiOverride != null) ? - new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS; - - if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null && - NativeLibraryHelper.hasRenderscriptBitcode(handle)) { - abiList = Build.SUPPORTED_32_BIT_ABIS; - } - - int copyRet = copyNativeLibrariesForInternalApp(handle, libraryRoot, abiList, - true /* use isa specific subdirs */); - if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { - Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]"); - return copyRet; - } - } + ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, libraryRoot, + abiOverride, multiArch); } catch (IOException e) { Slog.e(TAG, "Copying native libraries failed", e); ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; - } catch (PackageManagerException pme) { - Slog.e(TAG, "Copying native libraries failed", pme); - ret = pme.error; } finally { IoUtils.closeQuietly(handle); } @@ -9504,8 +9419,6 @@ public class PackageManagerService extends IPackageManager.Stub { * renaming logic. */ class AsecInstallArgs extends InstallArgs { - // TODO: teach about handling cluster directories - static final String RES_FILE_NAME = "pkg.apk"; static final String PUBLIC_RES_FILE_NAME = "res.zip"; @@ -9528,12 +9441,17 @@ public class PackageManagerService extends IPackageManager.Stub { super(null, false, null, (isExternal ? INSTALL_EXTERNAL : 0) | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, instructionSets, null, isMultiArch); + // Hackily pretend we're still looking at a full code path + if (!fullCodePath.endsWith(RES_FILE_NAME)) { + fullCodePath = new File(fullCodePath, RES_FILE_NAME).getAbsolutePath(); + } + // Extract cid from fullCodePath int eidx = fullCodePath.lastIndexOf("/"); String subStr1 = fullCodePath.substring(0, eidx); int sidx = subStr1.lastIndexOf("/"); cid = subStr1.substring(sidx+1, eidx); - setCachePath(subStr1); + setMountPath(subStr1); } AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked, @@ -9542,7 +9460,7 @@ public class PackageManagerService extends IPackageManager.Stub { | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, instructionSets, null, isMultiArch); this.cid = cid; - setCachePath(PackageHelper.getSdDir(cid)); + setMountPath(PackageHelper.getSdDir(cid)); } /** New install from existing */ @@ -9564,7 +9482,7 @@ public class PackageManagerService extends IPackageManager.Stub { final File target; if (isExternal()) { - target = Environment.getExternalStorageDirectory(); + target = new UserEnvironment(UserHandle.USER_OWNER).getExternalStorageDirectory(); } else { target = Environment.getDataDirectory(); } @@ -9578,6 +9496,7 @@ public class PackageManagerService extends IPackageManager.Stub { } int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { + // TODO: if already staged, we only need to extract native code if (temp) { createCopyFile(); } else { @@ -9588,12 +9507,12 @@ public class PackageManagerService extends IPackageManager.Stub { PackageHelper.destroySdDir(cid); } - final String newCachePath = imcs.copyPackageToContainer( + final String newMountPath = imcs.copyPackageToContainer( originFile.getAbsolutePath(), cid, getEncryptKey(), isExternal(), isFwdLocked(), deriveAbiOverride(abiOverride, null /* settings */)); - if (newCachePath != null) { - setCachePath(newCachePath); + if (newMountPath != null) { + setMountPath(newMountPath); return PackageManager.INSTALL_SUCCEEDED; } else { return PackageManager.INSTALL_FAILED_CONTAINER_ERROR; @@ -9622,10 +9541,10 @@ public class PackageManagerService extends IPackageManager.Stub { } else { boolean mounted = PackageHelper.isContainerMounted(cid); if (!mounted) { - String newCachePath = PackageHelper.mountSdDir(cid, getEncryptKey(), + String newMountPath = PackageHelper.mountSdDir(cid, getEncryptKey(), Process.SYSTEM_UID); - if (newCachePath != null) { - setCachePath(newCachePath); + if (newMountPath != null) { + setMountPath(newMountPath); } else { return PackageManager.INSTALL_FAILED_CONTAINER_ERROR; } @@ -9636,7 +9555,7 @@ public class PackageManagerService extends IPackageManager.Stub { boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) { String newCacheId = getNextCodePath(oldCodePath, pkg.packageName, "/" + RES_FILE_NAME); - String newCachePath = null; + String newMountPath = null; if (PackageHelper.isContainerMounted(cid)) { // Unmount the container if (!PackageHelper.unMountSdDir(cid)) { @@ -9661,46 +9580,59 @@ public class PackageManagerService extends IPackageManager.Stub { } if (!PackageHelper.isContainerMounted(newCacheId)) { Slog.w(TAG, "Mounting container " + newCacheId); - newCachePath = PackageHelper.mountSdDir(newCacheId, + newMountPath = PackageHelper.mountSdDir(newCacheId, getEncryptKey(), Process.SYSTEM_UID); } else { - newCachePath = PackageHelper.getSdDir(newCacheId); + newMountPath = PackageHelper.getSdDir(newCacheId); } - if (newCachePath == null) { + if (newMountPath == null) { Slog.w(TAG, "Failed to get cache path for " + newCacheId); return false; } Log.i(TAG, "Succesfully renamed " + cid + " to " + newCacheId + - " at new path: " + newCachePath); + " at new path: " + newMountPath); cid = newCacheId; - setCachePath(newCachePath); - - // TODO: extend to support split APKs - pkg.codePath = getCodePath(); - pkg.baseCodePath = getCodePath(); - pkg.splitCodePaths = null; - pkg.applicationInfo.setCodePath(getCodePath()); - pkg.applicationInfo.setBaseCodePath(getCodePath()); - pkg.applicationInfo.setSplitCodePaths(null); - pkg.applicationInfo.setResourcePath(getResourcePath()); - pkg.applicationInfo.setBaseResourcePath(getResourcePath()); - pkg.applicationInfo.setSplitResourcePaths(null); + final File beforeCodeFile = new File(packagePath); + setMountPath(newMountPath); + final File afterCodeFile = new File(packagePath); + + // Reflect the rename in scanned details + pkg.codePath = afterCodeFile.getAbsolutePath(); + pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile, + pkg.baseCodePath); + pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile, + pkg.splitCodePaths); + + // Reflect the rename in app info + pkg.applicationInfo.setCodePath(pkg.codePath); + pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath); + pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths); + pkg.applicationInfo.setResourcePath(pkg.codePath); + pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath); + pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths); return true; } - private void setCachePath(String newCachePath) { - File cachePath = new File(newCachePath); - legacyNativeLibraryDir = new File(cachePath, LIB_DIR_NAME).getPath(); - packagePath = new File(cachePath, RES_FILE_NAME).getPath(); + private void setMountPath(String mountPath) { + final File mountFile = new File(mountPath); - if (isFwdLocked()) { - resourcePath = new File(cachePath, PUBLIC_RES_FILE_NAME).getPath(); + final File monolithicFile = new File(mountFile, RES_FILE_NAME); + if (monolithicFile.exists()) { + packagePath = monolithicFile.getAbsolutePath(); + if (isFwdLocked()) { + resourcePath = new File(mountFile, PUBLIC_RES_FILE_NAME).getAbsolutePath(); + } else { + resourcePath = packagePath; + } } else { + packagePath = mountFile.getAbsolutePath(); resourcePath = packagePath; } + + legacyNativeLibraryDir = new File(mountFile, LIB_DIR_NAME).getAbsolutePath(); } int doPostInstall(int status, int uid) { @@ -9739,23 +9671,43 @@ public class PackageManagerService extends IPackageManager.Stub { PackageHelper.destroySdDir(cid); } - void cleanUpResourcesLI() { - String sourceFile = getCodePath(); - // Remove dex file - if (instructionSets == null) { - throw new IllegalStateException("instructionSet == null"); - } - String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); - for (String dexCodeInstructionSet : dexCodeInstructionSets) { - int retCode = mInstaller.rmdex(sourceFile, dexCodeInstructionSet); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove dex file for package: " - + " at location " - + sourceFile.toString() + ", retcode=" + retCode); - // we don't consider this to be a failure of the core package deletion + private List<String> getAllCodePaths() { + final File codeFile = new File(getCodePath()); + if (codeFile != null && codeFile.exists()) { + try { + final PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0); + return pkg.getAllCodePaths(); + } catch (PackageParserException e) { + // Ignored; we tried our best } } + return Collections.EMPTY_LIST; + } + + void cleanUpResourcesLI() { + // Enumerate all code paths before deleting + cleanUpResourcesLI(getAllCodePaths()); + } + + private void cleanUpResourcesLI(List<String> allCodePaths) { cleanUp(); + + if (!allCodePaths.isEmpty()) { + if (instructionSets == null) { + throw new IllegalStateException("instructionSet == null"); + } + String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); + for (String codePath : allCodePaths) { + for (String dexCodeInstructionSet : dexCodeInstructionSets) { + int retCode = mInstaller.rmdex(codePath, dexCodeInstructionSet); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove dex file for package: " + + " at location " + codePath + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion + } + } + } + } } boolean matchContainer(String app) { @@ -9770,16 +9722,19 @@ public class PackageManagerService extends IPackageManager.Stub { } boolean doPostDeleteLI(boolean delete) { - boolean ret = false; + if (DEBUG_SD_INSTALL) Slog.i(TAG, "doPostDeleteLI() del=" + delete); + final List<String> allCodePaths = getAllCodePaths(); boolean mounted = PackageHelper.isContainerMounted(cid); if (mounted) { // Unmount first - ret = PackageHelper.unMountSdDir(cid); + if (PackageHelper.unMountSdDir(cid)) { + mounted = false; + } } - if (ret && delete) { - cleanUpResourcesLI(); + if (!mounted && delete) { + cleanUpResourcesLI(allCodePaths); } - return ret; + return !mounted; } @Override @@ -10966,6 +10921,7 @@ public class PackageManagerService extends IPackageManager.Stub { outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, getAppDexInstructionSets(ps), isMultiArch(ps)); + if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args); } return true; } @@ -12772,7 +12728,8 @@ public class PackageManagerService extends IPackageManager.Stub { getAppDexInstructionSets(ps), isForwardLocked(ps), isMultiArch(ps)); // The package status is changed only if the code path // matches between settings and the container id. - if (ps.codePathString != null && ps.codePathString.equals(args.getCodePath())) { + if (ps.codePathString != null + && ps.codePathString.startsWith(args.getCodePath())) { if (DEBUG_SD_INSTALL) { Log.i(TAG, "Container : " + cid + " corresponds to pkg : " + pkgName + " at code path: " + ps.codePathString); @@ -12851,7 +12808,7 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } // Check code path here. - if (codePath == null || !codePath.equals(args.getCodePath())) { + if (codePath == null || !codePath.startsWith(args.getCodePath())) { Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath() + " does not match one in settings " + codePath); continue; |