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
diff --git a/api/current.txt b/api/current.txt
index dfa95a0..093252d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8680,10 +8680,12 @@
method public void removeSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
method public void uninstall(java.lang.String, android.content.IntentSender);
field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
+ field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS";
field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
+ field public static final java.lang.String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
field public static final int STATUS_FAILURE = 1; // 0x1
field public static final int STATUS_FAILURE_ABORTED = 3; // 0x3
field public static final int STATUS_FAILURE_BLOCKED = 2; // 0x2
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 46d8ade..d9b40b1 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1004,6 +1004,10 @@
params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
} else if (opt.equals("-p")) {
params.mode = SessionParams.MODE_INHERIT_EXISTING;
+ params.appPackageName = nextOptionData();
+ if (params.appPackageName == null) {
+ throw new IllegalArgumentException("Missing inherit package name");
+ }
} else if (opt.equals("-S")) {
params.setSize(Long.parseLong(nextOptionData()));
} else if (opt.equals("--abi")) {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 7419ebc..9afdbf7 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -45,6 +45,7 @@
import java.io.OutputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -86,6 +87,8 @@
* <p>
* In some cases, a matching Activity may not exist, so ensure you safeguard
* against this.
+ * <p>
+ * The session to show details for is defined in {@link #EXTRA_SESSION_ID}.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
@@ -95,22 +98,58 @@
ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS";
/**
- * An integer session ID.
+ * An integer session ID that an operation is working with.
*
- * @see #ACTION_SESSION_DETAILS
+ * @see Intent#getIntExtra(String, int)
*/
public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
- public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
- public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
-
/**
- * Package name relevant to a status.
+ * Package name that an operation is working with.
*
* @see Intent#getStringExtra(String)
*/
public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
+ /**
+ * Current status of an operation. Will be one of
+ * {@link #STATUS_PENDING_USER_ACTION}, {@link #STATUS_SUCCESS},
+ * {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED},
+ * {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT},
+ * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, or
+ * {@link #STATUS_FAILURE_STORAGE}.
+ * <p>
+ * More information about a status may be available through additional
+ * extras; see the individual status documentation for details.
+ *
+ * @see Intent#getIntExtra(String, int)
+ */
+ public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
+
+ /**
+ * Detailed string representation of the status, including raw details that
+ * are useful for debugging.
+ *
+ * @see Intent#getStringExtra(String)
+ */
+ public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
+
+ /**
+ * Another package name relevant to a status. This is typically the package
+ * responsible for causing an operation failure.
+ *
+ * @see Intent#getStringExtra(String)
+ */
+ public static final String
+ EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
+
+ /**
+ * Storage path relevant to a status.
+ *
+ * @see Intent#getStringExtra(String)
+ */
+ public static final String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
+
/** {@hide} */
@Deprecated
public static final String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES";
@@ -153,8 +192,12 @@
* The operation failed because it was blocked. For example, a device policy
* may be blocking the operation, a package verifier may have blocked the
* operation, or the app may be required for core system operation.
+ * <p>
+ * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
+ * specific package blocking the install.
*
* @see #EXTRA_STATUS_MESSAGE
+ * @see #EXTRA_OTHER_PACKAGE_NAME
*/
public static final int STATUS_FAILURE_BLOCKED = 2;
@@ -182,10 +225,11 @@
* permission, incompatible certificates, etc. The user may be able to
* uninstall another app to fix the issue.
* <p>
- * The result may also contain {@link #EXTRA_PACKAGE_NAME} with the
+ * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
* specific package identified as the cause of the conflict.
*
* @see #EXTRA_STATUS_MESSAGE
+ * @see #EXTRA_OTHER_PACKAGE_NAME
*/
public static final int STATUS_FAILURE_CONFLICT = 5;
@@ -193,8 +237,12 @@
* The operation failed because of storage issues. For example, the device
* may be running low on space, or external media may be unavailable. The
* user may be able to help free space or insert different external media.
+ * <p>
+ * The result may also contain {@link #EXTRA_STORAGE_PATH} with the path to
+ * the storage device that caused the failure.
*
* @see #EXTRA_STATUS_MESSAGE
+ * @see #EXTRA_STORAGE_PATH
*/
public static final int STATUS_FAILURE_STORAGE = 6;
@@ -281,6 +329,13 @@
* To succeed, the caller must be the current home app.
*/
public @NonNull List<SessionInfo> getAllSessions() {
+ final ApplicationInfo info = mContext.getApplicationInfo();
+ if ("com.google.android.googlequicksearchbox".equals(info.packageName)
+ && info.versionCode <= 300400070) {
+ Log.d(TAG, "Ignoring callback request from old prebuilt");
+ return Collections.EMPTY_LIST;
+ }
+
try {
return mInstaller.getAllSessions(mUserId);
} catch (RemoteException e) {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index eb8b762..142206a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -264,7 +264,7 @@
public final boolean coreApp;
public final boolean multiArch;
- private PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
+ public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
String[] splitCodePaths) {
this.packageName = baseApk.packageName;
this.versionCode = baseApk.versionCode;
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 939cda9..d1fadd6 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -321,7 +321,7 @@
* Mount a secure container with the specified key and owner UID.
* Returns an int consistent with MountServiceResultCode
*/
- public int mountSecureContainer(String id, String key, int ownerUid)
+ public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly)
throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
@@ -331,6 +331,7 @@
_data.writeString(id);
_data.writeString(key);
_data.writeInt(ownerUid);
+ _data.writeInt(readOnly ? 1 : 0);
mRemote.transact(Stub.TRANSACTION_mountSecureContainer, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -834,6 +835,27 @@
}
return _result;
}
+
+ @Override
+ public int resizeSecureContainer(String id, int sizeMb, String key)
+ throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ int _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(id);
+ _data.writeInt(sizeMb);
+ _data.writeString(key);
+ mRemote.transact(Stub.TRANSACTION_resizeSecureContainer, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readInt();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
}
private static final String DESCRIPTOR = "IMountService";
@@ -918,6 +940,8 @@
static final int TRANSACTION_getField = IBinder.FIRST_CALL_TRANSACTION + 39;
+ static final int TRANSACTION_resizeSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 40;
+
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -1082,7 +1106,9 @@
key = data.readString();
int ownerUid;
ownerUid = data.readInt();
- int resultCode = mountSecureContainer(id, key, ownerUid);
+ boolean readOnly;
+ readOnly = data.readInt() != 0;
+ int resultCode = mountSecureContainer(id, key, ownerUid, readOnly);
reply.writeNoException();
reply.writeInt(resultCode);
return true;
@@ -1308,6 +1334,19 @@
reply.writeString(contents);
return true;
}
+ case TRANSACTION_resizeSecureContainer: {
+ data.enforceInterface(DESCRIPTOR);
+ String id;
+ id = data.readString();
+ int sizeMb;
+ sizeMb = data.readInt();
+ String key;
+ key = data.readString();
+ int resultCode = resizeSecureContainer(id, sizeMb, key);
+ reply.writeNoException();
+ reply.writeInt(resultCode);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
}
@@ -1405,7 +1444,8 @@
* Mount a secure container with the specified key and owner UID. Returns an
* int consistent with MountServiceResultCode
*/
- public int mountSecureContainer(String id, String key, int ownerUid) throws RemoteException;
+ public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly)
+ throws RemoteException;
/**
* Mount external storage at given mount point. Returns an int consistent
@@ -1571,4 +1611,6 @@
* @return contents of field
*/
public String getField(String field) throws RemoteException;
+
+ public int resizeSecureContainer(String id, int sizeMb, String key) throws RemoteException;
}
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 179d5e8..02f675c 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -25,6 +25,7 @@
import static android.system.OsConstants.S_IXGRP;
import static android.system.OsConstants.S_IXOTH;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.Package;
@@ -51,9 +52,11 @@
*/
public class NativeLibraryHelper {
private static final String TAG = "NativeHelper";
-
private static final boolean DEBUG_NATIVE = false;
+ public static final String LIB_DIR_NAME = "lib";
+ public static final String LIB64_DIR_NAME = "lib64";
+
// Special value for {@code PackageParser.Package#cpuAbiOverride} to indicate
// that the cpuAbiOverride must be clear.
public static final String CLEAR_ABI_OVERRIDE = "-";
@@ -70,6 +73,7 @@
private volatile boolean mClosed;
final long[] apkHandles;
+ final boolean multiArch;
public static Handle create(File packageFile) throws IOException {
try {
@@ -81,14 +85,15 @@
}
public static Handle create(Package pkg) throws IOException {
- return create(pkg.getAllCodePaths());
+ return create(pkg.getAllCodePaths(),
+ (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0);
}
public static Handle create(PackageLite lite) throws IOException {
- return create(lite.getAllCodePaths());
+ return create(lite.getAllCodePaths(), lite.multiArch);
}
- private static Handle create(List<String> codePaths) throws IOException {
+ private static Handle create(List<String> codePaths, boolean multiArch) throws IOException {
final int size = codePaths.size();
final long[] apkHandles = new long[size];
for (int i = 0; i < size; i++) {
@@ -103,11 +108,12 @@
}
}
- return new Handle(apkHandles);
+ return new Handle(apkHandles, multiArch);
}
- Handle(long[] apkHandles) {
+ Handle(long[] apkHandles, boolean multiArch) {
this.apkHandles = apkHandles;
+ this.multiArch = multiArch;
mGuard.open("close");
}
@@ -159,8 +165,7 @@
* @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
* error code from that class if not
*/
- public static int copyNativeBinariesIfNeededLI(Handle handle, File sharedLibraryDir,
- String abi) {
+ public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
for (long apkHandle : handle.apkHandles) {
int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi);
if (res != INSTALL_SUCCEEDED) {
@@ -267,7 +272,7 @@
}
}
- private static long sumNativeBinaries(Handle handle, String[] abiList) {
+ private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) {
int abi = findSupportedAbi(handle, abiList);
if (abi >= 0) {
return sumNativeBinaries(handle, abiList[abi]);
@@ -276,7 +281,7 @@
}
}
- public static int copyNativeBinariesIfNeededLI(Handle handle, File libraryRoot,
+ public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
String[] abiList, boolean useIsaSubdir) throws IOException {
createNativeLibrarySubdir(libraryRoot);
@@ -300,7 +305,7 @@
subDir = libraryRoot;
}
- int copyRet = copyNativeBinariesIfNeededLI(handle, subDir, abiList[abi]);
+ int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]);
if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
return copyRet;
}
@@ -309,10 +314,10 @@
return abi;
}
- public static int copyNativeBinariesIfNeededLI(Handle handle, File libraryRoot,
- String abiOverride, boolean multiArch) {
+ public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
+ String abiOverride) {
try {
- if (multiArch) {
+ if (handle.multiArch) {
// Warn if we've set an abiOverride for multi-lib packages..
// By definition, we need to copy both 32 and 64 bit libraries for
// such packages.
@@ -322,7 +327,7 @@
int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
- copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot,
+ copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
@@ -332,7 +337,7 @@
}
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
- copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot,
+ copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
@@ -355,7 +360,7 @@
abiList = Build.SUPPORTED_32_BIT_ABIS;
}
- int copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot, abiList,
+ int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList,
true /* use isa specific subdirs */);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
@@ -370,10 +375,10 @@
}
}
- public static long sumNativeBinaries(Handle handle, String abiOverride, boolean multiArch)
+ public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
throws IOException {
long sum = 0;
- if (multiArch) {
+ if (handle.multiArch) {
// Warn if we've set an abiOverride for multi-lib packages..
// By definition, we need to copy both 32 and 64 bit libraries for
// such packages.
@@ -382,11 +387,11 @@
}
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
- sum += sumNativeBinaries(handle, Build.SUPPORTED_32_BIT_ABIS);
+ sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
}
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
- sum += sumNativeBinaries(handle, Build.SUPPORTED_64_BIT_ABIS);
+ sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
}
} else {
String cpuAbiOverride = null;
@@ -403,7 +408,7 @@
abiList = Build.SUPPORTED_32_BIT_ABIS;
}
- sum += sumNativeBinaries(handle, abiList);
+ sum += sumNativeBinariesForSupportedAbi(handle, abiList);
}
return sum;
}
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index a529cc3..c17f4ee 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -16,11 +16,14 @@
package com.android.internal.content;
+import static android.net.TrafficStats.MB_IN_BYTES;
+
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageParser.PackageLite;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
import android.os.FileUtils;
@@ -77,9 +80,10 @@
}
}
- public static String createSdDir(int sizeMb, String cid, String sdEncKey, int uid,
+ public static String createSdDir(long sizeBytes, String cid, String sdEncKey, int uid,
boolean isExternal) {
- // Create mount point via MountService
+ // Round up to nearest MB, plus another MB for filesystem overhead
+ final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
try {
IMountService mountService = getMountService();
@@ -102,19 +106,39 @@
return null;
}
- public static String mountSdDir(String cid, String key, int ownerUid) {
- try {
- int rc = getMountService().mountSecureContainer(cid, key, ownerUid);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.i(TAG, "Failed to mount container " + cid + " rc : " + rc);
- return null;
+ public static boolean resizeSdDir(long sizeBytes, String cid, String sdEncKey) {
+ // Round up to nearest MB, plus another MB for filesystem overhead
+ final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
+ try {
+ IMountService mountService = getMountService();
+ int rc = mountService.resizeSecureContainer(cid, sizeMb, sdEncKey);
+ if (rc == StorageResultCode.OperationSucceeded) {
+ return true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "MountService running?");
}
- return getMountService().getSecureContainerPath(cid);
- } catch (RemoteException e) {
- Log.e(TAG, "MountService running?");
+ Log.e(TAG, "Failed to create secure container " + cid);
+ return false;
}
- return null;
- }
+
+ public static String mountSdDir(String cid, String key, int ownerUid) {
+ return mountSdDir(cid, key, ownerUid, true);
+ }
+
+ public static String mountSdDir(String cid, String key, int ownerUid, boolean readOnly) {
+ try {
+ int rc = getMountService().mountSecureContainer(cid, key, ownerUid, readOnly);
+ if (rc != StorageResultCode.OperationSucceeded) {
+ Log.i(TAG, "Failed to mount container " + cid + " rc : " + rc);
+ return null;
+ }
+ return getMountService().getSecureContainerPath(cid);
+ } catch (RemoteException e) {
+ Log.e(TAG, "MountService running?");
+ }
+ return null;
+ }
public static boolean unMountSdDir(String cid) {
try {
@@ -400,6 +424,37 @@
}
}
+ public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked,
+ String abiOverride) throws IOException {
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(pkg);
+ return calculateInstalledSize(pkg, handle, isForwardLocked, abiOverride);
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
+ }
+
+ public static long calculateInstalledSize(PackageLite pkg, NativeLibraryHelper.Handle handle,
+ boolean isForwardLocked, String abiOverride) throws IOException {
+ long sizeBytes = 0;
+
+ // Include raw APKs, and possibly unpacked resources
+ for (String codePath : pkg.getAllCodePaths()) {
+ final File codeFile = new File(codePath);
+ sizeBytes += codeFile.length();
+
+ if (isForwardLocked) {
+ sizeBytes += PackageHelper.extractPublicFiles(codeFile, null);
+ }
+ }
+
+ // Include all relevant native code
+ sizeBytes += NativeLibraryHelper.sumNativeBinariesWithOverride(handle, abiOverride);
+
+ return sizeBytes;
+ }
+
public static String replaceEnd(String str, String before, String after) {
if (!str.endsWith(before)) {
throw new IllegalArgumentException(
diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
index 7ad35d0..06c495e 100644
--- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
@@ -16,7 +16,7 @@
package android.content.pm;
-import com.android.internal.content.PackageHelper;
+import static android.net.TrafficStats.MB_IN_BYTES;
import android.os.IBinder;
import android.os.RemoteException;
@@ -25,6 +25,8 @@
import android.test.AndroidTestCase;
import android.util.Log;
+import com.android.internal.content.PackageHelper;
+
public class PackageHelperTests extends AndroidTestCase {
private static final boolean localLOGV = true;
public static final String TAG = "PackageHelperTests";
@@ -81,8 +83,8 @@
public void testMountAndPullSdCard() {
try {
fullId = PREFIX;
- fullId2 = PackageHelper.createSdDir(1024, fullId, "none", android.os.Process.myUid(),
- true);
+ fullId2 = PackageHelper.createSdDir(1024 * MB_IN_BYTES, fullId, "none",
+ android.os.Process.myUid(), true);
Log.d(TAG,PackageHelper.getSdDir(fullId));
PackageHelper.unMountSdDir(fullId);
diff --git a/core/tests/coretests/src/android/os/storage/AsecTests.java b/core/tests/coretests/src/android/os/storage/AsecTests.java
index abb8eae..4f724fe 100644
--- a/core/tests/coretests/src/android/os/storage/AsecTests.java
+++ b/core/tests/coretests/src/android/os/storage/AsecTests.java
@@ -90,7 +90,7 @@
String fullId = SECURE_CONTAINER_PREFIX + localId;
IMountService ms = getMs();
- return ms.mountSecureContainer(fullId, key, android.os.Process.myUid());
+ return ms.mountSecureContainer(fullId, key, android.os.Process.myUid(), true);
}
private int renameContainer(String localId1, String localId2) throws Exception {
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 5701678..621de18 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -295,6 +295,8 @@
data.writeString16(id);
data.writeString16(key);
data.writeInt32(ownerUid);
+ // Assume read-only
+ data.writeInt32(1);
if (remote()->transact(TRANSACTION_mountSecureContainer, data, &reply) != NO_ERROR) {
ALOGD("mountSecureContainer couldn't call remote");
return -1;
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index fae30e5..1f28324 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -16,7 +16,7 @@
package com.android.defcontainer;
-import static android.net.TrafficStats.MB_IN_BYTES;
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import android.app.IntentService;
import android.content.Context;
@@ -67,8 +67,6 @@
public class DefaultContainerService extends IntentService {
private static final String TAG = "DefContainer";
- private static final String LIB_DIR_NAME = "lib";
-
// TODO: migrate native code unpacking to always be a derivative work
private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
@@ -168,7 +166,7 @@
final long sizeBytes;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
- sizeBytes = calculateInstalledSizeInner(pkg, isForwardLocked, abiOverride);
+ sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
@@ -253,7 +251,7 @@
final PackageParser.PackageLite pkg;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
- return calculateInstalledSizeInner(pkg, isForwardLocked, abiOverride);
+ return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to calculate installed size: " + e);
return Long.MAX_VALUE;
@@ -315,13 +313,12 @@
// Calculate container size, rounding up to nearest MB and adding an
// extra MB for filesystem overhead
- final long sizeBytes = calculateInstalledSizeInner(pkg, handle, isForwardLocked,
- abiOverride);
- final int sizeMb = ((int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES)) + 1;
+ final long sizeBytes = PackageHelper.calculateInstalledSize(pkg, handle,
+ isForwardLocked, abiOverride);
// Create new container
- final String newMountPath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid(),
- isExternal);
+ final String newMountPath = PackageHelper.createSdDir(sizeBytes, newCid, key,
+ Process.myUid(), isExternal);
if (newMountPath == null) {
throw new IOException("Failed to create container " + newCid);
}
@@ -339,8 +336,8 @@
// Extract native code
final File libraryRoot = new File(targetDir, LIB_DIR_NAME);
- final int res = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, libraryRoot,
- abiOverride, pkg.multiArch);
+ final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
+ abiOverride);
if (res != PackageManager.INSTALL_SUCCEEDED) {
throw new IOException("Failed to extract native code, res=" + res);
}
@@ -415,35 +412,4 @@
Os.chmod(targetFile.getAbsolutePath(), 0644);
}
}
-
- private long calculateInstalledSizeInner(PackageLite pkg, boolean isForwardLocked,
- String abiOverride) throws IOException {
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(pkg);
- return calculateInstalledSizeInner(pkg, handle, isForwardLocked, abiOverride);
- } finally {
- IoUtils.closeQuietly(handle);
- }
- }
-
- private long calculateInstalledSizeInner(PackageLite pkg, NativeLibraryHelper.Handle handle,
- boolean isForwardLocked, String abiOverride) throws IOException {
- long sizeBytes = 0;
-
- // Include raw APKs, and possibly unpacked resources
- for (String codePath : pkg.getAllCodePaths()) {
- final File codeFile = new File(codePath);
- sizeBytes += codeFile.length();
-
- if (isForwardLocked) {
- sizeBytes += PackageHelper.extractPublicFiles(codeFile, null);
- }
- }
-
- // Include all relevant native code
- sizeBytes += NativeLibraryHelper.sumNativeBinaries(handle, abiOverride, pkg.multiArch);
-
- return sizeBytes;
- }
}
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.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 @@
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 @@
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 @@
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 @@
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.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 @@
// 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 @@
// 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 @@
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 @@
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 @@
}
}
+ // 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 @@
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);
-
- throw new UnsupportedOperationException("TODO: finish fleshing out ASEC support");
+ if (existingApp == null) {
+ throw new IllegalStateException(
+ "Missing existing app " + params.appPackageName);
}
+ 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 @@
}
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 @@
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 @@
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.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.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.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 @@
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,20 +130,6 @@
private String mFinalMessage;
@GuardedBy("mLock")
- private File mResolvedStageDir;
-
- /**
- * 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).
- * <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;
-
- @GuardedBy("mLock")
private ArrayList<FileBridge> mBridges = new ArrayList<>();
@GuardedBy("mLock")
@@ -157,6 +140,25 @@
private int mVersionCode;
private Signature[] mSignatures;
+ /**
+ * 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 File mResolvedBaseFile;
+
+ @GuardedBy("mLock")
+ private File mResolvedStageDir;
+
+ @GuardedBy("mLock")
+ 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
public boolean handleMessage(Message msg) {
@@ -168,9 +170,10 @@
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 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 @@
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 @@
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 @@
* 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 String[] getNames() {
assertNotSealed("getNames");
try {
- return getStageDir().list();
+ return resolveStageDir().list();
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
@@ -339,8 +342,10 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
};
- mPm.installStage(mPackageName, this.internalStageDir, this.externalStageCid, localObserver,
+ mPm.installStage(mPackageName, stageDir, stageCid, localObserver,
params, installerPackageName, installerUid, new UserHandle(userId));
}
@@ -490,81 +519,88 @@
* 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 @@
"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);
}
- assertPackageConsistent("Existing base", info.packageName, info.versionCode,
- info.signatures);
+ assertApkConsistent("Existing base", existingBase);
+
+ // Inherit base if not overridden
+ if (mResolvedBaseFile == null) {
+ mResolvedBaseFile = new File(app.getBaseCodePath());
+ mResolvedInheritedFiles.add(mResolvedBaseFile);
+ }
+
+ // 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();
}
}
- if (LOGD) Slog.d(TAG, "Spliced " + n + " existing APKs into stage");
+ 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 (!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 @@
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 @@
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_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 @@
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 @@
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 @@
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 @@
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 @@
+ " 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 @@
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 @@
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 @@
if (isAsec) {
copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
} else {
- copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
+ copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
}
@@ -7782,7 +7783,8 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
if (srcArgs != null) {
final String codePath = srcArgs.getCodePath();
targetArgs = createInstallArgsForMoveTarget(codePath, flags, packageName,
- instructionSets, isMultiArch);
+ instructionSets);
} else {
targetArgs = null;
}
@@ -9002,8 +9018,6 @@
}
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 @@
* 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 @@
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 @@
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 @@
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 @@
/** 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 @@
/** 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 @@
}
/** 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 @@
}
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);
}
- };
-
- ret = imcs.copyPackage(originFile.getAbsolutePath(), target);
- if (ret != PackageManager.INSTALL_SUCCEEDED) {
- Slog.e(TAG, "Failed to copy package");
- return ret;
+ 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());
+ }
}
+ };
+
+ 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 @@
/** 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 @@
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 @@
}
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
* 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 @@
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 @@
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 :"