diff options
author | Jeff Sharkey <jsharkey@android.com> | 2014-08-20 16:26:32 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2014-08-22 16:25:04 -0700 |
commit | 941a8ba1a6043cf84a7bf622e44a0b4f7abd0178 (patch) | |
tree | c783987f68caaa4cc827b3c720f269bcc9d34667 /services/core | |
parent | 7653a30ea0232ab8323ec51ddcba8d8054ca8a2f (diff) | |
download | frameworks_base-941a8ba1a6043cf84a7bf622e44a0b4f7abd0178.zip frameworks_base-941a8ba1a6043cf84a7bf622e44a0b4f7abd0178.tar.gz frameworks_base-941a8ba1a6043cf84a7bf622e44a0b4f7abd0178.tar.bz2 |
Installing splits into ASECs!
Sessions can now zero-copy data directly into pre-allocated ASEC
containers. Then at commit time, we compute the total size of the
final app, including any inherited APKs and unpacked libraries, and
resize the container in one step.
This supports both brand new ASEC installs and inheriting from
existing ASEC installs. To keep things simple, it currently requires
copying any inherited ASEC contents, but this could be optimized in
the future.
Expose new vold resize command, and allow read-write mounting of ASEC
containers. Move native library extraction into the installer flow,
since it needs to happen before ASEC is sealed. Move multiArch flag
into NativeLibraryHelper, instead of making everyone pass it
around. Migrate size calculation to shared location.
Separate "other" package name in public API, provide a path to a
storage device when relevant, and add more docs.
Bug: 16514385
Change-Id: I06c6ce588d312ee7e64cce02733895d640b88456
Diffstat (limited to 'services/core')
4 files changed, 493 insertions, 284 deletions
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 50f2ae9..ea24d7c 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -33,7 +33,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.hardware.usb.UsbManager; -import android.app.admin.DevicePolicyManager; import android.net.Uri; import android.os.Binder; import android.os.Environment; @@ -1761,6 +1760,21 @@ class MountService extends IMountService.Stub return rc; } + @Override + public int resizeSecureContainer(String id, int sizeMb, String key) { + validatePermission(android.Manifest.permission.ASEC_CREATE); + waitForReady(); + warnOnNotMounted(); + + int rc = StorageResultCode.OperationSucceeded; + try { + mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key)); + } catch (NativeDaemonConnectorException e) { + rc = StorageResultCode.OperationFailedInternalError; + } + return rc; + } + public int finalizeSecureContainer(String id) { validatePermission(android.Manifest.permission.ASEC_CREATE); warnOnNotMounted(); @@ -1835,7 +1849,7 @@ class MountService extends IMountService.Stub return rc; } - public int mountSecureContainer(String id, String key, int ownerUid) { + public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) { validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); waitForReady(); warnOnNotMounted(); @@ -1848,7 +1862,8 @@ class MountService extends IMountService.Stub int rc = StorageResultCode.OperationSucceeded; try { - mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid); + mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid, + readOnly ? "ro" : "rw"); } catch (NativeDaemonConnectorException e) { int code = e.getCode(); if (code != VoldResponseCode.OpFailedStorageBusy) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 5e802de..6f60d24 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -19,7 +19,6 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_ALL_USERS; import static android.content.pm.PackageManager.INSTALL_FROM_ADB; import static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING; -import static android.net.TrafficStats.MB_IN_BYTES; import static com.android.internal.util.XmlUtils.readBitmapAttribute; import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; @@ -48,8 +47,11 @@ import android.content.pm.IPackageInstaller; import android.content.pm.IPackageInstallerCallback; import android.content.pm.IPackageInstallerSession; import android.content.pm.PackageInstaller; +import android.content.pm.PackageParser; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageInstaller.SessionParams; +import android.content.pm.PackageParser.PackageLite; +import android.content.pm.PackageParser.PackageParserException; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.net.Uri; @@ -208,7 +210,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { // Ignore stages claimed by active sessions for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); - unclaimed.remove(session.internalStageDir); + unclaimed.remove(session.stageDir); } // Clean up orphaned staging directories @@ -234,7 +236,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { // Ignore stages claimed by active sessions for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); - final String cid = session.externalStageCid; + final String cid = session.stageCid; if (unclaimed.remove(cid)) { // Claimed by active session, mount it @@ -304,10 +306,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub { Slog.w(TAG, "Abandoning old session first created at " + session.createdMillis); valid = false; - } else if (session.internalStageDir != null - && !session.internalStageDir.exists()) { + } else if (session.stageDir != null + && !session.stageDir.exists()) { Slog.w(TAG, "Abandoning internal session with missing stage " - + session.internalStageDir); + + session.stageDir); valid = false; } else { valid = true; @@ -401,12 +403,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub { writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME, session.installerPackageName); writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis); - if (session.internalStageDir != null) { + if (session.stageDir != null) { writeStringAttribute(out, ATTR_SESSION_STAGE_DIR, - session.internalStageDir.getAbsolutePath()); + session.stageDir.getAbsolutePath()); } - if (session.externalStageCid != null) { - writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.externalStageCid); + if (session.stageCid != null) { + writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid); } writeBooleanAttribute(out, ATTR_SEALED, session.isSealed()); @@ -479,6 +481,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } } + // TODO: treat INHERIT_EXISTING as install for user + // Figure out where we're going to be staging session data final boolean stageInternal; @@ -502,22 +506,36 @@ public class PackageInstallerService extends IPackageInstaller.Stub { Binder.restoreCallingIdentity(ident); } } else if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { - // We always stage inheriting sessions on internal storage first, - // since we don't want to grow containers until we're sure that - // everything looks legit. - stageInternal = true; - checkInternalStorage(params.sizeBytes); - - // If we have a good hunch we'll end up on external storage, verify - // free space there too. - final ApplicationInfo info = mPm.getApplicationInfo(params.appPackageName, 0, + // Inheriting existing install, so stay on the same storage medium. + final ApplicationInfo existingApp = mPm.getApplicationInfo(params.appPackageName, 0, userId); - if (info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { - checkExternalStorage(params.sizeBytes); + if (existingApp == null) { + throw new IllegalStateException( + "Missing existing app " + params.appPackageName); + } - throw new UnsupportedOperationException("TODO: finish fleshing out ASEC support"); + final long existingSize; + try { + final PackageLite existingPkg = PackageParser.parsePackageLite( + new File(existingApp.getCodePath()), 0); + existingSize = PackageHelper.calculateInstalledSize(existingPkg, false, + params.abiOverride); + } catch (PackageParserException e) { + throw new IllegalStateException( + "Failed to calculate size of " + params.appPackageName); } + if ((existingApp.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0) { + // Internal we can link existing install into place, so we only + // need enough space for the new data. + checkInternalStorage(params.sizeBytes); + stageInternal = true; + } else { + // External we're going to copy existing install into our + // container, so we need footprint of both. + checkExternalStorage(params.sizeBytes + existingSize); + stageInternal = false; + } } else { throw new IllegalArgumentException("Invalid install mode: " + params.mode); } @@ -641,11 +659,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } final String cid = "smdl" + sessionId + ".tmp"; - - // Round up to nearest MB, plus another MB for filesystem overhead - final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1; - - if (PackageHelper.createSdDir(sizeMb, cid, PackageManagerService.getEncryptKey(), + if (PackageHelper.createSdDir(sizeBytes, cid, PackageManagerService.getEncryptKey(), Process.SYSTEM_UID, true) == null) { throw new IOException("Failed to create ASEC"); } @@ -857,7 +871,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_NAME, existing); + fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing); } } try { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 38a2016..5264fc4 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -19,6 +19,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED; import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS; import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR; +import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED; @@ -38,6 +39,7 @@ import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageParser.ApkLite; +import android.content.pm.PackageParser.PackageLite; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.Signature; import android.os.Bundle; @@ -47,6 +49,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.system.ErrnoException; @@ -59,18 +62,21 @@ import android.util.MathUtils; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter; +import libcore.io.IoUtils; import libcore.io.Libcore; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class PackageInstallerSession extends IPackageInstallerSession.Stub { @@ -95,18 +101,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final SessionParams params; final long createdMillis; - /** Internal location where staged data is written. */ - final File internalStageDir; - /** External container where staged data is written. */ - final String externalStageCid; - - /** - * When a {@link SessionParams#MODE_INHERIT_EXISTING} session is installed - * into an ASEC, this is the container where the stage is combined with the - * existing install. - */ - // TODO: persist this cid once we start splicing - String combinedCid; + /** Staging location where client data is written. */ + final File stageDir; + final String stageCid; /** Note that UID is not persisted; it's always derived at runtime. */ final int installerUid; @@ -133,29 +130,34 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private String mFinalMessage; @GuardedBy("mLock") - private File mResolvedStageDir; + private ArrayList<FileBridge> mBridges = new ArrayList<>(); + + @GuardedBy("mLock") + private IPackageInstallObserver2 mRemoteObserver; + + /** Fields derived from commit parsing */ + private String mPackageName; + private int mVersionCode; + private Signature[] mSignatures; /** - * Path to the resolved base APK for this session, which may point at an APK - * inside the session (when the session defines the base), or it may point - * at the existing base APK (when adding splits to an existing app). + * Path to the validated base APK for this session, which may point at an + * APK inside the session (when the session defines the base), or it may + * point at the existing base APK (when adding splits to an existing app). * <p> * This is used when confirming permissions, since we can't fully stage the * session inside an ASEC before confirming with user. */ @GuardedBy("mLock") - private String mResolvedBaseCodePath; + private File mResolvedBaseFile; @GuardedBy("mLock") - private ArrayList<FileBridge> mBridges = new ArrayList<>(); + private File mResolvedStageDir; @GuardedBy("mLock") - private IPackageInstallObserver2 mRemoteObserver; - - /** Fields derived from commit parsing */ - private String mPackageName; - private int mVersionCode; - private Signature[] mSignatures; + private final List<File> mResolvedStagedFiles = new ArrayList<>(); + @GuardedBy("mLock") + private final List<File> mResolvedInheritedFiles = new ArrayList<>(); private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override @@ -168,9 +170,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { try { commitLocked(); } catch (PackageManagerException e) { - Slog.e(TAG, "Install failed: " + e); + final String completeMsg = ExceptionUtils.getCompleteMessage(e); + Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); destroyInternal(); - dispatchSessionFinished(e.error, e.getMessage(), null); + dispatchSessionFinished(e.error, completeMsg, null); } return true; @@ -181,7 +184,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { public PackageInstallerSession(PackageInstallerService.InternalCallback callback, Context context, PackageManagerService pm, Looper looper, int sessionId, int userId, String installerPackageName, SessionParams params, long createdMillis, - File internalStageDir, String externalStageCid, boolean sealed) { + File stageDir, String stageCid, boolean sealed) { mCallback = callback; mContext = context; mPm = pm; @@ -192,12 +195,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { this.installerPackageName = installerPackageName; this.params = params; this.createdMillis = createdMillis; - this.internalStageDir = internalStageDir; - this.externalStageCid = externalStageCid; + this.stageDir = stageDir; + this.stageCid = stageCid; - if ((internalStageDir == null) == (externalStageCid == null)) { + if ((stageDir == null) == (stageCid == null)) { throw new IllegalArgumentException( - "Exactly one of internal or external stage must be set"); + "Exactly one of stageDir or stageCid stage must be set"); } mSealed = sealed; @@ -220,7 +223,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { info.sessionId = sessionId; info.installerPackageName = installerPackageName; - info.resolvedBaseCodePath = mResolvedBaseCodePath; + info.resolvedBaseCodePath = (mResolvedBaseFile != null) ? + mResolvedBaseFile.getAbsolutePath() : null; info.progress = mProgress; info.sealed = mSealed; info.open = mOpenCount.get() > 0; @@ -253,18 +257,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * might point at an ASEC mount point, which is why we delay path resolution * until someone actively works with the session. */ - private File getStageDir() throws IOException { + private File resolveStageDir() throws IOException { synchronized (mLock) { if (mResolvedStageDir == null) { - if (internalStageDir != null) { - mResolvedStageDir = internalStageDir; + if (stageDir != null) { + mResolvedStageDir = stageDir; } else { - final String path = PackageHelper.getSdDir(externalStageCid); + final String path = PackageHelper.getSdDir(stageCid); if (path != null) { mResolvedStageDir = new File(path); } else { - throw new IOException( - "Failed to resolve container path for " + externalStageCid); + throw new IOException("Failed to resolve path to container " + stageCid); } } } @@ -306,7 +309,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { public String[] getNames() { assertNotSealed("getNames"); try { - return getStageDir().list(); + return resolveStageDir().list(); } catch (IOException e) { throw ExceptionUtils.wrap(e); } @@ -339,8 +342,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (!FileUtils.isValidExtFilename(name)) { throw new IllegalArgumentException("Invalid name: " + name); } - final File target = new File(getStageDir(), name); + final File target = new File(resolveStageDir(), name); + // TODO: this should delegate to DCS so the system process avoids + // holding open FDs into containers. final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_CREAT | O_WRONLY, 0644); Os.chmod(target.getAbsolutePath(), 0644); @@ -350,7 +355,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (lengthBytes > 0) { final StructStat stat = Libcore.os.fstat(targetFd); final long deltaBytes = lengthBytes - stat.st_size; - if (deltaBytes > 0) { + // Only need to free up space when writing to internal stage + if (stageDir != null && deltaBytes > 0) { mPm.freeStorage(deltaBytes); } Libcore.os.posix_fallocate(targetFd, 0, lengthBytes); @@ -385,7 +391,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (!FileUtils.isValidExtFilename(name)) { throw new IllegalArgumentException("Invalid name: " + name); } - final File target = new File(getStageDir(), name); + final File target = new File(resolveStageDir(), name); final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0); return new ParcelFileDescriptor(targetFd); @@ -424,22 +430,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mCallback.onSessionSealed(this); } - final File stageDir; try { - stageDir = getStageDir(); + resolveStageDir(); } catch (IOException e) { throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, - "Failed to resolve stage dir", e); + "Failed to resolve stage location", e); } // Verify that stage looks sane with respect to existing application. // This currently only ensures packageName, versionCode, and certificate // consistency. - validateInstallLocked(stageDir); + validateInstallLocked(); Preconditions.checkNotNull(mPackageName); Preconditions.checkNotNull(mSignatures); - Preconditions.checkNotNull(mResolvedBaseCodePath); + Preconditions.checkNotNull(mResolvedBaseFile); if (!mPermissionsAccepted) { // User needs to accept permissions; give installer an intent they @@ -454,17 +459,41 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } + if (stageCid != null) { + // Figure out the final installed size and resize the container once + // and for all. Internally the parser handles straddling between two + // locations when inheriting. + final long finalSize = calculateInstalledSize(); + resizeContainer(stageCid, finalSize); + } + // Inherit any packages and native libraries from existing install that // haven't been overridden. if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { - // TODO: implement splicing into existing ASEC - spliceExistingFilesIntoStage(stageDir); + try { + if (stageCid != null) { + // TODO: this should delegate to DCS so the system process + // avoids holding open FDs into containers. + copyFiles(mResolvedInheritedFiles, resolveStageDir()); + } else { + linkFiles(mResolvedInheritedFiles, resolveStageDir()); + } + } catch (IOException e) { + throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, + "Failed to inherit existing install", e); + } } // TODO: surface more granular state from dexopt mCallback.onSessionProgressChanged(this, 0.9f); - // TODO: for ASEC based applications, grow and stream in packages + // Unpack native libraries + extractNativeLibraries(mResolvedStageDir, params.abiOverride); + + // Container is ready to go, let's seal it up! + if (stageCid != null) { + finalizeAndFixContainer(stageCid); + } // We've reached point of no return; call into PMS to install the stage. // Regardless of success or failure we always destroy session. @@ -482,7 +511,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } }; - mPm.installStage(mPackageName, this.internalStageDir, this.externalStageCid, localObserver, + mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params, installerPackageName, installerUid, new UserHandle(userId)); } @@ -490,81 +519,88 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * Validate install by confirming that all application packages are have * consistent package name, version code, and signing certificates. * <p> + * Clears and populates {@link #mResolvedBaseFile}, + * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}. + * <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 { + private void validateInstallLocked() throws PackageManagerException { mPackageName = null; mVersionCode = -1; mSignatures = null; - mResolvedBaseCodePath = null; - final File[] files = stageDir.listFiles(); + mResolvedBaseFile = null; + mResolvedStagedFiles.clear(); + mResolvedInheritedFiles.clear(); + + final File[] files = mResolvedStageDir.listFiles(); if (ArrayUtils.isEmpty(files)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); } // Verify that all staged packages are internally consistent - final ArraySet<String> seenSplits = new ArraySet<>(); + final ArraySet<String> stagedSplits = 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; + final ApkLite apk; try { - info = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES); + apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES); } catch (PackageParserException e) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Failed to parse " + file + ": " + e); } - if (!seenSplits.add(info.splitName)) { + if (!stagedSplits.add(apk.splitName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, - "Split " + info.splitName + " was defined multiple times"); + "Split " + apk.splitName + " was defined multiple times"); } // Use first package to define unknown values if (mPackageName == null) { - mPackageName = info.packageName; - mVersionCode = info.versionCode; + mPackageName = apk.packageName; + mVersionCode = apk.versionCode; } if (mSignatures == null) { - mSignatures = info.signatures; + mSignatures = apk.signatures; } - assertPackageConsistent(String.valueOf(file), info.packageName, info.versionCode, - info.signatures); + assertApkConsistent(String.valueOf(file), apk); // Take this opportunity to enforce uniform naming final String targetName; - if (info.splitName == null) { + if (apk.splitName == null) { targetName = "base.apk"; } else { - targetName = "split_" + info.splitName + ".apk"; + targetName = "split_" + apk.splitName + ".apk"; } if (!FileUtils.isValidExtFilename(targetName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Invalid filename: " + targetName); } - final File targetFile = new File(stageDir, targetName); + final File targetFile = new File(mResolvedStageDir, targetName); if (!file.equals(targetFile)) { file.renameTo(targetFile); } // Base is coming from session - if (info.splitName == null) { - mResolvedBaseCodePath = targetFile.getAbsolutePath(); + if (apk.splitName == null) { + mResolvedBaseFile = targetFile; } + + mResolvedStagedFiles.add(targetFile); } if (params.mode == SessionParams.MODE_FULL_INSTALL) { // Full installs must include a base package - if (!seenSplits.contains(null)) { + if (!stagedSplits.contains(null)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Full install must include a base package"); } @@ -577,67 +613,204 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Missing existing base package for " + mPackageName); } - // Base might be inherited from existing install - if (mResolvedBaseCodePath == null) { - mResolvedBaseCodePath = app.getBaseCodePath(); - } - - final ApkLite info; + final PackageLite existing; + final ApkLite existingBase; try { - info = PackageParser.parseApkLite(new File(app.getBaseCodePath()), + existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0); + existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()), PackageParser.PARSE_COLLECT_CERTIFICATES); } catch (PackageParserException e) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, - "Failed to parse existing base " + app.getBaseCodePath() + ": " + e); + "Failed to parse existing package " + app.getCodePath() + ": " + e); + } + + assertApkConsistent("Existing base", existingBase); + + // Inherit base if not overridden + if (mResolvedBaseFile == null) { + mResolvedBaseFile = new File(app.getBaseCodePath()); + mResolvedInheritedFiles.add(mResolvedBaseFile); } - assertPackageConsistent("Existing base", info.packageName, info.versionCode, - info.signatures); + // Inherit splits if not overridden + if (!ArrayUtils.isEmpty(existing.splitNames)) { + for (int i = 0; i < existing.splitNames.length; i++) { + final String splitName = existing.splitNames[i]; + final File splitFile = new File(existing.splitCodePaths[i]); + + if (!stagedSplits.contains(splitName)) { + mResolvedInheritedFiles.add(splitFile); + } + } + } } } - private void assertPackageConsistent(String tag, String packageName, int versionCode, - Signature[] signatures) throws PackageManagerException { - if (!mPackageName.equals(packageName)) { + private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException { + if (!mPackageName.equals(apk.packageName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " - + packageName + " inconsistent with " + mPackageName); + + apk.packageName + " inconsistent with " + mPackageName); } - if (mVersionCode != versionCode) { + if (mVersionCode != apk.versionCode) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag - + " version code " + versionCode + " inconsistent with " + + " version code " + apk.versionCode + " inconsistent with " + mVersionCode); } - if (!Signature.areExactMatch(mSignatures, signatures)) { + if (!Signature.areExactMatch(mSignatures, apk.signatures)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " signatures are inconsistent"); } } /** - * Application is already installed; splice existing files that haven't been - * overridden into our stage. + * Calculate the final install footprint size, combining both staged and + * existing APKs together and including unpacked native code from both. */ - private void spliceExistingFilesIntoStage(File stageDir) throws PackageManagerException { - final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId); + private long calculateInstalledSize() throws PackageManagerException { + Preconditions.checkNotNull(mResolvedBaseFile); - int n = 0; - final File[] oldFiles = new File(app.getCodePath()).listFiles(); - if (!ArrayUtils.isEmpty(oldFiles)) { - for (File oldFile : oldFiles) { - if (!PackageParser.isApkFile(oldFile)) continue; + final ApkLite baseApk; + try { + baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0); + } catch (PackageParserException e) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + "Failed to parse base package " + mResolvedBaseFile + ": " + e); + } - final File newFile = new File(stageDir, oldFile.getName()); - try { - Os.link(oldFile.getAbsolutePath(), newFile.getAbsolutePath()); - n++; - } catch (ErrnoException e) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Failed to splice into stage", e); - } + final List<String> splitPaths = new ArrayList<>(); + for (File file : mResolvedStagedFiles) { + if (mResolvedBaseFile.equals(file)) continue; + splitPaths.add(file.getAbsolutePath()); + } + for (File file : mResolvedInheritedFiles) { + if (mResolvedBaseFile.equals(file)) continue; + splitPaths.add(file.getAbsolutePath()); + } + + // This is kind of hacky; we're creating a half-parsed package that is + // straddled between the inherited and staged APKs. + final PackageLite pkg = new PackageLite(null, baseApk, null, + splitPaths.toArray(new String[splitPaths.size()])); + final boolean isForwardLocked = + (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; + + try { + return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride); + } catch (IOException e) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + "Failed to calculate install size", e); + } + } + + private static void linkFiles(List<File> fromFiles, File toDir) throws IOException { + for (File fromFile : fromFiles) { + final File toFile = new File(toDir, fromFile.getName()); + try { + if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile); + Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath()); + } catch (ErrnoException e) { + throw new IOException("Failed to link " + fromFile + " to " + toFile, e); + } + } + Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); + } + + private static void copyFiles(List<File> fromFiles, File toDir) throws IOException { + // Remove any partial files from previous attempt + for (File file : toDir.listFiles()) { + if (file.getName().endsWith(".tmp")) { + file.delete(); + } + } + + for (File fromFile : fromFiles) { + final File tmpFile = File.createTempFile("inherit", ".tmp", toDir); + if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile); + if (!FileUtils.copyFile(fromFile, tmpFile)) { + throw new IOException("Failed to copy " + fromFile + " to " + tmpFile); + } + + final File toFile = new File(toDir, fromFile.getName()); + if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile); + if (!tmpFile.renameTo(toFile)) { + throw new IOException("Failed to rename " + tmpFile + " to " + toFile); + } + } + Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir); + } + + private static void extractNativeLibraries(File packageDir, String abiOverride) + throws PackageManagerException { + if (LOGD) Slog.v(TAG, "extractNativeLibraries()"); + + // Always start from a clean slate + final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME); + NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true); + + NativeLibraryHelper.Handle handle = null; + try { + handle = NativeLibraryHelper.Handle.create(packageDir); + final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, + abiOverride); + if (res != PackageManager.INSTALL_SUCCEEDED) { + throw new PackageManagerException(res, + "Failed to extract native libraries, res=" + res); } + } catch (IOException e) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Failed to extract native libraries", e); + } finally { + IoUtils.closeQuietly(handle); + } + } + + private static void resizeContainer(String cid, long targetSize) + throws PackageManagerException { + String path = PackageHelper.getSdDir(cid); + if (path == null) { + throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, + "Failed to find mounted " + cid); + } + + final long currentSize = new File(path).getTotalSpace(); + if (currentSize > targetSize) { + Slog.w(TAG, "Current size " + currentSize + " is larger than target size " + + targetSize + "; skipping resize"); + return; + } + + if (!PackageHelper.unMountSdDir(cid)) { + throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, + "Failed to unmount " + cid + " before resize"); } - if (LOGD) Slog.d(TAG, "Spliced " + n + " existing APKs into stage"); + if (!PackageHelper.resizeSdDir(targetSize, cid, + PackageManagerService.getEncryptKey())) { + throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, + "Failed to resize " + cid + " to " + targetSize + " bytes"); + } + + path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(), + Process.SYSTEM_UID, false); + if (path == null) { + throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, + "Failed to mount " + cid + " after resize"); + } + } + + private void finalizeAndFixContainer(String cid) throws PackageManagerException { + if (!PackageHelper.finalizeSdDir(cid)) { + throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, + "Failed to finalize container " + cid); + } + + final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE, + UserHandle.USER_OWNER); + final int gid = UserHandle.getSharedAppGid(uid); + if (!PackageHelper.fixSdPermissions(cid, gid, null)) { + throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, + "Failed to fix permissions on container " + cid); + } } void setPermissionsResult(boolean accepted) { @@ -694,12 +867,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSealed = true; mDestroyed = true; } - if (internalStageDir != null) { - FileUtils.deleteContents(internalStageDir); - internalStageDir.delete(); + if (stageDir != null) { + FileUtils.deleteContents(stageDir); + stageDir.delete(); } - if (externalStageCid != null) { - PackageHelper.destroySdDir(externalStageCid); + if (stageCid != null) { + PackageHelper.destroySdDir(stageCid); } } @@ -717,8 +890,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { pw.printPair("installerPackageName", installerPackageName); pw.printPair("installerUid", installerUid); pw.printPair("createdMillis", createdMillis); - pw.printPair("internalStageDir", internalStageDir); - pw.printPair("externalStageCid", externalStageCid); + pw.printPair("stageDir", stageDir); + pw.printPair("stageCid", stageCid); pw.println(); params.dump(pw); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index aa49b27..f06992a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -50,6 +50,8 @@ import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDWR; 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.content.NativeLibraryHelper.LIB64_DIR_NAME; +import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.internal.util.ArrayUtils.removeInt; @@ -298,9 +300,6 @@ public class PackageManagerService extends IPackageManager.Stub { private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; - private static final String LIB_DIR_NAME = "lib"; - private static final String LIB64_DIR_NAME = "lib64"; - private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay"; private static String sPreferredInstructionSet; @@ -1121,7 +1120,7 @@ public class PackageManagerService extends IPackageManager.Stub { if ((state != null) && !state.timeoutExtended()) { final InstallArgs args = state.getInstallArgs(); - final Uri originUri = Uri.fromFile(args.originFile); + final Uri originUri = Uri.fromFile(args.origin.resolvedFile); Slog.i(TAG, "Verification timed out for " + originUri); mPendingVerification.remove(verificationId); @@ -1168,7 +1167,7 @@ public class PackageManagerService extends IPackageManager.Stub { mPendingVerification.remove(verificationId); final InstallArgs args = state.getInstallArgs(); - final Uri originUri = Uri.fromFile(args.originFile); + final Uri originUri = Uri.fromFile(args.origin.resolvedFile); int ret; if (state.isInstallAllowed()) { @@ -4271,7 +4270,7 @@ public class PackageManagerService extends IPackageManager.Stub { InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, - getAppDexInstructionSets(ps), isMultiArch(ps)); + getAppDexInstructionSets(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } @@ -4334,7 +4333,7 @@ public class PackageManagerService extends IPackageManager.Stub { + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, - getAppDexInstructionSets(ps), isMultiArch(ps)); + getAppDexInstructionSets(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } @@ -5527,8 +5526,9 @@ public class PackageManagerService extends IPackageManager.Stub { if (isAsec) { abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS); } else { - abi32 = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, - nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, useIsaSpecificSubdirs); + abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, + nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, + useIsaSpecificSubdirs); } } @@ -5539,8 +5539,9 @@ public class PackageManagerService extends IPackageManager.Stub { if (isAsec) { abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS); } else { - abi64 = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, - nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, useIsaSpecificSubdirs); + abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, + nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, + useIsaSpecificSubdirs); } } @@ -5578,7 +5579,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (isAsec) { copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList); } else { - copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, + copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, nativeLibraryRoot, abiList, useIsaSpecificSubdirs); } @@ -7782,7 +7783,8 @@ public class PackageManagerService extends IPackageManager.Stub { verificationParams.setInstallerUid(uid); final Message msg = mHandler.obtainMessage(INIT_COPY); - msg.obj = new InstallParams(originFile, null, false, observer, filteredFlags, + final OriginInfo origin = new OriginInfo(originFile, null, false); + msg.obj = new InstallParams(origin, observer, filteredFlags, installerPackageName, verificationParams, user, packageAbiOverride); mHandler.sendMessage(msg); } @@ -7794,7 +7796,8 @@ public class PackageManagerService extends IPackageManager.Stub { params.referrerUri, installerUid, null); final Message msg = mHandler.obtainMessage(INIT_COPY); - msg.obj = new InstallParams(stagedDir, stagedCid, true, observer, params.installFlags, + final OriginInfo origin = new OriginInfo(stagedDir, stagedCid, true); + msg.obj = new InstallParams(origin, observer, params.installFlags, installerPackageName, verifParams, user, params.abiOverride); mHandler.sendMessage(msg); } @@ -8487,22 +8490,45 @@ public class PackageManagerService extends IPackageManager.Stub { } } - class InstallParams extends HandlerParams { + static class OriginInfo { /** * 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. */ - final File originFile; - final String originCid; + final File file; + final String cid; /** - * Flag indicating that {@link #originFile} or {@link #originCid} has - * already been staged, meaning downstream users don't need to - * defensively copy the contents. + * Flag indicating that {@link #file} or {@link #cid} has already been + * staged, meaning downstream users don't need to defensively copy the + * contents. */ - boolean originStaged; + final boolean staged; + + final String resolvedPath; + final File resolvedFile; + + public OriginInfo(File file, String cid, boolean staged) { + this.file = file; + this.cid = cid; + this.staged = staged; + + if (cid != null) { + resolvedPath = PackageHelper.getSdDir(cid); + resolvedFile = new File(resolvedPath); + } else if (file != null) { + resolvedPath = file.getAbsolutePath(); + resolvedFile = file; + } else { + resolvedPath = null; + resolvedFile = null; + } + } + } + class InstallParams extends HandlerParams { + final OriginInfo origin; final IPackageInstallObserver2 observer; int flags; final String installerPackageName; @@ -8510,15 +8536,12 @@ public class PackageManagerService extends IPackageManager.Stub { private InstallArgs mArgs; private int mRet; final String packageAbiOverride; - boolean multiArch; - InstallParams(File originFile, String originCid, boolean originStaged, - IPackageInstallObserver2 observer, int flags, String installerPackageName, - VerificationParams verificationParams, UserHandle user, String packageAbiOverride) { + InstallParams(OriginInfo origin, IPackageInstallObserver2 observer, int flags, + String installerPackageName, VerificationParams verificationParams, UserHandle user, + String packageAbiOverride) { super(user); - this.originFile = originFile; - this.originCid = originCid; - this.originStaged = originStaged; + this.origin = origin; this.observer = observer; this.flags = flags; this.installerPackageName = installerPackageName; @@ -8529,7 +8552,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public String toString() { return "InstallParams{" + Integer.toHexString(System.identityHashCode(this)) - + " file=" + originFile + " cid=" + originCid + "}"; + + " file=" + origin.file + " cid=" + origin.cid + "}"; } public ManifestDigest getManifestDigest() { @@ -8608,11 +8631,11 @@ public class PackageManagerService extends IPackageManager.Stub { int ret = PackageManager.INSTALL_SUCCEEDED; // If we're already staged, we've firmly committed to an install location - if (originStaged) { - if (originFile != null) { + if (origin.staged) { + if (origin.file != null) { flags |= PackageManager.INSTALL_INTERNAL; flags &= ~PackageManager.INSTALL_EXTERNAL; - } else if (originCid != null) { + } else if (origin.cid != null) { flags |= PackageManager.INSTALL_EXTERNAL; flags &= ~PackageManager.INSTALL_INTERNAL; } else { @@ -8622,6 +8645,7 @@ public class PackageManagerService extends IPackageManager.Stub { final boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0; final boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0; + PackageInfoLite pkgLite = null; if (onInt && onSd) { @@ -8629,21 +8653,14 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Conflicting flags specified for installing on both internal and external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { - // Remote call to find out default install location - final String originPath = originFile.getAbsolutePath(); - pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags, + pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, flags, packageAbiOverride); - // Keep track of whether this package is a multiArch package until - // we perform a full scan of it. We need to do this because we might - // end up extracting the package shared libraries before we perform - // a full scan. - multiArch = pkgLite.multiArch; /* * If we have too little free space, try to free cache * before giving up. */ - if (!originStaged && pkgLite.recommendedInstallLocation + if (!origin.staged && pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { // TODO: focus freeing disk space on the target device final StorageManager storage = StorageManager.from(mContext); @@ -8651,11 +8668,11 @@ public class PackageManagerService extends IPackageManager.Stub { Environment.getDataDirectory()); final long sizeBytes = mContainerService.calculateInstalledSize( - originPath, isForwardLocked(), packageAbiOverride); + origin.resolvedPath, isForwardLocked(), packageAbiOverride); if (mInstaller.freeCache(sizeBytes + lowThreshold) >= 0) { - pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags, - packageAbiOverride); + pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, + flags, packageAbiOverride); } /* @@ -8729,10 +8746,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(Uri.fromFile(originFile), PACKAGE_MIME_TYPE); + verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)), + PACKAGE_MIME_TYPE); verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); final List<ResolveInfo> receivers = queryIntentReceivers(verification, @@ -8890,8 +8907,7 @@ public class PackageManagerService extends IPackageManager.Stub { int mRet; MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags, - String packageName, String[] instructionSets, int uid, UserHandle user, - boolean isMultiArch) { + String packageName, String[] instructionSets, int uid, UserHandle user) { super(user); this.srcArgs = srcArgs; this.observer = observer; @@ -8901,7 +8917,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (srcArgs != null) { final String codePath = srcArgs.getCodePath(); targetArgs = createInstallArgsForMoveTarget(codePath, flags, packageName, - instructionSets, isMultiArch); + instructionSets); } else { targetArgs = null; } @@ -9002,8 +9018,6 @@ 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 { @@ -9016,8 +9030,7 @@ public class PackageManagerService extends IPackageManager.Stub { * when cleaning up old installs, or used as a move source. */ private InstallArgs createInstallArgsForExisting(int flags, String codePath, - String resourcePath, String nativeLibraryRoot, String[] instructionSets, - boolean isMultiArch) { + String resourcePath, String nativeLibraryRoot, String[] instructionSets) { final boolean isInAsec; if (installOnSd(flags)) { /* Apps on SD card are always in ASEC containers. */ @@ -9035,33 +9048,29 @@ public class PackageManagerService extends IPackageManager.Stub { if (isInAsec) { return new AsecInstallArgs(codePath, instructionSets, - installOnSd(flags), installForwardLocked(flags), isMultiArch); + installOnSd(flags), installForwardLocked(flags)); } else { return new FileInstallArgs(codePath, resourcePath, nativeLibraryRoot, - instructionSets, isMultiArch); + instructionSets); } } private InstallArgs createInstallArgsForMoveTarget(String codePath, int flags, String pkgName, - String[] instructionSets, boolean isMultiArch) { + String[] instructionSets) { final File codeFile = new File(codePath); if (installOnSd(flags) || installForwardLocked(flags)) { String cid = getNextCodePath(codePath, pkgName, "/" + AsecInstallArgs.RES_FILE_NAME); return new AsecInstallArgs(codeFile, cid, instructionSets, installOnSd(flags), - installForwardLocked(flags), isMultiArch); + installForwardLocked(flags)); } else { - return new FileInstallArgs(codeFile, instructionSets, isMultiArch); + return new FileInstallArgs(codeFile, instructionSets); } } static abstract class InstallArgs { - /** @see InstallParams#originFile */ - final File originFile; - /** @see InstallParams#originStaged */ - final boolean originStaged; - - // TODO: define inherit location + /** @see InstallParams#origin */ + final OriginInfo origin; final IPackageInstallObserver2 observer; // Always refers to PackageManager flags only @@ -9070,19 +9079,16 @@ public class PackageManagerService extends IPackageManager.Stub { final ManifestDigest manifestDigest; final UserHandle user; final String abiOverride; - final boolean multiArch; // The list of instruction sets supported by this app. This is currently // only used during the rmdex() phase to clean up resources. We can get rid of this // if we move dex files under the common app path. /* nullable */ String[] instructionSets; - InstallArgs(File originFile, boolean originStaged, IPackageInstallObserver2 observer, - int flags, String installerPackageName, ManifestDigest manifestDigest, - UserHandle user, String[] instructionSets, - String abiOverride, boolean multiArch) { - this.originFile = originFile; - this.originStaged = originStaged; + InstallArgs(OriginInfo origin, IPackageInstallObserver2 observer, int flags, + String installerPackageName, ManifestDigest manifestDigest, UserHandle user, + String[] instructionSets, String abiOverride) { + this.origin = origin; this.flags = flags; this.observer = observer; this.installerPackageName = installerPackageName; @@ -9090,7 +9096,6 @@ public class PackageManagerService extends IPackageManager.Stub { this.user = user; this.instructionSets = instructionSets; this.abiOverride = abiOverride; - this.multiArch = multiArch; } abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException; @@ -9161,10 +9166,9 @@ public class PackageManagerService extends IPackageManager.Stub { /** New install */ FileInstallArgs(InstallParams params) { - super(params.originFile, params.originStaged, params.observer, params.flags, + super(params.origin, params.observer, params.flags, params.installerPackageName, params.getManifestDigest(), params.getUser(), - null /* instruction sets */, params.packageAbiOverride, - params.multiArch); + null /* instruction sets */, params.packageAbiOverride); if (isFwdLocked()) { throw new IllegalArgumentException("Forward locking only supported in ASEC"); } @@ -9172,8 +9176,8 @@ public class PackageManagerService extends IPackageManager.Stub { /** Existing install */ FileInstallArgs(String codePath, String resourcePath, String legacyNativeLibraryPath, - String[] instructionSets, boolean isMultiArch) { - super(null, false, null, 0, null, null, null, instructionSets, null, isMultiArch); + String[] instructionSets) { + super(new OriginInfo(null, null, false), null, 0, null, null, null, instructionSets, null); this.codeFile = (codePath != null) ? new File(codePath) : null; this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; this.legacyNativeLibraryPath = (legacyNativeLibraryPath != null) ? @@ -9181,13 +9185,12 @@ public class PackageManagerService extends IPackageManager.Stub { } /** New install from existing */ - FileInstallArgs(File originFile, String[] instructionSets, boolean isMultiArch) { - super(originFile, false, null, 0, null, null, null, instructionSets, null, - isMultiArch); + FileInstallArgs(File originFile, String[] instructionSets) { + super(new OriginInfo(originFile, null, false), null, 0, null, null, null, instructionSets, null); } boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { - final long sizeBytes = imcs.calculateInstalledSize(originFile.getAbsolutePath(), + final long sizeBytes = imcs.calculateInstalledSize(origin.file.getAbsolutePath(), isFwdLocked(), abiOverride); final StorageManager storage = StorageManager.from(mContext); @@ -9195,53 +9198,53 @@ public class PackageManagerService extends IPackageManager.Stub { } int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { - int ret = PackageManager.INSTALL_SUCCEEDED; + if (origin.staged) { + Slog.d(TAG, origin.file + " already staged; skipping copy"); + codeFile = origin.file; + resourceFile = origin.file; + return PackageManager.INSTALL_SUCCEEDED; + } - if (originStaged) { - Slog.d(TAG, originFile + " already staged; skipping copy"); - codeFile = originFile; - resourceFile = originFile; - } else { - try { - final File tempDir = mInstallerService.allocateInternalStageDirLegacy(); - codeFile = tempDir; - resourceFile = tempDir; - } catch (IOException e) { - Slog.w(TAG, "Failed to create copy file: " + e); - return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - } + try { + final File tempDir = mInstallerService.allocateInternalStageDirLegacy(); + codeFile = tempDir; + resourceFile = tempDir; + } catch (IOException e) { + Slog.w(TAG, "Failed to create copy file: " + e); + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + } - final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() { - @Override - public ParcelFileDescriptor open(String name, int mode) throws RemoteException { - if (!FileUtils.isValidExtFilename(name)) { - throw new IllegalArgumentException("Invalid filename: " + name); - } - try { - final File file = new File(codeFile, name); - final FileDescriptor fd = Os.open(file.getAbsolutePath(), - O_RDWR | O_CREAT, 0644); - Os.chmod(file.getAbsolutePath(), 0644); - return new ParcelFileDescriptor(fd); - } catch (ErrnoException e) { - throw new RemoteException("Failed to open: " + e.getMessage()); - } + final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() { + @Override + public ParcelFileDescriptor open(String name, int mode) throws RemoteException { + if (!FileUtils.isValidExtFilename(name)) { + throw new IllegalArgumentException("Invalid filename: " + name); + } + try { + final File file = new File(codeFile, name); + final FileDescriptor fd = Os.open(file.getAbsolutePath(), + O_RDWR | O_CREAT, 0644); + Os.chmod(file.getAbsolutePath(), 0644); + return new ParcelFileDescriptor(fd); + } catch (ErrnoException e) { + throw new RemoteException("Failed to open: " + e.getMessage()); } - }; - - ret = imcs.copyPackage(originFile.getAbsolutePath(), target); - if (ret != PackageManager.INSTALL_SUCCEEDED) { - Slog.e(TAG, "Failed to copy package"); - return ret; } + }; + + int ret = PackageManager.INSTALL_SUCCEEDED; + ret = imcs.copyPackage(origin.file.getAbsolutePath(), target); + if (ret != PackageManager.INSTALL_SUCCEEDED) { + Slog.e(TAG, "Failed to copy package"); + return ret; } final File libraryRoot = new File(codeFile, LIB_DIR_NAME); NativeLibraryHelper.Handle handle = null; try { handle = NativeLibraryHelper.Handle.create(codeFile); - ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, libraryRoot, - abiOverride, multiArch); + ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot, + abiOverride); } catch (IOException e) { Slog.e(TAG, "Copying native libraries failed", e); ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; @@ -9429,18 +9432,18 @@ public class PackageManagerService extends IPackageManager.Stub { /** New install */ AsecInstallArgs(InstallParams params) { - super(params.originFile, params.originStaged, params.observer, params.flags, + super(params.origin, params.observer, params.flags, params.installerPackageName, params.getManifestDigest(), params.getUser(), null /* instruction sets */, - params.packageAbiOverride, params.multiArch); + params.packageAbiOverride); } /** Existing install */ AsecInstallArgs(String fullCodePath, String[] instructionSets, - boolean isExternal, boolean isForwardLocked, boolean isMultiArch) { - super(null, false, null, (isExternal ? INSTALL_EXTERNAL : 0) + boolean isExternal, boolean isForwardLocked) { + super(new OriginInfo(null, null, false), null, (isExternal ? INSTALL_EXTERNAL : 0) | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, - instructionSets, null, isMultiArch); + instructionSets, null); // 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(); @@ -9454,21 +9457,20 @@ public class PackageManagerService extends IPackageManager.Stub { setMountPath(subStr1); } - AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked, - boolean isMultiArch) { - super(null, false, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0) + AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) { + super(new OriginInfo(null, null, false), null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0) | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, - instructionSets, null, isMultiArch); + instructionSets, null); this.cid = cid; setMountPath(PackageHelper.getSdDir(cid)); } /** New install from existing */ AsecInstallArgs(File originPackageFile, String cid, String[] instructionSets, - boolean isExternal, boolean isForwardLocked, boolean isMultiArch) { - super(originPackageFile, false, null, (isExternal ? INSTALL_EXTERNAL : 0) + boolean isExternal, boolean isForwardLocked) { + super(new OriginInfo(originPackageFile, null, false), null, (isExternal ? INSTALL_EXTERNAL : 0) | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, - instructionSets, null, isMultiArch); + instructionSets, null); this.cid = cid; } @@ -9496,7 +9498,13 @@ 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 (origin.staged) { + Slog.d(TAG, origin.cid + " already staged; skipping copy"); + cid = origin.cid; + setMountPath(PackageHelper.getSdDir(cid)); + return PackageManager.INSTALL_SUCCEEDED; + } + if (temp) { createCopyFile(); } else { @@ -9508,7 +9516,7 @@ public class PackageManagerService extends IPackageManager.Stub { } final String newMountPath = imcs.copyPackageToContainer( - originFile.getAbsolutePath(), cid, getEncryptKey(), isExternal(), + origin.file.getAbsolutePath(), cid, getEncryptKey(), isExternal(), isFwdLocked(), deriveAbiOverride(abiOverride, null /* settings */)); if (newMountPath != null) { @@ -10133,8 +10141,7 @@ public class PackageManagerService extends IPackageManager.Stub { deletedPackage.applicationInfo.getCodePath(), deletedPackage.applicationInfo.getResourcePath(), deletedPackage.applicationInfo.nativeLibraryRootDir, - getAppDexInstructionSets(deletedPackage.applicationInfo), - isMultiArch(deletedPackage.applicationInfo)); + getAppDexInstructionSets(deletedPackage.applicationInfo)); } else { res.removedInfo.args = null; } @@ -10920,7 +10927,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (deleteCodeAndResources && (outInfo != null)) { outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, - getAppDexInstructionSets(ps), isMultiArch(ps)); + getAppDexInstructionSets(ps)); if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args); } return true; @@ -12725,7 +12732,7 @@ public class PackageManagerService extends IPackageManager.Stub { } final AsecInstallArgs args = new AsecInstallArgs(cid, - getAppDexInstructionSets(ps), isForwardLocked(ps), isMultiArch(ps)); + getAppDexInstructionSets(ps), isForwardLocked(ps)); // The package status is changed only if the code path // matches between settings and the container id. if (ps.codePathString != null @@ -13019,7 +13026,7 @@ public class PackageManagerService extends IPackageManager.Stub { * anyway. */ if (returnCode != PackageManager.MOVE_SUCCEEDED) { - processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1, user, false), + processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1, user), returnCode); } else { Message msg = mHandler.obtainMessage(INIT_COPY); @@ -13027,9 +13034,9 @@ public class PackageManagerService extends IPackageManager.Stub { final boolean multiArch = isMultiArch(pkg.applicationInfo); InstallArgs srcArgs = createInstallArgsForExisting(currFlags, pkg.applicationInfo.getCodePath(), pkg.applicationInfo.getResourcePath(), - pkg.applicationInfo.nativeLibraryRootDir, instructionSets, multiArch); + pkg.applicationInfo.nativeLibraryRootDir, instructionSets); MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName, - instructionSets, pkg.applicationInfo.uid, user, multiArch); + instructionSets, pkg.applicationInfo.uid, user); msg.obj = mp; mHandler.sendMessage(msg); } @@ -13107,8 +13114,8 @@ public class PackageManagerService extends IPackageManager.Stub { final int abi = NativeLibraryHelper.findSupportedAbi( handle, Build.SUPPORTED_ABIS); if (abi >= 0) { - NativeLibraryHelper.copyNativeBinariesIfNeededLI( - handle, newNativeDir, Build.SUPPORTED_ABIS[abi]); + NativeLibraryHelper.copyNativeBinaries(handle, + newNativeDir, Build.SUPPORTED_ABIS[abi]); } } catch (IOException ioe) { Slog.w(TAG, "Unable to extract native libs for package :" |