diff options
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; |