From 57dcf5b177b56195421535938544f32d8b591b42 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 18 Jun 2014 17:46:05 -0700 Subject: [PATCH] Slow progress towards APK clusters. Differentiate between "split APKs" and "cluster packages". A cluster package is a directory containing zero or more APKs (base+splits), and a monolithic package is a single APK (base). PackageSetting will use the directory name as its codePath, so track the baseCodePath separately. Clarify documentation in several places. Require that all installers provide file:// URIs through existing hidden APIs; PackageInstaller hasn't been able to read content:// URIs for a long time. Bug: 14975160 Change-Id: I1c6fed1b55205c2474b09871161a98a26669d22e --- .../android/content/pm/ApplicationInfo.java | 1 + .../android/content/pm/PackageParser.java | 67 +-- core/java/android/os/FileUtils.java | 27 +- core/java/android/os/SELinux.java | 19 + .../internal/app/IMediaContainerService.aidl | 26 +- .../com/android/internal/util/ArrayUtils.java | 7 + .../defcontainer/DefaultContainerService.java | 80 ++-- .../server/pm/PackageInstallerSession.java | 4 +- .../android/server/pm/PackageKeySetData.java | 15 +- .../server/pm/PackageManagerService.java | 439 +++++++++--------- .../android/server/pm/PackageSettingBase.java | 6 + 11 files changed, 366 insertions(+), 325 deletions(-) diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index be4e864417553..bb90fd7993ced 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -413,6 +413,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** * Full path to the base APK for this application. */ + // TODO: verify that nobody is doing codePath comparisons against this public String sourceDir; /** diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index f176dfb06b595..8b6f3d4519450 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -279,11 +279,11 @@ public class PackageParser { mMetrics = metrics; } - private static final boolean isPackageFilename(File file) { + public static final boolean isPackageFilename(File file) { return isPackageFilename(file.getName()); } - private static final boolean isPackageFilename(String name) { + public static final boolean isPackageFilename(String name) { return name.endsWith(".apk"); } @@ -552,7 +552,7 @@ public class PackageParser { * Note that this does not perform signature verification; that * must be done separately in {@link #collectCertificates(Package, int)}. */ - public Package parseSplitPackage(File apkDir, int flags) throws PackageParserException { + public Package parseClusterPackage(File apkDir, int flags) throws PackageParserException { final File[] files = apkDir.listFiles(); if (ArrayUtils.isEmpty(files)) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, @@ -600,27 +600,23 @@ public class PackageParser { "Missing base APK in " + apkDir); } - // Always apply deterministic ordering based on splitName - final int size = apks.size(); - - final String[] splitNames = apks.keySet().toArray(new String[size]); - Arrays.sort(splitNames, sSplitNameComparator); - - final File[] splitFiles = new File[size]; - for (int i = 0; i < size; i++) { - splitFiles[i] = apks.get(splitNames[i]); - } - final Package pkg = parseBaseApk(baseFile, flags); if (pkg == null) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse base APK: " + baseFile); } - for (File splitFile : splitFiles) { - parseSplitApk(pkg, splitFile, flags); + // Always apply deterministic ordering based on splitName + final int size = apks.size(); + final String[] splitNames = apks.keySet().toArray(new String[size]); + Arrays.sort(splitNames, sSplitNameComparator); + + for (String splitName : splitNames) { + final File splitFile = apks.get(splitName); + parseSplitApk(pkg, splitFile, splitName, flags); } + pkg.codePath = apkDir.getAbsolutePath(); return pkg; } @@ -632,11 +628,12 @@ public class PackageParser { */ public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { final Package pkg = parseBaseApk(apkFile, flags); - if (pkg != null) { - return pkg; - } else { + if (pkg == null) { throw new PackageParserException(mParseError, "Failed to parse " + apkFile); } + + pkg.codePath = apkFile.getAbsolutePath(); + return pkg; } private Package parseBaseApk(File apkFile, int flags) { @@ -723,19 +720,22 @@ public class PackageParser { parser.close(); assmgr.close(); - pkg.codePath = apkPath; + pkg.baseCodePath = apkPath; pkg.mSignatures = null; return pkg; } - private void parseSplitApk(Package pkg, File apkFile, int flags) throws PackageParserException { - final String apkPath = apkFile.getAbsolutePath(); + private void parseSplitApk(Package pkg, File apkFile, String splitName, int flags) + throws PackageParserException { + final String splitCodePath = apkFile.getAbsolutePath(); mArchiveSourcePath = apkFile.getAbsolutePath(); // TODO: expand split APK parsing + // TODO: extract splitName during parse + pkg.splitNames = ArrayUtils.appendElement(String.class, pkg.splitNames, splitName); pkg.splitCodePaths = ArrayUtils.appendElement(String.class, pkg.splitCodePaths, - apkFile.getAbsolutePath()); + splitCodePath); } /** @@ -748,7 +748,7 @@ public class PackageParser { // TODO: extend to gather digest for split APKs try { - final StrictJarFile jarFile = new StrictJarFile(pkg.codePath); + final StrictJarFile jarFile = new StrictJarFile(pkg.baseCodePath); try { final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); if (je != null) { @@ -773,7 +773,7 @@ public class PackageParser { pkg.mSignatures = null; pkg.mSigningKeys = null; - collectCertificates(pkg, new File(pkg.codePath), flags); + collectCertificates(pkg, new File(pkg.baseCodePath), flags); if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { for (String splitCodePath : pkg.splitCodePaths) { @@ -3692,12 +3692,21 @@ public class PackageParser { public final static class Package { public String packageName; + /** Names of any split APKs, ordered by parsed splitName */ + public String[] splitNames; // TODO: work towards making these paths invariant - /** Base APK */ + /** + * Path where this package was found on disk. For monolithic packages + * this is path to single base APK file; for cluster packages this is + * path to the cluster directory. + */ public String codePath; - /** Split APKs, ordered by parsed splitName */ + + /** Path of base APK */ + public String baseCodePath; + /** Paths of any split APKs, ordered by parsed splitName */ public String[] splitCodePaths; // For now we only support one application per package. @@ -3816,9 +3825,9 @@ public class PackageParser { applicationInfo.uid = -1; } - public Collection getAllCodePaths() { + public List getAllCodePaths() { ArrayList paths = new ArrayList<>(); - paths.add(codePath); + paths.add(baseCodePath); if (!ArrayUtils.isEmpty(splitCodePaths)) { Collections.addAll(paths, splitCodePaths); } diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 1dba77d229ede..d5d5eb8b339d7 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -384,16 +384,18 @@ public class FileUtils { return filePath.startsWith(dirPath); } - public static void deleteContents(File dir) { + public static boolean deleteContents(File dir) { File[] files = dir.listFiles(); + boolean success = true; if (files != null) { for (File file : files) { if (file.isDirectory()) { - deleteContents(file); + success &= deleteContents(file); } - file.delete(); + success &= file.delete(); } } + return success; } /** @@ -411,4 +413,23 @@ public class FileUtils { } return true; } + + public static String rewriteAfterRename(File beforeDir, File afterDir, String path) { + final File result = rewriteAfterRename(beforeDir, afterDir, new File(path)); + return (result != null) ? result.getAbsolutePath() : null; + } + + /** + * Given a path under the "before" directory, rewrite it to live under the + * "after" directory. For example, {@code /before/foo/bar.txt} would become + * {@code /after/foo/bar.txt}. + */ + public static File rewriteAfterRename(File beforeDir, File afterDir, File file) { + if (contains(beforeDir, file)) { + final String splice = file.getAbsolutePath().substring( + beforeDir.getAbsolutePath().length()); + return new File(afterDir, splice); + } + return null; + } } diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java index c9dd5d7ad647e..71d12c6d045d2 100644 --- a/core/java/android/os/SELinux.java +++ b/core/java/android/os/SELinux.java @@ -171,4 +171,23 @@ public class SELinux { return false; } } + + /** + * Recursively restores all files under the given path to their default + * SELinux security context. If the system is not compiled with SELinux, + * then {@code true} is automatically returned. If SELinux is compiled in, + * but disabled, then {@code true} is returned. + * + * @return a boolean indicating whether the relabeling succeeded. + */ + public static boolean restoreconTree(File dir) { + final File[] files = dir.listFiles(); + boolean success = true; + if (files != null) { + for (File file : files) { + success &= restorecon(file); + } + } + return success; + } } diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl index 77f0dec00c711..3cd0417f2dcdc 100644 --- a/core/java/com/android/internal/app/IMediaContainerService.aidl +++ b/core/java/com/android/internal/app/IMediaContainerService.aidl @@ -16,27 +16,25 @@ package com.android.internal.app; -import android.net.Uri; import android.os.ParcelFileDescriptor; import android.content.pm.ContainerEncryptionParams; import android.content.pm.PackageInfoLite; import android.content.res.ObbInfo; interface IMediaContainerService { - String copyResourceToContainer(in Uri packageURI, String containerId, String key, + String copyResourceToContainer(String packagePath, String containerId, String key, String resFileName, String publicResFileName, boolean isExternal, - boolean isForwardLocked, in String abiOverride); - int copyResource(in Uri packageURI, in ContainerEncryptionParams encryptionParams, + boolean isForwardLocked, String abiOverride); + int copyResource(String packagePath, in ContainerEncryptionParams encryptionParams, in ParcelFileDescriptor outStream); - PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold, - in String abiOverride); - boolean checkInternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in long threshold); - boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in String abiOverride); - ObbInfo getObbInfo(in String filename); - long calculateDirectorySize(in String directory); + PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, long threshold, + String abiOverride); + boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked, long threshold); + boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked, String abiOverride); + ObbInfo getObbInfo(String filename); + long calculateDirectorySize(String directory); /** Return file system stats: [0] is total bytes, [1] is available bytes */ - long[] getFileSystemStats(in String path); - void clearDirectory(in String directory); - long calculateInstalledSize(in String packagePath, boolean isForwardLocked, - in String abiOverride); + long[] getFileSystemStats(String path); + void clearDirectory(String directory); + long calculateInstalledSize(String packagePath, boolean isForwardLocked, String abiOverride); } diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index d66ef83ebcad1..7f6159dec44b6 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -138,6 +138,7 @@ public class ArrayUtils * not found. */ public static int indexOf(T[] array, T value) { + if (array == null) return -1; for (int i = 0; i < array.length; i++) { if (array[i] == null) { if (value == null) return i; @@ -161,6 +162,7 @@ public class ArrayUtils } public static boolean contains(int[] array, int value) { + if (array == null) return false; for (int element : array) { if (element == value) { return true; @@ -170,6 +172,7 @@ public class ArrayUtils } public static boolean contains(long[] array, long value) { + if (array == null) return false; for (long element : array) { if (element == value) { return true; @@ -325,4 +328,8 @@ public class ArrayUtils } return cur; } + + public static long[] cloneOrNull(long[] array) { + return (array != null) ? array.clone() : null; + } } diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 168fb417f9e21..74ff3b146e451 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; import android.content.pm.PackageParser.PackageParserException; import android.content.res.ObbInfo; import android.content.res.ObbScanner; -import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Environment.UserEnvironment; @@ -45,7 +44,6 @@ import android.provider.Settings; import android.system.ErrnoException; import android.system.Os; import android.system.StructStatVfs; -import android.util.DisplayMetrics; import android.util.Slog; import com.android.internal.app.IMediaContainerService; @@ -53,6 +51,9 @@ import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.NativeLibraryHelper.ApkHandle; import com.android.internal.content.PackageHelper; +import libcore.io.IoUtils; +import libcore.io.Streams; + import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -71,9 +72,6 @@ import javax.crypto.CipherInputStream; import javax.crypto.Mac; import javax.crypto.NoSuchPaddingException; -import libcore.io.IoUtils; -import libcore.io.Streams; - /* * This service copies a downloaded apk to a file passed in as * a ParcelFileDescriptor or to a newly created container specified @@ -101,14 +99,14 @@ public class DefaultContainerService extends IntentService { * @return Returns the new cache path where the resource has been copied into * */ - public String copyResourceToContainer(final Uri packageURI, final String cid, + @Override + public String copyResourceToContainer(final String packagePath, final String cid, final String key, final String resFileName, final String publicResFileName, boolean isExternal, boolean isForwardLocked, String abiOverride) { - if (packageURI == null || cid == null) { + if (packagePath == null || cid == null) { return null; } - if (isExternal) { // Make sure the sdcard is mounted. String status = Environment.getExternalStorageState(); @@ -120,11 +118,11 @@ public class DefaultContainerService extends IntentService { ApkHandle handle = null; try { - handle = ApkHandle.create(packageURI.getPath()); - return copyResourceInner(packageURI, cid, key, resFileName, publicResFileName, + handle = ApkHandle.create(packagePath); + return copyResourceInner(packagePath, cid, key, resFileName, publicResFileName, isExternal, isForwardLocked, handle, abiOverride); } catch (IOException ioe) { - Slog.w(TAG, "Problem opening APK: " + packageURI.getPath()); + Slog.w(TAG, "Problem opening APK: " + packagePath); return null; } finally { IoUtils.closeQuietly(handle); @@ -142,9 +140,10 @@ public class DefaultContainerService extends IntentService { * @return returns status code according to those in * {@link PackageManager} */ - public int copyResource(final Uri packageURI, ContainerEncryptionParams encryptionParams, - ParcelFileDescriptor outStream) { - if (packageURI == null || outStream == null) { + @Override + public int copyResource(final String packagePath, + ContainerEncryptionParams encryptionParams, ParcelFileDescriptor outStream) { + if (packagePath == null || outStream == null) { return PackageManager.INSTALL_FAILED_INVALID_URI; } @@ -152,18 +151,18 @@ public class DefaultContainerService extends IntentService { = new ParcelFileDescriptor.AutoCloseOutputStream(outStream); try { - copyFile(packageURI, autoOut, encryptionParams); + copyFile(packagePath, autoOut, encryptionParams); return PackageManager.INSTALL_SUCCEEDED; } catch (FileNotFoundException e) { - Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " FNF: " + Slog.e(TAG, "Could not copy URI " + packagePath.toString() + " FNF: " + e.getMessage()); return PackageManager.INSTALL_FAILED_INVALID_URI; } catch (IOException e) { - Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " IO: " + Slog.e(TAG, "Could not copy URI " + packagePath.toString() + " IO: " + e.getMessage()); return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } catch (DigestException e) { - Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " Security: " + Slog.e(TAG, "Could not copy URI " + packagePath.toString() + " Security: " + e.getMessage()); return PackageManager.INSTALL_FAILED_INVALID_APK; } finally { @@ -217,9 +216,9 @@ public class DefaultContainerService extends IntentService { } @Override - public boolean checkInternalFreeStorage(Uri packageUri, boolean isForwardLocked, + public boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked, long threshold) throws RemoteException { - final File apkFile = new File(packageUri.getPath()); + final File apkFile = new File(packagePath); try { return isUnderInternalThreshold(apkFile, isForwardLocked, threshold); } catch (IOException e) { @@ -228,9 +227,9 @@ public class DefaultContainerService extends IntentService { } @Override - public boolean checkExternalFreeStorage(Uri packageUri, boolean isForwardLocked, + public boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked, String abiOverride) throws RemoteException { - final File apkFile = new File(packageUri.getPath()); + final File apkFile = new File(packagePath); try { return isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride); } catch (IOException e) { @@ -238,6 +237,7 @@ public class DefaultContainerService extends IntentService { } } + @Override public ObbInfo getObbInfo(String filename) { try { return ObbScanner.getObbInfo(filename); @@ -347,11 +347,11 @@ public class DefaultContainerService extends IntentService { return mBinder; } - private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName, + private String copyResourceInner(String packagePath, String newCid, String key, String resFileName, String publicResFileName, boolean isExternal, boolean isForwardLocked, ApkHandle handle, String abiOverride) { // The .apk file - String codePath = packageURI.getPath(); + String codePath = packagePath; File codeFile = new File(codePath); String[] abiList = Build.SUPPORTED_ABIS; @@ -492,39 +492,13 @@ public class DefaultContainerService extends IntentService { } } - private void copyFile(Uri pPackageURI, OutputStream outStream, + private void copyFile(String packagePath, OutputStream outStream, ContainerEncryptionParams encryptionParams) throws FileNotFoundException, IOException, DigestException { - String scheme = pPackageURI.getScheme(); InputStream inStream = null; try { - if (scheme == null || scheme.equals("file")) { - final InputStream is = new FileInputStream(new File(pPackageURI.getPath())); - inStream = new BufferedInputStream(is); - } else if (scheme.equals("content")) { - final ParcelFileDescriptor fd; - try { - fd = getContentResolver().openFileDescriptor(pPackageURI, "r"); - } catch (FileNotFoundException e) { - Slog.e(TAG, "Couldn't open file descriptor from download service. " - + "Failed with exception " + e); - throw e; - } - - if (fd == null) { - Slog.e(TAG, "Provider returned no file descriptor for " + - pPackageURI.toString()); - throw new FileNotFoundException("provider returned no file descriptor"); - } else { - if (localLOGV) { - Slog.i(TAG, "Opened file descriptor from download service."); - } - inStream = new ParcelFileDescriptor.AutoCloseInputStream(fd); - } - } else { - Slog.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); - throw new FileNotFoundException("Package URI is not 'file:' or 'content:'"); - } + final InputStream is = new FileInputStream(new File(packagePath)); + inStream = new BufferedInputStream(is); /* * If this resource is encrypted, get the decrypted stream version diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 0658eeea93b5c..11e546f5e9992 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -41,6 +41,7 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.SELinux; +import android.os.UserHandle; import android.system.ErrnoException; import android.system.OsConstants; import android.system.StructStat; @@ -272,7 +273,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } }; - mPm.installStage(mPackageName, this.sessionDir, localObserver, params.installFlags); + mPm.installStage(mPackageName, this.sessionDir, localObserver, params, installerPackageName, + installerUid, new UserHandle(userId)); } /** diff --git a/services/core/java/com/android/server/pm/PackageKeySetData.java b/services/core/java/com/android/server/pm/PackageKeySetData.java index d47080732d0f0..11ed5d22a6e74 100644 --- a/services/core/java/com/android/server/pm/PackageKeySetData.java +++ b/services/core/java/com/android/server/pm/PackageKeySetData.java @@ -18,7 +18,6 @@ package com.android.server.pm; import com.android.internal.util.ArrayUtils; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -35,19 +34,17 @@ public class PackageKeySetData { private long[] mDefinedKeySets; - private final Map mKeySetAliases; + private final Map mKeySetAliases = new HashMap(); PackageKeySetData() { mProperSigningKeySet = KEYSET_UNASSIGNED; - mKeySetAliases = new HashMap(); } PackageKeySetData(PackageKeySetData original) { - mSigningKeySets = original.getSigningKeySets().clone(); - mUpgradeKeySets = original.getUpgradeKeySets().clone(); - mDefinedKeySets = original.getDefinedKeySets().clone(); - mKeySetAliases = new HashMap(); - mKeySetAliases.putAll(original.getAliases()); + mSigningKeySets = ArrayUtils.cloneOrNull(original.mSigningKeySets); + mUpgradeKeySets = ArrayUtils.cloneOrNull(original.mUpgradeKeySets); + mDefinedKeySets = ArrayUtils.cloneOrNull(original.mDefinedKeySets); + mKeySetAliases.putAll(original.mKeySetAliases); } protected void setProperSigningKeySet(long ks) { @@ -149,4 +146,4 @@ public class PackageKeySetData { /* should never be the case that mUpgradeKeySets.length == 0 */ return (mUpgradeKeySets != null && mUpgradeKeySets.length > 0); } -} \ No newline at end of file +} diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index bc45ae0fe4ded..2d9176d591528 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.content.pm.PackageParser.isPackageFilename; import static android.os.Process.PACKAGE_INFO_GID; import static android.os.Process.SYSTEM_UID; import static android.system.OsConstants.S_IRGRP; @@ -46,6 +47,7 @@ import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.EventLogTags; import com.android.server.IntentResolver; @@ -92,6 +94,7 @@ import android.content.pm.ManifestDigest; import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; +import android.content.pm.PackageInstallerParams; import android.content.pm.PackageManager; import android.content.pm.PackageParser.ActivityIntentInfo; import android.content.pm.PackageParser.PackageParserException; @@ -349,6 +352,7 @@ public class PackageManagerService extends IPackageManager.Stub { // LOCK HELD. Can be called with mInstallLock held. final Installer mInstaller; + /** Directory where installed third-party apps stored */ final File mAppInstallDir; /** @@ -361,6 +365,7 @@ public class PackageManagerService extends IPackageManager.Stub { // apps. final File mDrmAppPrivateInstallDir; + /** Directory where third-party apps are staged before install */ final File mAppStagingDir; // ---------------------------------------------------------------- @@ -1143,17 +1148,18 @@ public class PackageManagerService extends IPackageManager.Stub { if ((state != null) && !state.timeoutExtended()) { final InstallArgs args = state.getInstallArgs(); - Slog.i(TAG, "Verification timed out for " + args.packageURI.toString()); + final Uri fromUri = Uri.fromFile(args.fromFile); + + Slog.i(TAG, "Verification timed out for " + fromUri); mPendingVerification.remove(verificationId); int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) { - Slog.i(TAG, "Continuing with installation of " - + args.packageURI.toString()); + Slog.i(TAG, "Continuing with installation of " + fromUri); state.setVerifierResponse(Binder.getCallingUid(), PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT); - broadcastPackageVerified(verificationId, args.packageURI, + broadcastPackageVerified(verificationId, fromUri, PackageManager.VERIFICATION_ALLOW, state.getInstallArgs().getUser()); try { @@ -1162,7 +1168,7 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.e(TAG, "Could not contact the ContainerService"); } } else { - broadcastPackageVerified(verificationId, args.packageURI, + broadcastPackageVerified(verificationId, fromUri, PackageManager.VERIFICATION_REJECT, state.getInstallArgs().getUser()); } @@ -1189,11 +1195,12 @@ public class PackageManagerService extends IPackageManager.Stub { mPendingVerification.remove(verificationId); final InstallArgs args = state.getInstallArgs(); + final Uri fromUri = Uri.fromFile(args.fromFile); int ret; if (state.isInstallAllowed()) { ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; - broadcastPackageVerified(verificationId, args.packageURI, + broadcastPackageVerified(verificationId, fromUri, response.code, state.getInstallArgs().getUser()); try { ret = args.copyApk(mContainerService, true); @@ -1528,14 +1535,14 @@ public class PackageManagerService extends IPackageManager.Stub { // overlay packages if they reside in VENDOR_OVERLAY_DIR. File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR); mVendorOverlayInstallObserver = new AppDirObserver( - vendorOverlayDir.getPath(), OBSERVER_EVENTS, true, false); + vendorOverlayDir.getPath(), OBSERVER_EVENTS, true, false); mVendorOverlayInstallObserver.startWatching(); scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_TRUSTED_OVERLAY, 0); // Find base frameworks (resource packages without code). mFrameworkInstallObserver = new AppDirObserver( - frameworkDir.getPath(), OBSERVER_EVENTS, true, false); + frameworkDir.getPath(), OBSERVER_EVENTS, true, false); mFrameworkInstallObserver.startWatching(); scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR @@ -1547,14 +1554,14 @@ public class PackageManagerService extends IPackageManager.Stub { mPrivilegedInstallObserver = new AppDirObserver( privilegedAppDir.getPath(), OBSERVER_EVENTS, true, true); mPrivilegedInstallObserver.startWatching(); - scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM - | PackageParser.PARSE_IS_SYSTEM_DIR - | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0); + scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM + | PackageParser.PARSE_IS_SYSTEM_DIR + | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0); // Collect ordinary system packages. File systemAppDir = new File(Environment.getRootDirectory(), "app"); mSystemInstallObserver = new AppDirObserver( - systemAppDir.getPath(), OBSERVER_EVENTS, true, false); + systemAppDir.getPath(), OBSERVER_EVENTS, true, false); mSystemInstallObserver.startWatching(); scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); @@ -1567,7 +1574,7 @@ public class PackageManagerService extends IPackageManager.Stub { // failed to look up canonical path, continue with original one } mVendorInstallObserver = new AppDirObserver( - vendorAppDir.getPath(), OBSERVER_EVENTS, true, false); + vendorAppDir.getPath(), OBSERVER_EVENTS, true, false); mVendorInstallObserver.startWatching(); scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); @@ -1825,6 +1832,10 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanupInstallFailedPackage(PackageSetting ps) { Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name); removeDataDirsLI(ps.name); + + // TODO: try cleaning up codePath directory contents first, since it + // might be a cluster + if (ps.codePath != null) { if (!ps.codePath.delete()) { Slog.w(TAG, "Unable to remove old code file: " + ps.codePath); @@ -2071,15 +2082,12 @@ public class PackageManagerService extends IPackageManager.Stub { if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) { return null; } - // App code is gone, so we aren't worried about split paths + // Only data remains, so we aren't worried about code paths pkg = new PackageParser.Package(packageName); pkg.applicationInfo.packageName = packageName; pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY; - pkg.applicationInfo.sourceDir = ps.codePathString; - pkg.applicationInfo.publicSourceDir = ps.resourcePathString; pkg.applicationInfo.dataDir = getDataPathForPackage(packageName, 0).getPath(); - pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; pkg.applicationInfo.cpuAbi = ps.cpuAbiString; } return generatePackageInfo(pkg, flags, userId); @@ -4041,20 +4049,21 @@ public class PackageManagerService extends IPackageManager.Stub { private boolean createIdmapForPackagePairLI(PackageParser.Package pkg, PackageParser.Package opkg) { if (!opkg.mTrustedOverlay) { - Slog.w(TAG, "Skipping target and overlay pair " + pkg.codePath + " and " + - opkg.codePath + ": overlay not trusted"); + Slog.w(TAG, "Skipping target and overlay pair " + pkg.baseCodePath + " and " + + opkg.baseCodePath + ": overlay not trusted"); return false; } HashMap overlaySet = mOverlays.get(pkg.packageName); if (overlaySet == null) { - Slog.e(TAG, "was about to create idmap for " + pkg.codePath + " and " + - opkg.codePath + " but target package has no known overlays"); + Slog.e(TAG, "was about to create idmap for " + pkg.baseCodePath + " and " + + opkg.baseCodePath + " but target package has no known overlays"); return false; } final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); // TODO: generate idmap for split APKs - if (mInstaller.idmap(pkg.codePath, opkg.codePath, sharedGid) != 0) { - Slog.e(TAG, "Failed to generate idmap for " + pkg.codePath + " and " + opkg.codePath); + if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid) != 0) { + Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and " + + opkg.baseCodePath); return false; } PackageParser.Package[] overlayArray = @@ -4075,8 +4084,8 @@ public class PackageManagerService extends IPackageManager.Stub { } private void scanDirLI(File dir, int flags, int scanMode, long currentTime) { - String[] files = dir.list(); - if (files == null) { + final File[] files = dir.listFiles(); + if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + dir); return; } @@ -4086,10 +4095,8 @@ public class PackageManagerService extends IPackageManager.Stub { + " flags=0x" + Integer.toHexString(flags)); } - int i; - for (i=0; i=0; i--) { if (pkgInfo.permissions.get(i).info.name.equals(perm)) { @@ -7701,8 +7702,13 @@ public class PackageManagerService extends IPackageManager.Stub { verificationParams.setInstallerUid(uid); + if (!"file".equals(packageURI.getScheme())) { + throw new UnsupportedOperationException("Only file:// URIs are supported"); + } + final File fromFile = new File(packageURI.getPath()); + final Message msg = mHandler.obtainMessage(INIT_COPY); - msg.obj = new InstallParams(packageURI, observer, observer2, filteredFlags, + msg.obj = new InstallParams(fromFile, observer, observer2, filteredFlags, installerPackageName, verificationParams, encryptionParams, user, packageAbiOverride); mHandler.sendMessage(msg); @@ -7820,11 +7826,12 @@ public class PackageManagerService extends IPackageManager.Stub { } } - void installStage(String basePackageName, File stageDir, IPackageInstallObserver2 observer, - int flags) { - // TODO: install stage! + void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer2, + PackageInstallerParams params, String installerPackageName, int installerUid, + UserHandle user) { + Slog.e(TAG, "TODO: install stage!"); try { - observer.packageInstalled(basePackageName, null, + observer2.packageInstalled(packageName, null, PackageManager.INSTALL_FAILED_INTERNAL_ERROR); } catch (RemoteException ignored) { } @@ -8407,30 +8414,38 @@ public class PackageManagerService extends IPackageManager.Stub { } class InstallParams extends HandlerParams { + /** + * Location where install is coming from, before it has been + * copied/renamed into place. This could be a single monolithic APK + * file, or a cluster directory. This location may be untrusted. + */ + private final File mFromFile; + + /** + * Local copy of {@link #mFromFile}, if generated. + */ + private File mLocalFromFile; + final IPackageInstallObserver observer; final IPackageInstallObserver2 observer2; int flags; - - private final Uri mPackageURI; final String installerPackageName; final VerificationParams verificationParams; private InstallArgs mArgs; private int mRet; - private File mTempPackage; final ContainerEncryptionParams encryptionParams; final String packageAbiOverride; final String packageInstructionSetOverride; - InstallParams(Uri packageURI, - IPackageInstallObserver observer, IPackageInstallObserver2 observer2, - int flags, String installerPackageName, VerificationParams verificationParams, - ContainerEncryptionParams encryptionParams, UserHandle user, - String packageAbiOverride) { + InstallParams(File fromFile, IPackageInstallObserver observer, + IPackageInstallObserver2 observer2, int flags, String installerPackageName, + VerificationParams verificationParams, ContainerEncryptionParams encryptionParams, + UserHandle user, String packageAbiOverride) { super(user); - this.mPackageURI = packageURI; - this.flags = flags; + mFromFile = Preconditions.checkNotNull(fromFile); this.observer = observer; this.observer2 = observer2; + this.flags = flags; this.installerPackageName = installerPackageName; this.verificationParams = verificationParams; this.encryptionParams = encryptionParams; @@ -8443,7 +8458,7 @@ public class PackageManagerService extends IPackageManager.Stub { public String toString() { return "InstallParams{" + Integer.toHexString(System.identityHashCode(this)) - + " " + mPackageURI + "}"; + + " " + mFromFile + "}"; } public ManifestDigest getManifestDigest() { @@ -8543,76 +8558,55 @@ public class PackageManagerService extends IPackageManager.Stub { Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed"); } - try { - mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, mPackageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); - - final File packageFile; - if (encryptionParams != null || !"file".equals(mPackageURI.getScheme())) { - mTempPackage = createTempPackageFile(mDrmAppPrivateInstallDir); - if (mTempPackage != null) { - ParcelFileDescriptor out; - try { - out = ParcelFileDescriptor.open(mTempPackage, - ParcelFileDescriptor.MODE_READ_WRITE); - } catch (FileNotFoundException e) { - out = null; - Slog.e(TAG, "Failed to create temporary file for : " + mPackageURI); - } - - // Make a temporary file for decryption. - ret = mContainerService - .copyResource(mPackageURI, encryptionParams, out); + if (encryptionParams != null) { + // Make a temporary file for decryption. + mLocalFromFile = createTempPackageFile(mDrmAppPrivateInstallDir); + if (mLocalFromFile != null) { + ParcelFileDescriptor out = null; + try { + out = ParcelFileDescriptor.open(mLocalFromFile, + ParcelFileDescriptor.MODE_READ_WRITE); + ret = mContainerService.copyResource(mFromFile.getAbsolutePath(), + encryptionParams, out); + } catch (FileNotFoundException e) { + Slog.e(TAG, "Failed to create temporary file for: " + mFromFile); + } finally { IoUtils.closeQuietly(out); - - packageFile = mTempPackage; - - FileUtils.setPermissions(packageFile.getAbsolutePath(), - FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP - | FileUtils.S_IROTH, - -1, -1); - } else { - packageFile = null; } - } else { - packageFile = new File(mPackageURI.getPath()); - } - if (packageFile != null) { - // Remote call to find out default install location - final String packageFilePath = packageFile.getAbsolutePath(); - pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags, - lowThreshold, packageAbiOverride); - - /* - * If we have too little free space, try to free cache - * before giving up. - */ - if (pkgLite.recommendedInstallLocation - == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { - final long size = mContainerService.calculateInstalledSize( - packageFilePath, isForwardLocked(), packageAbiOverride); - if (mInstaller.freeCache(size + lowThreshold) >= 0) { - pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, - flags, lowThreshold, packageAbiOverride); - } - /* - * The cache free must have deleted the file we - * downloaded to install. - * - * TODO: fix the "freeCache" call to not delete - * the file we care about. - */ - if (pkgLite.recommendedInstallLocation - == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { - pkgLite.recommendedInstallLocation - = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; - } - } + FileUtils.setPermissions(mLocalFromFile, 0644, -1, -1); + } + } + + // Remote call to find out default install location + final String fromPath = getFromFile().getAbsolutePath(); + pkgLite = mContainerService.getMinimalPackageInfo(fromPath, flags, lowThreshold, + packageAbiOverride); + + /* + * If we have too little free space, try to free cache + * before giving up. + */ + if (pkgLite.recommendedInstallLocation + == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { + final long size = mContainerService.calculateInstalledSize( + fromPath, isForwardLocked(), packageAbiOverride); + if (mInstaller.freeCache(size + lowThreshold) >= 0) { + pkgLite = mContainerService.getMinimalPackageInfo(fromPath, + flags, lowThreshold, packageAbiOverride); + } + /* + * The cache free must have deleted the file we + * downloaded to install. + * + * TODO: fix the "freeCache" call to not delete + * the file we care about. + */ + if (pkgLite.recommendedInstallLocation + == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { + pkgLite.recommendedInstallLocation + = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } - } finally { - mContext.revokeUriPermission(mPackageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); } } @@ -8672,9 +8666,10 @@ public class PackageManagerService extends IPackageManager.Stub { final int requiredUid = mRequiredVerifierPackage == null ? -1 : getPackageUid(mRequiredVerifierPackage, userIdentifier); if (requiredUid != -1 && isVerificationEnabled(userIdentifier, flags)) { + // TODO: send verifier the install session instead of uri final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); - verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE); + verification.setDataAndType(Uri.fromFile(getFromFile()), PACKAGE_MIME_TYPE); verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); final List receivers = queryIntentReceivers(verification, @@ -8802,10 +8797,9 @@ public class PackageManagerService extends IPackageManager.Stub { if (mArgs != null) { processPendingInstall(mArgs, mRet); - if (mTempPackage != null) { - if (!mTempPackage.delete()) { - Slog.w(TAG, "Couldn't delete temporary file: " + - mTempPackage.getAbsolutePath()); + if (mLocalFromFile != null) { + if (!mLocalFromFile.delete()) { + Slog.w(TAG, "Couldn't delete temporary file: " + mLocalFromFile); } } } @@ -8821,11 +8815,11 @@ public class PackageManagerService extends IPackageManager.Stub { return (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0; } - public Uri getPackageUri() { - if (mTempPackage != null) { - return Uri.fromFile(mTempPackage); + public File getFromFile() { + if (mLocalFromFile != null) { + return mLocalFromFile; } else { - return mPackageURI; + return mFromFile; } } } @@ -8856,8 +8850,9 @@ public class PackageManagerService extends IPackageManager.Stub { this.packageName = packageName; this.uid = uid; if (srcArgs != null) { - Uri packageUri = Uri.fromFile(new File(srcArgs.getCodePath())); - targetArgs = createInstallArgs(packageUri, flags, packageName, dataDir, instructionSet); + final String codePath = srcArgs.getCodePath(); + targetArgs = createInstallArgsForMoveTarget(codePath, flags, packageName, dataDir, + instructionSet); } else { targetArgs = null; } @@ -8958,6 +8953,8 @@ public class PackageManagerService extends IPackageManager.Stub { } private InstallArgs createInstallArgs(InstallParams params) { + // TODO: extend to support incoming zero-copy locations + if (installOnSd(params.flags) || params.isForwardLocked()) { return new AsecInstallArgs(params); } else { @@ -8965,14 +8962,18 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath, - String nativeLibraryPath, String instructionSet) { + /** + * Create args that describe an existing installed package. Typically used + * when cleaning up old installs, or used as a move source. + */ + private InstallArgs createInstallArgsForExisting(int flags, String codePath, + String resourcePath, String nativeLibraryPath, String instructionSet) { final boolean isInAsec; if (installOnSd(flags)) { /* Apps on SD card are always in ASEC containers. */ isInAsec = true; } else if (installForwardLocked(flags) - && !fullCodePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) { + && !codePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) { /* * Forward-locked apps are only in ASEC containers if they're the * new style @@ -8983,44 +8984,49 @@ public class PackageManagerService extends IPackageManager.Stub { } if (isInAsec) { - return new AsecInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath, + return new AsecInstallArgs(codePath, resourcePath, nativeLibraryPath, instructionSet, installOnSd(flags), installForwardLocked(flags)); } else { - return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath, - instructionSet); + return new FileInstallArgs(codePath, resourcePath, nativeLibraryPath, instructionSet); } } - // Used by package mover - private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName, String dataDir, - String instructionSet) { + private InstallArgs createInstallArgsForMoveTarget(String codePath, int flags, String pkgName, + String dataDir, String instructionSet) { + final File codeFile = new File(codePath); if (installOnSd(flags) || installForwardLocked(flags)) { - String cid = getNextCodePath(packageURI.getPath(), pkgName, "/" + String cid = getNextCodePath(codePath, pkgName, "/" + AsecInstallArgs.RES_FILE_NAME); - return new AsecInstallArgs(packageURI, cid, instructionSet, installOnSd(flags), + return new AsecInstallArgs(codeFile, cid, instructionSet, installOnSd(flags), installForwardLocked(flags)); } else { - return new FileInstallArgs(packageURI, pkgName, dataDir, instructionSet); + return new FileInstallArgs(codeFile, pkgName, dataDir, instructionSet); } } static abstract class InstallArgs { + /** + * Location where install is coming from, before it has been + * copied/renamed into place. This could be a single monolithic APK + * file, or a cluster directory. This location is typically untrusted. + */ + final File fromFile; + final IPackageInstallObserver observer; final IPackageInstallObserver2 observer2; // Always refers to PackageManager flags only final int flags; - final Uri packageURI; final String installerPackageName; final ManifestDigest manifestDigest; final UserHandle user; final String instructionSet; final String abiOverride; - InstallArgs(Uri packageURI, - IPackageInstallObserver observer, IPackageInstallObserver2 observer2, - int flags, String installerPackageName, ManifestDigest manifestDigest, - UserHandle user, String instructionSet, String abiOverride) { - this.packageURI = packageURI; + InstallArgs(File fromFile, IPackageInstallObserver observer, + IPackageInstallObserver2 observer2, int flags, String installerPackageName, + ManifestDigest manifestDigest, UserHandle user, String instructionSet, + String abiOverride) { + this.fromFile = fromFile; this.flags = flags; this.observer = observer; this.observer2 = observer2; @@ -9031,24 +9037,23 @@ public class PackageManagerService extends IPackageManager.Stub { this.abiOverride = abiOverride; } - abstract void createCopyFile(); abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException; abstract int doPreInstall(int status); abstract boolean doRename(int status, String pkgName, String oldCodePath); - abstract int doPostInstall(int status, int uid); + + /** @see PackageSettingBase#codePathString */ abstract String getCodePath(); + /** @see PackageSettingBase#resourcePathString */ abstract String getResourcePath(); + /** @see PackageSettingBase#nativeLibraryPathString */ abstract String getNativeLibraryPath(); + // Need installer lock especially for dex file removal. abstract void cleanUpResourcesLI(); abstract boolean doPostDeleteLI(boolean delete); abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException; - String[] getSplitCodePaths() { - return null; - } - /** * Called before the source arguments are copied. This is used mostly * for MoveParams when it needs to read the source file to put it in the @@ -9078,20 +9083,27 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /** + * Logic to handle installation of non-ASEC applications, including copying + * and renaming logic. + */ class FileInstallArgs extends InstallArgs { + // TODO: teach about handling cluster directories + File installDir; String codeFileName; String resourceFileName; String libraryPath; boolean created = false; + /** New install */ FileInstallArgs(InstallParams params) { - super(params.getPackageUri(), params.observer, params.observer2, params.flags, - params.installerPackageName, params.getManifestDigest(), - params.getUser(), params.packageInstructionSetOverride, - params.packageAbiOverride); + super(params.getFromFile(), params.observer, params.observer2, params.flags, + params.installerPackageName, params.getManifestDigest(), params.getUser(), + params.packageInstructionSetOverride, params.packageAbiOverride); } + /** Existing install */ FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, String instructionSet) { super(null, null, null, 0, null, null, null, instructionSet, null); @@ -9102,8 +9114,9 @@ public class PackageManagerService extends IPackageManager.Stub { libraryPath = nativeLibraryPath; } - FileInstallArgs(Uri packageURI, String pkgName, String dataDir, String instructionSet) { - super(packageURI, null, null, 0, null, null, null, instructionSet, null); + /** New install from existing */ + FileInstallArgs(File fromFile, String pkgName, String dataDir, String instructionSet) { + super(fromFile, null, null, 0, null, null, null, instructionSet, null); installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; String apkName = getNextCodePath(null, pkgName, ".apk"); codeFileName = new File(installDir, apkName + ".apk").getPath(); @@ -9128,13 +9141,8 @@ public class PackageManagerService extends IPackageManager.Stub { lowThreshold = dsm.getMemoryLowThreshold(); } - try { - mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); - return imcs.checkInternalFreeStorage(packageURI, isFwdLocked(), lowThreshold); - } finally { - mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - } + return imcs.checkInternalFreeStorage(fromFile.getAbsolutePath(), isFwdLocked(), + lowThreshold); } void createCopyFile() { @@ -9175,12 +9183,9 @@ public class PackageManagerService extends IPackageManager.Stub { // Copy the resource now int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; try { - mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); - ret = imcs.copyResource(packageURI, null, out); + ret = imcs.copyResource(fromFile.getAbsolutePath(), null, out); } finally { IoUtils.closeQuietly(out); - mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } if (isFwdLocked()) { @@ -9432,7 +9437,13 @@ public class PackageManagerService extends IPackageManager.Stub { return subStr1.substring(sidx+1, eidx); } + /** + * Logic to handle installation of ASEC applications, including copying and + * 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"; @@ -9441,13 +9452,14 @@ public class PackageManagerService extends IPackageManager.Stub { String resourcePath; String libraryPath; + /** New install */ AsecInstallArgs(InstallParams params) { - super(params.getPackageUri(), params.observer, params.observer2, params.flags, - params.installerPackageName, params.getManifestDigest(), - params.getUser(), params.packageInstructionSetOverride, - params.packageAbiOverride); + super(params.getFromFile(), params.observer, params.observer2, params.flags, + params.installerPackageName, params.getManifestDigest(), params.getUser(), + params.packageInstructionSetOverride, params.packageAbiOverride); } + /** Existing install */ AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, String instructionSet, boolean isExternal, boolean isForwardLocked) { super(null, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) @@ -9469,9 +9481,10 @@ public class PackageManagerService extends IPackageManager.Stub { setCachePath(PackageHelper.getSdDir(cid)); } - AsecInstallArgs(Uri packageURI, String cid, String instructionSet, + /** New install from existing */ + AsecInstallArgs(File fromFile, String cid, String instructionSet, boolean isExternal, boolean isForwardLocked) { - super(packageURI, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) + super(fromFile, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null, null, instructionSet, null); this.cid = cid; @@ -9482,13 +9495,8 @@ public class PackageManagerService extends IPackageManager.Stub { } boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { - try { - mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); - return imcs.checkExternalFreeStorage(packageURI, isFwdLocked(), abiOverride); - } finally { - mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - } + return imcs.checkExternalFreeStorage(fromFile.getAbsolutePath(), isFwdLocked(), + abiOverride); } private final boolean isExternal() { @@ -9506,16 +9514,9 @@ public class PackageManagerService extends IPackageManager.Stub { PackageHelper.destroySdDir(cid); } - final String newCachePath; - try { - mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); - newCachePath = imcs.copyResourceToContainer(packageURI, cid, getEncryptKey(), - RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked(), - abiOverride); - } finally { - mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - } + final String newCachePath = imcs.copyResourceToContainer(fromFile.getAbsolutePath(), + cid, getEncryptKey(), RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), + isFwdLocked(), abiOverride); if (newCachePath != null) { setCachePath(newCachePath); @@ -10078,7 +10079,7 @@ public class PackageManagerService extends IPackageManager.Stub { // We didn't need to disable the .apk as a current system package, // which means we are replacing another update that is already // installed. We need to make sure to delete the older one's .apk. - res.removedInfo.args = createInstallArgs(0, + res.removedInfo.args = createInstallArgsForExisting(0, deletedPackage.applicationInfo.sourceDir, deletedPackage.applicationInfo.publicSourceDir, deletedPackage.applicationInfo.nativeLibraryDir, @@ -10143,7 +10144,7 @@ public class PackageManagerService extends IPackageManager.Stub { // TODO: extend to move split APK dex files if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { final String instructionSet = getAppInstructionSet(newPackage.applicationInfo); - int retCode = mInstaller.movedex(oldCodePath, newPackage.codePath, + int retCode = mInstaller.movedex(oldCodePath, newPackage.baseCodePath, instructionSet); if (retCode != 0) { /* @@ -10156,7 +10157,7 @@ public class PackageManagerService extends IPackageManager.Stub { */ newPackage.mDexOptNeeded = true; mInstaller.rmdex(oldCodePath, instructionSet); - mInstaller.rmdex(newPackage.codePath, instructionSet); + mInstaller.rmdex(newPackage.baseCodePath, instructionSet); } } return PackageManager.INSTALL_SUCCEEDED; @@ -10221,8 +10222,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private void installPackageLI(InstallArgs args, - boolean newInstall, PackageInstalledInfo res) { + private void installPackageLI(InstallArgs args, boolean newInstall, PackageInstalledInfo res) { int pFlags = args.flags; String installerPackageName = args.installerPackageName; File tmpPackageFile = new File(args.getCodePath()); @@ -10367,14 +10367,18 @@ public class PackageManagerService extends IPackageManager.Stub { res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return; } + // Set application objects path explicitly after the rename + // TODO: derive split paths from original scan after rename pkg.codePath = args.getCodePath(); + pkg.baseCodePath = args.getCodePath(); + pkg.splitCodePaths = null; pkg.applicationInfo.sourceDir = args.getCodePath(); pkg.applicationInfo.publicSourceDir = args.getResourcePath(); - pkg.applicationInfo.splitSourceDirs = args.getSplitCodePaths(); - pkg.applicationInfo.splitPublicSourceDirs = deriveSplitResPaths( - pkg.applicationInfo.splitSourceDirs); + pkg.applicationInfo.splitSourceDirs = null; + pkg.applicationInfo.splitPublicSourceDirs = null; pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath(); + if (replace) { replacePackageLI(pkg, parseFlags, scanMode, args.user, installerPackageName, res, args.abiOverride); @@ -10847,8 +10851,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Delete application code and resources if (deleteCodeAndResources && (outInfo != null)) { - outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString, - ps.resourcePathString, ps.nativeLibraryPathString, + outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), + ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString, getAppInstructionSetFromSettings(ps)); } return true; @@ -11253,7 +11257,8 @@ public class PackageManagerService extends IPackageManager.Stub { publicSrcDir = applicationInfo.publicSourceDir; } } - int res = mInstaller.getSizeInfo(packageName, userHandle, p.codePath, libDirPath, + // TODO: extend to measure size of split APKs + int res = mInstaller.getSizeInfo(packageName, userHandle, p.baseCodePath, libDirPath, publicSrcDir, asecPath, getAppInstructionSetFromSettings(ps), pStats); if (res < 0) { @@ -12843,9 +12848,9 @@ public class PackageManagerService extends IPackageManager.Stub { } else { Message msg = mHandler.obtainMessage(INIT_COPY); final String instructionSet = getAppInstructionSet(pkg.applicationInfo); - InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir, - pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir, - instructionSet); + InstallArgs srcArgs = createInstallArgsForExisting(currFlags, + pkg.applicationInfo.sourceDir, pkg.applicationInfo.publicSourceDir, + pkg.applicationInfo.nativeLibraryDir, instructionSet); MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName, pkg.applicationInfo.dataDir, instructionSet, pkg.applicationInfo.uid, user); msg.obj = mp; @@ -12943,11 +12948,13 @@ public class PackageManagerService extends IPackageManager.Stub { if (returnCode == PackageManager.MOVE_SUCCEEDED) { pkg.codePath = newCodePath; + pkg.baseCodePath = newCodePath; // Move dex files around if (moveDexFilesLI(oldCodePath, pkg) != PackageManager.INSTALL_SUCCEEDED) { // Moving of dex files failed. Set // error code and abort move. pkg.codePath = oldCodePath; + pkg.baseCodePath = oldCodePath; returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; } } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 7fee3725b7fc5..2091c16aeb5a8 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -45,6 +45,12 @@ class PackageSettingBase extends GrantedPermissions { final String name; final String realName; + + /** + * Path where this package was found on disk. For monolithic packages + * this is path to single base APK file; for cluster packages this is + * path to the cluster directory. + */ File codePath; String codePathString; File resourcePath;