summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2014-08-16 19:09:13 -0700
committerJeff Sharkey <jsharkey@android.com>2014-08-18 15:52:24 -0700
commit742e790294b3441b79f715fe447069b63c6065db (patch)
tree68ac792721f920e154e26f56c1d14b3ee01ba18d
parent2aaed141fa22b235ecbd009b11c426abfa5b847c (diff)
downloadframeworks_base-742e790294b3441b79f715fe447069b63c6065db.zip
frameworks_base-742e790294b3441b79f715fe447069b63c6065db.tar.gz
frameworks_base-742e790294b3441b79f715fe447069b63c6065db.tar.bz2
Progress towards staging ASECs.
Move location selection logic into shared PackageHelper location, and share it between DCS and PackageInstaller. Fix bugs related to installed footprint calculation; always count unpacked native libs. Have PMS do its own threshold checking, since it's fine to stat devices. PMS only ever deleted staging ASECs, so move that logic into installer and nuke unclaimed staging ASECs. Allocate legacy ASEC names using PackageInstaller to make sure they don't conflict with sessions. Start wiring up session to allocate ASEC and pass through staged container for installation. Fix bug to actually delete invalid cluster-style installs. Bug: 16514385 Change-Id: I325e0c4422fc128398c921ba45fd73ecf05fc2a9
-rw-r--r--api/current.txt16
-rw-r--r--core/java/android/content/pm/PackageInstaller.java19
-rw-r--r--core/java/android/os/storage/StorageManager.java10
-rw-r--r--core/java/com/android/internal/app/IMediaContainerService.aidl5
-rw-r--r--core/java/com/android/internal/content/PackageHelper.java83
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java292
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java241
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java163
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java225
9 files changed, 538 insertions, 516 deletions
diff --git a/api/current.txt b/api/current.txt
index f2200af..0154dd2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8666,14 +8666,14 @@ package android.content.pm {
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 int STATUS_FAILURE = 1; // 0x1
- field public static final int STATUS_FAILURE_ABORTED = 2; // 0x2
- field public static final int STATUS_FAILURE_BLOCKED = 1; // 0x1
- field public static final int STATUS_FAILURE_CONFLICT = 4; // 0x4
- field public static final int STATUS_FAILURE_INCOMPATIBLE = 6; // 0x6
- field public static final int STATUS_FAILURE_INVALID = 3; // 0x3
- field public static final int STATUS_FAILURE_STORAGE = 5; // 0x5
+ field public static final int STATUS_FAILURE_ABORTED = 3; // 0x3
+ field public static final int STATUS_FAILURE_BLOCKED = 2; // 0x2
+ field public static final int STATUS_FAILURE_CONFLICT = 5; // 0x5
+ field public static final int STATUS_FAILURE_INCOMPATIBLE = 7; // 0x7
+ field public static final int STATUS_FAILURE_INVALID = 4; // 0x4
+ field public static final int STATUS_FAILURE_STORAGE = 6; // 0x6
+ field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
field public static final int STATUS_SUCCESS = 0; // 0x0
- field public static final int STATUS_USER_ACTION_REQUIRED = -1; // 0xffffffff
}
public static class PackageInstaller.Session implements java.io.Closeable {
@@ -8681,7 +8681,7 @@ package android.content.pm {
method public void close();
method public void commit(android.content.IntentSender);
method public void fsync(java.io.OutputStream) throws java.io.IOException;
- method public java.lang.String[] getNames();
+ method public java.lang.String[] getNames() throws java.io.IOException;
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
method public void setProgress(float);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index aa4ea45..4ac701f 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -129,7 +129,7 @@ public class PackageInstaller {
*
* @see Intent#getParcelableExtra(String)
*/
- public static final int STATUS_USER_ACTION_REQUIRED = -1;
+ public static final int STATUS_PENDING_USER_ACTION = -1;
/**
* The operation succeeded.
@@ -152,7 +152,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_BLOCKED = 1;
+ public static final int STATUS_FAILURE_BLOCKED = 2;
/**
* The operation failed because it was actively aborted. For example, the
@@ -161,7 +161,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_ABORTED = 2;
+ public static final int STATUS_FAILURE_ABORTED = 3;
/**
* The operation failed because one or more of the APKs was invalid. For
@@ -170,7 +170,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_INVALID = 3;
+ public static final int STATUS_FAILURE_INVALID = 4;
/**
* The operation failed because it conflicts (or is inconsistent with) with
@@ -183,7 +183,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_CONFLICT = 4;
+ public static final int STATUS_FAILURE_CONFLICT = 5;
/**
* The operation failed because of storage issues. For example, the device
@@ -192,7 +192,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_STORAGE = 5;
+ public static final int STATUS_FAILURE_STORAGE = 6;
/**
* The operation failed because it is fundamentally incompatible with this
@@ -202,7 +202,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_INCOMPATIBLE = 6;
+ public static final int STATUS_FAILURE_INCOMPATIBLE = 7;
private final Context mContext;
private final PackageManager mPm;
@@ -584,9 +584,12 @@ public class PackageInstaller {
* This returns all names which have been previously written through
* {@link #openWrite(String, long, long)} as part of this session.
*/
- public @NonNull String[] getNames() {
+ public @NonNull String[] getNames() throws IOException {
try {
return mSession.getNames();
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 68b91cb..4cdafe1 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -619,6 +619,16 @@ public class StorageManager {
private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
/**
+ * Return the number of available bytes until the given path is considered
+ * running low on storage.
+ *
+ * @hide
+ */
+ public long getStorageBytesUntilLow(File path) {
+ return path.getUsableSpace() - getStorageFullBytes(path);
+ }
+
+ /**
* Return the number of available bytes at which the given path is
* considered running low on storage.
*
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 7a3ffdf..81ea191 100644
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -25,10 +25,7 @@ interface IMediaContainerService {
boolean isExternal, boolean isForwardLocked, String abiOverride);
int copyPackage(String packagePath, in IParcelFileDescriptorFactory target);
- PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, long threshold,
- String abiOverride);
- boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked, long threshold);
- boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked, String abiOverride);
+ PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, String abiOverride);
ObbInfo getObbInfo(String filename);
long calculateDirectorySize(String directory);
/** Return file system stats: [0] is total bytes, [1] is available bytes */
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index eff6684..fd96f64 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -16,14 +16,21 @@
package com.android.internal.content;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Environment;
import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
import android.os.storage.StorageResultCode;
import android.util.Log;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -33,8 +40,6 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
-import libcore.io.IoUtils;
-
/**
* Constants used internally between the PackageManager
* and media container service transports.
@@ -298,4 +303,78 @@ public class PackageHelper {
}
return false;
}
+
+ /**
+ * Given a requested {@link PackageInfo#installLocation} and calculated
+ * install size, pick the actual location to install the app.
+ */
+ public static int resolveInstallLocation(Context context, int installLocation, long sizeBytes,
+ int installFlags) {
+ final int prefer;
+ final boolean checkBoth;
+ if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
+ prefer = RECOMMEND_INSTALL_INTERNAL;
+ checkBoth = false;
+ } else if ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ prefer = RECOMMEND_INSTALL_EXTERNAL;
+ checkBoth = false;
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ prefer = RECOMMEND_INSTALL_INTERNAL;
+ checkBoth = false;
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+ prefer = RECOMMEND_INSTALL_EXTERNAL;
+ checkBoth = true;
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
+ prefer = RECOMMEND_INSTALL_INTERNAL;
+ checkBoth = true;
+ } else {
+ prefer = RECOMMEND_INSTALL_INTERNAL;
+ checkBoth = false;
+ }
+
+ final boolean emulated = Environment.isExternalStorageEmulated();
+ final StorageManager storage = StorageManager.from(context);
+
+ boolean fitsOnInternal = false;
+ if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {
+ fitsOnInternal = (sizeBytes
+ <= storage.getStorageBytesUntilLow(Environment.getDataDirectory()));
+ }
+
+ boolean fitsOnExternal = false;
+ if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)) {
+ fitsOnExternal = (sizeBytes
+ <= storage.getStorageBytesUntilLow(Environment.getExternalStorageDirectory()));
+ }
+
+ if (prefer == RECOMMEND_INSTALL_INTERNAL) {
+ if (fitsOnInternal) {
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ }
+ } else if (!emulated && prefer == RECOMMEND_INSTALL_EXTERNAL) {
+ if (fitsOnExternal) {
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ }
+ }
+
+ if (checkBoth) {
+ if (fitsOnInternal) {
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ } else if (!emulated && fitsOnExternal) {
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ }
+ }
+
+ /*
+ * If they requested to be on the external media by default, return that
+ * the media was unavailable. Otherwise, indicate there was insufficient
+ * storage space available.
+ */
+ if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)
+ && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
+ } else {
+ return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ }
+ }
}
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 20a621b..4c225c1 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -16,11 +16,13 @@
package com.android.defcontainer;
+import static android.net.TrafficStats.MB_IN_BYTES;
+
import android.app.IntentService;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageCleanItem;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -37,8 +39,6 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.StatFs;
-import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStatVfs;
@@ -50,13 +50,11 @@ import com.android.internal.content.PackageHelper;
import com.android.internal.os.IParcelFileDescriptorFactory;
import com.android.internal.util.ArrayUtils;
-import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
import libcore.io.Streams;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -155,10 +153,12 @@ public class DefaultContainerService extends IntentService {
* containing one or more APKs.
*/
@Override
- public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags,
- long threshold, String abiOverride) {
- PackageInfoLite ret = new PackageInfoLite();
+ public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags,
+ String abiOverride) {
+ final Context context = DefaultContainerService.this;
+ final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
+ PackageInfoLite ret = new PackageInfoLite();
if (packagePath == null) {
Slog.i(TAG, "Invalid package file " + packagePath);
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
@@ -167,10 +167,12 @@ public class DefaultContainerService extends IntentService {
final File packageFile = new File(packagePath);
final PackageParser.PackageLite pkg;
+ final long sizeBytes;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
- } catch (PackageParserException e) {
- Slog.w(TAG, "Failed to parse package at " + packagePath);
+ sizeBytes = calculateInstalledSizeInner(pkg, isForwardLocked, abiOverride);
+ } catch (PackageParserException | IOException e) {
+ Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
if (!packageFile.exists()) {
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
@@ -185,55 +187,13 @@ public class DefaultContainerService extends IntentService {
ret.versionCode = pkg.versionCode;
ret.installLocation = pkg.installLocation;
ret.verifiers = pkg.verifiers;
- ret.recommendedInstallLocation = recommendAppInstallLocation(pkg, flags, threshold,
- abiOverride);
+ ret.recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
+ pkg.installLocation, sizeBytes, flags);
ret.multiArch = pkg.multiArch;
return ret;
}
- /**
- * Determine if package will fit on internal storage.
- *
- * @param packagePath absolute path to the package to be copied. Can be
- * a single monolithic APK file or a cluster directory
- * containing one or more APKs.
- */
- @Override
- public boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked,
- long threshold) throws RemoteException {
- final File packageFile = new File(packagePath);
- final PackageParser.PackageLite pkg;
- try {
- pkg = PackageParser.parsePackageLite(packageFile, 0);
- return isUnderInternalThreshold(pkg, isForwardLocked, threshold);
- } catch (PackageParserException | IOException e) {
- Slog.w(TAG, "Failed to parse package at " + packagePath);
- return false;
- }
- }
-
- /**
- * Determine if package will fit on external storage.
- *
- * @param packagePath absolute path to the package to be copied. Can be
- * a single monolithic APK file or a cluster directory
- * containing one or more APKs.
- */
- @Override
- public boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked,
- String abiOverride) throws RemoteException {
- final File packageFile = new File(packagePath);
- final PackageParser.PackageLite pkg;
- try {
- pkg = PackageParser.parsePackageLite(packageFile, 0);
- return isUnderExternalThreshold(pkg, isForwardLocked, abiOverride);
- } catch (PackageParserException | IOException e) {
- Slog.w(TAG, "Failed to parse package at " + packagePath);
- return false;
- }
- }
-
@Override
public ObbInfo getObbInfo(String filename) {
try {
@@ -295,13 +255,10 @@ public class DefaultContainerService extends IntentService {
final PackageParser.PackageLite pkg;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
- return calculateContainerSize(pkg, isForwardLocked, abiOverride) * 1024 * 1024;
+ return calculateInstalledSizeInner(pkg, isForwardLocked, abiOverride);
} catch (PackageParserException | IOException e) {
- /*
- * Okay, something failed, so let's just estimate it to be 2x
- * the file size. Note this will be 0 if the file doesn't exist.
- */
- return packageFile.length() * 2;
+ Slog.w(TAG, "Failed to calculate installed size: " + e);
+ return Long.MAX_VALUE;
}
}
};
@@ -381,10 +338,12 @@ public class DefaultContainerService extends IntentService {
return null;
}
- // Calculate size of container needed to hold base APK.
+ // Calculate size of container needed to hold base APK. Round up to
+ // nearest MB, and tack on an extra MB for filesystem overhead.
final int sizeMb;
try {
- sizeMb = calculateContainerSize(pkg, handle, isForwardLocked, abis);
+ final long sizeBytes = calculateInstalledSizeInner(pkg, handle, isForwardLocked, abis);
+ sizeMb = ((int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES)) + 1;
} catch (IOException e) {
Slog.w(TAG, "Problem when trying to copy " + codeFile.getPath());
return null;
@@ -523,124 +482,23 @@ public class DefaultContainerService extends IntentService {
}
}
- private static final int PREFER_INTERNAL = 1;
- private static final int PREFER_EXTERNAL = 2;
-
- private int recommendAppInstallLocation(PackageLite pkg, int flags, long threshold,
- String abiOverride) {
- int prefer;
- boolean checkBoth = false;
-
- final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
-
- check_inner : {
- /*
- * Explicit install flags should override the manifest settings.
- */
- if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
- prefer = PREFER_INTERNAL;
- break check_inner;
- } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
- prefer = PREFER_EXTERNAL;
- break check_inner;
- }
-
- /* No install flags. Check for manifest option. */
- if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
- prefer = PREFER_INTERNAL;
- break check_inner;
- } else if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
- prefer = PREFER_EXTERNAL;
- checkBoth = true;
- break check_inner;
- } else if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
- // We default to preferring internal storage.
- prefer = PREFER_INTERNAL;
- checkBoth = true;
- break check_inner;
- }
-
- // Pick user preference
- int installPreference = Settings.Global.getInt(getApplicationContext()
- .getContentResolver(),
- Settings.Global.DEFAULT_INSTALL_LOCATION,
- PackageHelper.APP_INSTALL_AUTO);
- if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) {
- prefer = PREFER_INTERNAL;
- break check_inner;
- } else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) {
- prefer = PREFER_EXTERNAL;
- break check_inner;
- }
-
- /*
- * Fall back to default policy of internal-only if nothing else is
- * specified.
- */
- prefer = PREFER_INTERNAL;
- }
-
- final boolean emulated = Environment.isExternalStorageEmulated();
-
- boolean fitsOnInternal = false;
- if (checkBoth || prefer == PREFER_INTERNAL) {
- try {
- fitsOnInternal = isUnderInternalThreshold(pkg, isForwardLocked, threshold);
- } catch (IOException e) {
- return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
- }
- }
-
- boolean fitsOnSd = false;
- if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) {
- try {
- fitsOnSd = isUnderExternalThreshold(pkg, isForwardLocked, abiOverride);
- } catch (IOException e) {
- return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
- }
- }
-
- if (prefer == PREFER_INTERNAL) {
- if (fitsOnInternal) {
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- }
- } else if (!emulated && prefer == PREFER_EXTERNAL) {
- if (fitsOnSd) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
- }
-
- if (checkBoth) {
- if (fitsOnInternal) {
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- } else if (!emulated && fitsOnSd) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
- }
-
- /*
- * If they requested to be on the external media by default, return that
- * the media was unavailable. Otherwise, indicate there was insufficient
- * storage space available.
- */
- if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)
- && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
- return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
- } else {
- return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ private long calculateInstalledSizeInner(PackageLite pkg, boolean isForwardLocked,
+ String abiOverride) throws IOException {
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(pkg);
+ return calculateInstalledSizeInner(pkg, handle, isForwardLocked,
+ calculateAbiList(handle, abiOverride, pkg.multiArch));
+ } finally {
+ IoUtils.closeQuietly(handle);
}
}
- /**
- * Measure a file to see if it fits within the free space threshold.
- *
- * @param threshold byte threshold to compare against
- * @return true if file fits under threshold
- * @throws FileNotFoundException when APK does not exist
- */
- private boolean isUnderInternalThreshold(PackageLite pkg, boolean isForwardLocked,
- long threshold) throws IOException {
+ private long calculateInstalledSizeInner(PackageLite pkg, NativeLibraryHelper.Handle handle,
+ boolean isForwardLocked, String[] abis) throws IOException {
long sizeBytes = 0;
+
+ // Include raw APKs, and possibly unpacked resources
for (String codePath : pkg.getAllCodePaths()) {
sizeBytes += new File(codePath).length();
@@ -649,47 +507,12 @@ public class DefaultContainerService extends IntentService {
}
}
- final StatFs stat = new StatFs(Environment.getDataDirectory().getPath());
- final long availBytes = stat.getAvailableBytes();
- return (availBytes - sizeBytes) > threshold;
- }
-
- /**
- * Measure a file to see if it fits in the external free space.
- *
- * @return true if file fits
- * @throws IOException when file does not exist
- */
- private boolean isUnderExternalThreshold(PackageLite pkg, boolean isForwardLocked,
- String abiOverride) throws IOException {
- if (Environment.isExternalStorageEmulated()) {
- return false;
- }
-
- final int sizeMb = calculateContainerSize(pkg, isForwardLocked, abiOverride);
-
- final int availSdMb;
- if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
- final StatFs sdStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
- final int blocksToMb = (1 << 20) / sdStats.getBlockSize();
- availSdMb = sdStats.getAvailableBlocks() * blocksToMb;
- } else {
- availSdMb = -1;
+ // Include all relevant native code
+ if (!ArrayUtils.isEmpty(abis)) {
+ sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(handle, abis);
}
- return availSdMb > sizeMb;
- }
-
- private int calculateContainerSize(PackageLite pkg, boolean isForwardLocked, String abiOverride)
- throws IOException {
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(pkg);
- return calculateContainerSize(pkg, handle, isForwardLocked,
- calculateAbiList(handle, abiOverride, pkg.multiArch));
- } finally {
- IoUtils.closeQuietly(handle);
- }
+ return sizeBytes;
}
private String[] calculateAbiList(NativeLibraryHelper.Handle handle, String abiOverride,
@@ -731,43 +554,4 @@ public class DefaultContainerService extends IntentService {
return null;
}
-
- /**
- * Calculate the container size for a package.
- *
- * @return size in megabytes (2^20 bytes)
- * @throws IOException when there is a problem reading the file
- */
- private int calculateContainerSize(PackageLite pkg, NativeLibraryHelper.Handle handle,
- boolean isForwardLocked, String[] abis) throws IOException {
- // Calculate size of container needed to hold APKs.
- long sizeBytes = 0;
- for (String codePath : pkg.getAllCodePaths()) {
- sizeBytes += new File(codePath).length();
-
- if (isForwardLocked) {
- sizeBytes += PackageHelper.extractPublicFiles(codePath, null);
- }
- }
-
- // Check all the native files that need to be copied and add that to the
- // container size.
- if (abis != null) {
- sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(handle, abis);
- }
-
- int sizeMb = (int) (sizeBytes >> 20);
- if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) {
- sizeMb++;
- }
-
- /*
- * Add buffer size because we don't have a good way to determine the
- * real FAT size. Your FAT size varies with how many directory entries
- * you need, how big the whole filesystem is, and other such headaches.
- */
- sizeMb++;
-
- return sizeMb;
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index dca8ad4..0393518 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -19,6 +19,7 @@ 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;
@@ -42,6 +43,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageInstallerSession;
@@ -65,6 +67,7 @@ import android.os.RemoteException;
import android.os.SELinux;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
@@ -75,13 +78,14 @@ import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageHelper;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
-import com.android.server.pm.PackageInstallerSession.Snapshot;
import com.google.android.collect.Sets;
import libcore.io.IoUtils;
@@ -108,6 +112,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
// TODO: remove outstanding sessions when installer package goes away
// TODO: notify listeners in other users when package has been installed there
+ // TODO: purge expired sessions periodically in addition to at reboot
/** XML constants used in {@link #mSessionsFile} */
private static final String TAG_SESSIONS = "sessions";
@@ -117,6 +122,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
private static final String ATTR_CREATED_MILLIS = "createdMillis";
private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
+ private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
private static final String ATTR_SEALED = "sealed";
private static final String ATTR_MODE = "mode";
private static final String ATTR_INSTALL_FLAGS = "installFlags";
@@ -139,6 +145,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private final Context mContext;
private final PackageManagerService mPm;
private final AppOpsManager mAppOps;
+ private final StorageManager mStorage;
private final File mStagingDir;
private final HandlerThread mInstallThread;
@@ -165,10 +172,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@GuardedBy("mSessions")
private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
+ /** Sessions allocated to legacy users */
+ @GuardedBy("mSessions")
+ private final SparseBooleanArray mLegacySessions = new SparseBooleanArray();
+
private static final FilenameFilter sStageFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
- return name.startsWith("vmdl") && name.endsWith(".tmp");
+ return isStageName(name);
}
};
@@ -176,6 +187,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
mContext = context;
mPm = pm;
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mStorage = StorageManager.from(mContext);
mStagingDir = stagingDir;
@@ -190,13 +202,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
synchronized (mSessions) {
readSessionsLocked();
- // Clean up orphaned staging directories
- final ArraySet<File> stages = Sets.newArraySet(mStagingDir.listFiles(sStageFilter));
+ final ArraySet<File> unclaimed = Sets.newArraySet(mStagingDir.listFiles(sStageFilter));
+
+ // Ignore stages claimed by active sessions
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
- stages.remove(session.sessionStageDir);
+ unclaimed.remove(session.internalStageDir);
}
- for (File stage : stages) {
+
+ // Clean up orphaned staging directories
+ for (File stage : unclaimed) {
Slog.w(TAG, "Deleting orphan stage " + stage);
if (stage.isDirectory()) {
FileUtils.deleteContents(stage);
@@ -206,22 +221,64 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
}
- public static boolean isStageFile(File file) {
- return sStageFilter.accept(null, file.getName());
+ public void onSecureContainersAvailable() {
+ synchronized (mSessions) {
+ final ArraySet<String> unclaimed = new ArraySet<>();
+ for (String cid : PackageHelper.getSecureContainerList()) {
+ if (isStageName(cid)) {
+ unclaimed.add(cid);
+ }
+ }
+
+ // 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;
+
+ if (unclaimed.remove(cid)) {
+ // Claimed by active session, mount it
+ PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
+ Process.SYSTEM_UID);
+ }
+ }
+
+ // Clean up orphaned staging containers
+ for (String cid : unclaimed) {
+ Slog.w(TAG, "Deleting orphan container " + cid);
+ PackageHelper.destroySdDir(cid);
+ }
+ }
+ }
+
+ public static boolean isStageName(String name) {
+ final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
+ final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
+ final boolean isLegacyContainer = name.startsWith("smdl2tmp");
+ return isFile || isContainer || isLegacyContainer;
}
@Deprecated
- public File allocateSessionDir() throws IOException {
+ public File allocateInternalStageDirLegacy() throws IOException {
synchronized (mSessions) {
try {
final int sessionId = allocateSessionIdLocked();
- return prepareSessionStageDir(sessionId);
+ mLegacySessions.put(sessionId, true);
+ return prepareInternalStageDir(sessionId);
} catch (IllegalStateException e) {
throw new IOException(e);
}
}
}
+ @Deprecated
+ public String allocateExternalStageCidLegacy() {
+ synchronized (mSessions) {
+ final int sessionId = allocateSessionIdLocked();
+ mLegacySessions.put(sessionId, true);
+ return "smdl" + sessionId + ".tmp";
+ }
+ }
+
private void readSessionsLocked() {
if (LOGD) Slog.v(TAG, "readSessionsLocked()");
@@ -246,9 +303,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
Slog.w(TAG, "Abandoning old session first created at "
+ session.createdMillis);
valid = false;
- } else if (!session.sessionStageDir.exists()) {
- Slog.w(TAG, "Abandoning session with missing stage "
- + session.sessionStageDir);
+ } else if (session.internalStageDir != null
+ && !session.internalStageDir.exists()) {
+ Slog.w(TAG, "Abandoning internal session with missing stage "
+ + session.internalStageDir);
valid = false;
} else {
valid = true;
@@ -281,7 +339,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
final int userId = readIntAttribute(in, ATTR_USER_ID);
final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
- final File sessionStageDir = new File(readStringAttribute(in, ATTR_SESSION_STAGE_DIR));
+ final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
+ final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
+ final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
final SessionParams params = new SessionParams(
@@ -299,7 +359,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
return new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
- createdMillis, sessionStageDir, sealed);
+ createdMillis, stageDir, stageCid, sealed);
}
private void writeSessionsLocked() {
@@ -332,7 +392,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session)
throws IOException {
final SessionParams params = session.params;
- final Snapshot snapshot = session.snapshot();
out.startTag(null, TAG_SESSION);
@@ -341,9 +400,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
session.installerPackageName);
writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
- writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
- session.sessionStageDir.getAbsolutePath());
- writeBooleanAttribute(out, ATTR_SEALED, snapshot.sealed);
+ if (session.internalStageDir != null) {
+ writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
+ session.internalStageDir.getAbsolutePath());
+ }
+ if (session.externalStageCid != null) {
+ writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.externalStageCid);
+ }
+ writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
writeIntAttribute(out, ATTR_MODE, params.mode);
writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
@@ -372,6 +436,15 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
+ try {
+ return createSessionInternal(params, installerPackageName, userId);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
+ private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
+ throws IOException {
final int callingUid = Binder.getCallingUid();
mPm.enforceCrossUserPermission(callingUid, userId, true, "createSession");
@@ -393,14 +466,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
params.installFlags |= INSTALL_REPLACE_EXISTING;
}
- switch (params.mode) {
- case SessionParams.MODE_FULL_INSTALL:
- case SessionParams.MODE_INHERIT_EXISTING:
- break;
- default:
- throw new IllegalArgumentException("Params must have valid mode set");
- }
-
// Defensively resize giant app icons
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
@@ -413,13 +478,41 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
}
- // Sanity check that install could fit
- if (params.sizeBytes > 0) {
- try {
- mPm.freeStorage(params.sizeBytes);
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
+ // Figure out where we're going to be staging session data
+ final boolean stageInternal;
+
+ if (params.mode == SessionParams.MODE_FULL_INSTALL) {
+ // Brand new install, use best resolved location. This also verifies
+ // that target has enough free space for the install.
+ final int resolved = PackageHelper.resolveInstallLocation(mContext,
+ params.installLocation, params.sizeBytes, params.installFlags);
+ if (resolved == PackageHelper.RECOMMEND_INSTALL_INTERNAL) {
+ stageInternal = true;
+ } else if (resolved == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
+ stageInternal = false;
+ } else {
+ throw new IOException("No storage with enough free space; res=" + resolved);
}
+
+ } 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,
+ userId);
+ if (info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ checkExternalStorage(params.sizeBytes);
+
+ throw new UnsupportedOperationException("TODO: finish fleshing out ASEC support");
+ }
+
+ } else {
+ throw new IllegalArgumentException("Invalid install mode: " + params.mode);
}
final int sessionId;
@@ -437,14 +530,21 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
"Too many historical sessions for UID " + callingUid);
}
+ final long createdMillis = System.currentTimeMillis();
sessionId = allocateSessionIdLocked();
- final long createdMillis = System.currentTimeMillis();
- final File sessionStageDir = prepareSessionStageDir(sessionId);
+ // We're staging to exactly one location
+ File stageDir = null;
+ String stageCid = null;
+ if (stageInternal) {
+ stageDir = prepareInternalStageDir(sessionId);
+ } else {
+ stageCid = prepareExternalStageCid(sessionId, params.sizeBytes);
+ }
session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
- createdMillis, sessionStageDir, false);
+ createdMillis, stageDir, stageCid, false);
mSessions.put(sessionId, session);
}
@@ -453,6 +553,29 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
return sessionId;
}
+ private void checkInternalStorage(long sizeBytes) throws IOException {
+ if (sizeBytes <= 0) return;
+
+ final File target = Environment.getDataDirectory();
+ final long targetBytes = sizeBytes + mStorage.getStorageLowBytes(target);
+
+ mPm.freeStorage(targetBytes);
+ if (target.getUsableSpace() < targetBytes) {
+ throw new IOException("Not enough internal space to write " + sizeBytes + " bytes");
+ }
+ }
+
+ private void checkExternalStorage(long sizeBytes) throws IOException {
+ if (sizeBytes <= 0) return;
+
+ final File target = Environment.getExternalStorageDirectory();
+ final long targetBytes = sizeBytes + mStorage.getStorageLowBytes(target);
+
+ if (target.getUsableSpace() < targetBytes) {
+ throw new IOException("Not enough external space to write " + sizeBytes + " bytes");
+ }
+ }
+
@Override
public IPackageInstallerSession openSession(int sessionId) {
synchronized (mSessions) {
@@ -463,9 +586,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
if (!isCallingUidOwner(session)) {
throw new SecurityException("Caller has no access to session " + sessionId);
}
- if (session.openCount.getAndIncrement() == 0) {
- mCallbacks.notifySessionOpened(sessionId, session.userId);
- }
+ session.open();
return session;
}
}
@@ -475,7 +596,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
int sessionId;
do {
sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
- if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null) {
+ if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null
+ && !mLegacySessions.get(sessionId, false)) {
return sessionId;
}
} while (n++ < 32);
@@ -483,11 +605,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
throw new IllegalStateException("Failed to allocate session ID");
}
- private File prepareSessionStageDir(int sessionId) {
+ private File prepareInternalStageDir(int sessionId) throws IOException {
final File file = new File(mStagingDir, "vmdl" + sessionId + ".tmp");
if (file.exists()) {
- throw new IllegalStateException("Session dir already exists: " + file);
+ throw new IOException("Session dir already exists: " + file);
}
try {
@@ -495,16 +617,34 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
Os.chmod(file.getAbsolutePath(), 0755);
} catch (ErrnoException e) {
// This purposefully throws if directory already exists
- throw new IllegalStateException("Failed to prepare session dir", e);
+ throw new IOException("Failed to prepare session dir", e);
}
if (!SELinux.restorecon(file)) {
- throw new IllegalStateException("Failed to restorecon session dir");
+ throw new IOException("Failed to restorecon session dir");
}
return file;
}
+ private String prepareExternalStageCid(int sessionId, long sizeBytes) throws IOException {
+ if (sizeBytes <= 0) {
+ throw new IOException("Session must provide valid size for ASEC");
+ }
+
+ 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(),
+ Process.SYSTEM_UID, true) == null) {
+ throw new IOException("Failed to create ASEC");
+ }
+
+ return cid;
+ }
+
@Override
public SessionInfo getSessionInfo(int sessionId) {
synchronized (mSessions) {
@@ -643,7 +783,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
public void onUserActionRequired(Intent intent) {
final Intent fillIn = new Intent();
fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_USER_ACTION_REQUIRED);
+ PackageInstaller.STATUS_PENDING_USER_ACTION);
fillIn.putExtra(Intent.EXTRA_INTENT, intent);
try {
mTarget.sendIntent(mContext, 0, fillIn, null, null);
@@ -679,7 +819,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
public void onUserActionRequired(Intent intent) {
final Intent fillIn = new Intent();
fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_USER_ACTION_REQUIRED);
+ PackageInstaller.STATUS_PENDING_USER_ACTION);
fillIn.putExtra(Intent.EXTRA_INTENT, intent);
try {
mTarget.sendIntent(mContext, 0, fillIn, null, null);
@@ -817,6 +957,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
pw.println();
pw.decreaseIndent();
+
+ pw.println("Legacy install sessions:");
+ pw.increaseIndent();
+ pw.println(mLegacySessions.toString());
+ pw.decreaseIndent();
}
}
@@ -825,6 +970,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
}
+ public void onSessionOpened(PackageInstallerSession session) {
+ mCallbacks.notifySessionOpened(session.sessionId, session.userId);
+ }
+
public void onSessionClosed(PackageInstallerSession session) {
mCallbacks.notifySessionClosed(session.sessionId, session.userId);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5ef24f2..0616460 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -18,6 +18,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_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
@@ -58,6 +59,7 @@ import android.util.MathUtils;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -79,7 +81,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// TODO: enforce INSTALL_ALLOW_TEST
// TODO: enforce INSTALL_ALLOW_DOWNGRADE
- // TODO: handle INSTALL_EXTERNAL, INSTALL_INTERNAL
// TODO: treat INHERIT_EXISTING as installExistingPackage()
@@ -93,12 +94,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final String installerPackageName;
final SessionParams params;
final long createdMillis;
- final File sessionStageDir;
+
+ /** 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;
/** Note that UID is not persisted; it's always derived at runtime. */
final int installerUid;
- AtomicInteger openCount = new AtomicInteger();
+ private final AtomicInteger mOpenCount = new AtomicInteger();
private final Object mLock = new Object();
@@ -119,6 +132,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private int mFinalStatus;
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
@@ -165,7 +181,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 sessionStageDir, boolean sealed) {
+ File internalStageDir, String externalStageCid, boolean sealed) {
mCallback = callback;
mContext = context;
mPm = pm;
@@ -176,7 +192,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
this.installerPackageName = installerPackageName;
this.params = params;
this.createdMillis = createdMillis;
- this.sessionStageDir = sessionStageDir;
+ this.internalStageDir = internalStageDir;
+ this.externalStageCid = externalStageCid;
mSealed = sealed;
@@ -195,23 +212,29 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
public SessionInfo generateInfo() {
final SessionInfo info = new SessionInfo();
-
- info.sessionId = sessionId;
- info.installerPackageName = installerPackageName;
- info.resolvedBaseCodePath = mResolvedBaseCodePath;
- info.progress = mProgress;
- info.sealed = mSealed;
- info.open = openCount.get() > 0;
-
- info.mode = params.mode;
- info.sizeBytes = params.sizeBytes;
- info.appPackageName = params.appPackageName;
- info.appIcon = params.appIcon;
- info.appLabel = params.appLabel;
-
+ synchronized (mLock) {
+ info.sessionId = sessionId;
+ info.installerPackageName = installerPackageName;
+ info.resolvedBaseCodePath = mResolvedBaseCodePath;
+ info.progress = mProgress;
+ info.sealed = mSealed;
+ info.open = mOpenCount.get() > 0;
+
+ info.mode = params.mode;
+ info.sizeBytes = params.sizeBytes;
+ info.appPackageName = params.appPackageName;
+ info.appIcon = params.appIcon;
+ info.appLabel = params.appLabel;
+ }
return info;
}
+ public boolean isSealed() {
+ synchronized (mLock) {
+ return mSealed;
+ }
+ }
+
private void assertNotSealed(String cookie) {
synchronized (mLock) {
if (mSealed) {
@@ -220,6 +243,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ /**
+ * Resolve the actual location where staged data should be written. This
+ * 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 {
+ synchronized (mLock) {
+ if (mResolvedStageDir == null) {
+ if (internalStageDir != null) {
+ mResolvedStageDir = internalStageDir;
+ } else {
+ final String path = PackageHelper.getSdDir(externalStageCid);
+ if (path != null) {
+ mResolvedStageDir = new File(path);
+ } else {
+ throw new IOException(
+ "Failed to resolve container path for " + externalStageCid);
+ }
+ }
+ }
+ return mResolvedStageDir;
+ }
+ }
+
@Override
public void setClientProgress(float progress) {
synchronized (mLock) {
@@ -253,7 +300,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public String[] getNames() {
assertNotSealed("getNames");
- return sessionStageDir.list();
+ try {
+ return getStageDir().list();
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
}
@Override
@@ -267,8 +318,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
throws IOException {
- // TODO: relay over to DCS when installing to ASEC
-
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
@@ -285,7 +334,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
- final File target = new File(sessionStageDir, name);
+ final File target = new File(getStageDir(), name);
final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
O_CREAT | O_WRONLY, 0644);
@@ -331,7 +380,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
- final File target = new File(sessionStageDir, name);
+ final File target = new File(getStageDir(), name);
final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0);
return new ParcelFileDescriptor(targetFd);
@@ -369,10 +418,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// beyond this point we may have hardlinks to the valid install
}
+ final File stageDir;
+ try {
+ stageDir = getStageDir();
+ } catch (IOException e) {
+ throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
+ "Failed to resolve stage dir", e);
+ }
+
// Verify that stage looks sane with respect to existing application.
// This currently only ensures packageName, versionCode, and certificate
// consistency.
- validateInstallLocked();
+ validateInstallLocked(stageDir);
Preconditions.checkNotNull(mPackageName);
Preconditions.checkNotNull(mSignatures);
@@ -394,7 +451,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
- spliceExistingFilesIntoStage();
+ // TODO: implement splicing into existing ASEC
+ spliceExistingFilesIntoStage(stageDir);
}
// TODO: surface more granular state from dexopt
@@ -418,7 +476,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
};
- mPm.installStage(mPackageName, this.sessionStageDir, localObserver, params,
+ // TODO: send ASEC cid if that's where we staged things
+ mPm.installStage(mPackageName, this.internalStageDir, null, localObserver, params,
installerPackageName, installerUid, new UserHandle(userId));
}
@@ -428,13 +487,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* <p>
* Renames package files in stage to match split names defined inside.
*/
- private void validateInstallLocked() throws PackageManagerException {
+ private void validateInstallLocked(File stageDir) throws PackageManagerException {
mPackageName = null;
mVersionCode = -1;
mSignatures = null;
mResolvedBaseCodePath = null;
- final File[] files = sessionStageDir.listFiles();
+ final File[] files = stageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
@@ -480,7 +539,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Invalid filename: " + targetName);
}
- final File targetFile = new File(sessionStageDir, targetName);
+ final File targetFile = new File(stageDir, targetName);
if (!file.equals(targetFile)) {
file.renameTo(targetFile);
}
@@ -550,7 +609,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* Application is already installed; splice existing files that haven't been
* overridden into our stage.
*/
- private void spliceExistingFilesIntoStage() throws PackageManagerException {
+ private void spliceExistingFilesIntoStage(File stageDir) throws PackageManagerException {
final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
int n = 0;
@@ -559,7 +618,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
for (File oldFile : oldFiles) {
if (!PackageParser.isApkFile(oldFile)) continue;
- final File newFile = new File(sessionStageDir, oldFile.getName());
+ final File newFile = new File(stageDir, oldFile.getName());
try {
Os.link(oldFile.getAbsolutePath(), newFile.getAbsolutePath());
n++;
@@ -588,9 +647,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ public void open() {
+ if (mOpenCount.getAndIncrement() == 0) {
+ mCallback.onSessionOpened(this);
+ }
+ }
+
@Override
public void close() {
- if (openCount.decrementAndGet() == 0) {
+ if (mOpenCount.decrementAndGet() == 0) {
mCallback.onSessionClosed(this);
}
}
@@ -621,11 +686,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mSealed = true;
mDestroyed = true;
}
- FileUtils.deleteContents(sessionStageDir);
- sessionStageDir.delete();
+ if (internalStageDir != null) {
+ FileUtils.deleteContents(internalStageDir);
+ internalStageDir.delete();
+ }
+ if (externalStageCid != null) {
+ PackageHelper.destroySdDir(externalStageCid);
+ }
}
void dump(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ dumpLocked(pw);
+ }
+ }
+
+ private void dumpLocked(IndentingPrintWriter pw) {
pw.println("Session " + sessionId + ":");
pw.increaseIndent();
@@ -633,7 +709,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.printPair("installerPackageName", installerPackageName);
pw.printPair("installerUid", installerUid);
pw.printPair("createdMillis", createdMillis);
- pw.printPair("sessionStageDir", sessionStageDir);
+ pw.printPair("internalStageDir", internalStageDir);
+ pw.printPair("externalStageCid", externalStageCid);
pw.println();
params.dump(pw);
@@ -650,18 +727,4 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.decreaseIndent();
}
-
- Snapshot snapshot() {
- return new Snapshot(this);
- }
-
- static class Snapshot {
- final float clientProgress;
- final boolean sealed;
-
- public Snapshot(PackageInstallerSession session) {
- clientProgress = session.mClientProgress;
- sealed = session.mSealed;
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b62c304..51559aa 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -140,6 +140,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
+import android.os.storage.StorageManager;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
@@ -211,6 +212,7 @@ import dalvik.system.StaleDexCacheError;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
/**
* Keep track of all those .apks everywhere.
@@ -311,8 +313,6 @@ public class PackageManagerService extends IPackageManager.Stub {
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
- static final String mTempContainerPrefix = "smdl2tmp";
-
private static String sPreferredInstructionSet;
final ServiceThread mHandlerThread;
@@ -4101,21 +4101,24 @@ public class PackageManagerService extends IPackageManager.Stub {
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
- && !PackageInstallerService.isStageFile(file);
+ && !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
- // Ignore entries which are not apk's
+ // Ignore entries which are not packages
continue;
}
try {
- scanPackageLI(file, flags | PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
+ scanPackageLI(file, flags | PackageParser.PARSE_MUST_BE_APK,
+ scanMode, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
- // Don't mess around with apps in system partition.
+ // Delete invalid userdata apps
if ((flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
- // Delete the apk
- Slog.w(TAG, "Cleaning up failed install of " + file);
+ Slog.w(TAG, "Deleting invalid package at " + file);
+ if (file.isDirectory()) {
+ FileUtils.deleteContents(file);
+ }
file.delete();
}
}
@@ -7839,19 +7842,19 @@ public class PackageManagerService extends IPackageManager.Stub {
verificationParams.setInstallerUid(uid);
final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(originFile, false, observer, filteredFlags,
+ msg.obj = new InstallParams(originFile, null, false, observer, filteredFlags,
installerPackageName, verificationParams, user, packageAbiOverride);
mHandler.sendMessage(msg);
}
- void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer,
- PackageInstaller.SessionParams params, String installerPackageName, int installerUid,
- UserHandle user) {
+ void installStage(String packageName, File stagedDir, String stagedCid,
+ IPackageInstallObserver2 observer, PackageInstaller.SessionParams params,
+ String installerPackageName, int installerUid, UserHandle user) {
final VerificationParams verifParams = new VerificationParams(null, params.originatingUri,
params.referrerUri, installerUid, null);
final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(stageDir, true, observer, params.installFlags,
+ msg.obj = new InstallParams(stagedDir, stagedCid, true, observer, params.installFlags,
installerPackageName, verifParams, user, params.abiOverride);
mHandler.sendMessage(msg);
}
@@ -8551,10 +8554,12 @@ public class PackageManagerService extends IPackageManager.Stub {
* file, or a cluster directory. This location may be untrusted.
*/
final File originFile;
+ final String originCid;
/**
- * Flag indicating that {@link #originFile} has already been staged,
- * meaning downstream users don't need to defensively copy the contents.
+ * Flag indicating that {@link #originFile} or {@link #originCid} has
+ * already been staged, meaning downstream users don't need to
+ * defensively copy the contents.
*/
boolean originStaged;
@@ -8567,11 +8572,12 @@ public class PackageManagerService extends IPackageManager.Stub {
final String packageAbiOverride;
boolean multiArch;
- InstallParams(File originFile, boolean originStaged, IPackageInstallObserver2 observer,
- int flags, String installerPackageName, VerificationParams verificationParams,
- UserHandle user, String packageAbiOverride) {
+ InstallParams(File originFile, String originCid, boolean originStaged,
+ IPackageInstallObserver2 observer, int flags, String installerPackageName,
+ VerificationParams verificationParams, UserHandle user, String packageAbiOverride) {
super(user);
- this.originFile = Preconditions.checkNotNull(originFile);
+ this.originFile = originFile;
+ this.originCid = originCid;
this.originStaged = originStaged;
this.observer = observer;
this.flags = flags;
@@ -8582,9 +8588,8 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public String toString() {
- return "InstallParams{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + originFile + "}";
+ return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
+ + " file=" + originFile + " cid=" + originCid + "}";
}
public ManifestDigest getManifestDigest() {
@@ -8653,15 +8658,6 @@ public class PackageManagerService extends IPackageManager.Stub {
return pkgLite.recommendedInstallLocation;
}
- private long getMemoryLowThreshold() {
- final DeviceStorageMonitorInternal
- dsm = LocalServices.getService(DeviceStorageMonitorInternal.class);
- if (dsm == null) {
- return 0L;
- }
- return dsm.getMemoryLowThreshold();
- }
-
/*
* Invoke remote method to get package information and install
* location values. Override install location based on default
@@ -8679,14 +8675,9 @@ 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 {
- final long lowThreshold = getMemoryLowThreshold();
- if (lowThreshold == 0L) {
- Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
- }
-
// Remote call to find out default install location
final String originPath = originFile.getAbsolutePath();
- pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags, lowThreshold,
+ pkgLite = mContainerService.getMinimalPackageInfo(originPath, 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
@@ -8700,12 +8691,19 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- final long size = mContainerService.calculateInstalledSize(
+ // TODO: focus freeing disk space on the target device
+ final StorageManager storage = StorageManager.from(mContext);
+ final long lowThreshold = storage.getStorageLowBytes(
+ Environment.getDataDirectory());
+
+ final long sizeBytes = mContainerService.calculateInstalledSize(
originPath, isForwardLocked(), packageAbiOverride);
- if (mInstaller.freeCache(size + lowThreshold) >= 0) {
+
+ if (mInstaller.freeCache(sizeBytes + lowThreshold) >= 0) {
pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags,
- lowThreshold, packageAbiOverride);
+ packageAbiOverride);
}
+
/*
* The cache free must have deleted the file we
* downloaded to install.
@@ -9235,24 +9233,11 @@ public class PackageManagerService extends IPackageManager.Stub {
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
- final long lowThreshold;
-
- final DeviceStorageMonitorInternal
- dsm = LocalServices.getService(DeviceStorageMonitorInternal.class);
- if (dsm == null) {
- Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
- lowThreshold = 0L;
- } else {
- if (dsm.isMemoryLow()) {
- Log.w(TAG, "Memory is reported as being too low; aborting package install");
- return false;
- }
+ final long sizeBytes = imcs.calculateInstalledSize(originFile.getAbsolutePath(),
+ isFwdLocked(), abiOverride);
- lowThreshold = dsm.getMemoryLowThreshold();
- }
-
- return imcs.checkInternalFreeStorage(originFile.getAbsolutePath(), isFwdLocked(),
- lowThreshold);
+ final StorageManager storage = StorageManager.from(mContext);
+ return (sizeBytes <= storage.getStorageBytesUntilLow(Environment.getDataDirectory()));
}
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
@@ -9264,7 +9249,7 @@ public class PackageManagerService extends IPackageManager.Stub {
resourceFile = originFile;
} else {
try {
- final File tempDir = mInstallerService.allocateSessionDir();
+ final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
@@ -9569,12 +9554,22 @@ public class PackageManagerService extends IPackageManager.Stub {
}
void createCopyFile() {
- cid = getTempContainerId();
+ cid = mInstallerService.allocateExternalStageCidLegacy();
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
- return imcs.checkExternalFreeStorage(originFile.getAbsolutePath(), isFwdLocked(),
+ final long sizeBytes = imcs.calculateInstalledSize(packagePath, isFwdLocked(),
abiOverride);
+
+ final File target;
+ if (isExternal()) {
+ target = Environment.getExternalStorageDirectory();
+ } else {
+ target = Environment.getDataDirectory();
+ }
+
+ final StorageManager storage = StorageManager.from(mContext);
+ return (sizeBytes <= storage.getStorageBytesUntilLow(target));
}
private final boolean isExternal() {
@@ -12653,7 +12648,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private boolean mMediaMounted = false;
- private String getEncryptKey() {
+ static String getEncryptKey() {
try {
String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(
SD_ENCRYPTION_KEYSTORE_NAME);
@@ -12673,30 +12668,6 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.e(TAG, "Failed to retrieve encryption keys with exception: " + ioe);
return null;
}
-
- }
-
- /* package */static String getTempContainerId() {
- int tmpIdx = 1;
- String list[] = PackageHelper.getSecureContainerList();
- if (list != null) {
- for (final String name : list) {
- // Ignore null and non-temporary container entries
- if (name == null || !name.startsWith(mTempContainerPrefix)) {
- continue;
- }
-
- String subStr = name.substring(mTempContainerPrefix.length());
- try {
- int cid = Integer.parseInt(subStr);
- if (cid >= tmpIdx) {
- tmpIdx = cid + 1;
- }
- } catch (NumberFormatException e) {
- }
- }
- }
- return mTempContainerPrefix + tmpIdx;
}
/*
@@ -12754,31 +12725,27 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
private void updateExternalMediaStatusInner(boolean isMounted, boolean reportStatus,
boolean externalStorage) {
- // Collection of uids
- int uidArr[] = null;
- // Collection of stale containers
- HashSet<String> removeCids = new HashSet<String>();
- // Collection of packages on external media with valid containers.
- HashMap<AsecInstallArgs, String> processCids = new HashMap<AsecInstallArgs, String>();
- // Get list of secure containers.
- final String list[] = PackageHelper.getSecureContainerList();
- if (list == null || list.length == 0) {
- Log.i(TAG, "No secure containers on sdcard");
+ ArrayMap<AsecInstallArgs, String> processCids = new ArrayMap<>();
+ int[] uidArr = EmptyArray.INT;
+
+ final String[] list = PackageHelper.getSecureContainerList();
+ if (ArrayUtils.isEmpty(list)) {
+ Log.i(TAG, "No secure containers found");
} else {
// Process list of secure containers and categorize them
// as active or stale based on their package internal state.
- int uidList[] = new int[list.length];
- int num = 0;
+
// reader
synchronized (mPackages) {
for (String cid : list) {
+ // Leave stages untouched for now; installer service owns them
+ if (PackageInstallerService.isStageName(cid)) continue;
+
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Processing container " + cid);
String pkgName = getAsecPackageName(cid);
if (pkgName == null) {
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Container : " + cid + " stale");
- removeCids.add(cid);
+ Slog.i(TAG, "Found stale container " + cid + " with no package name");
continue;
}
if (DEBUG_SD_INSTALL)
@@ -12786,8 +12753,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps == null) {
- Log.i(TAG, "Deleting container with no matching settings " + cid);
- removeCids.add(cid);
+ Slog.i(TAG, "Found stale container " + cid + " with no matching settings");
continue;
}
@@ -12813,35 +12779,25 @@ public class PackageManagerService extends IPackageManager.Stub {
processCids.put(args, ps.codePathString);
final int uid = ps.appId;
if (uid != -1) {
- uidList[num++] = uid;
+ uidArr = ArrayUtils.appendInt(uidArr, uid);
}
} else {
- Log.i(TAG, "Deleting stale container for " + cid);
- removeCids.add(cid);
+ Slog.i(TAG, "Found stale container " + cid + ": expected codePath="
+ + ps.codePathString);
}
}
}
- if (num > 0) {
- // Sort uid list
- Arrays.sort(uidList, 0, num);
- // Throw away duplicates
- uidArr = new int[num];
- uidArr[0] = uidList[0];
- int di = 0;
- for (int i = 1; i < num; i++) {
- if (uidList[i - 1] != uidList[i]) {
- uidArr[di++] = uidList[i];
- }
- }
- }
+ Arrays.sort(uidArr);
}
+
// Process packages with valid entries.
if (isMounted) {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Loading packages");
- loadMediaPackages(processCids, uidArr, removeCids);
+ loadMediaPackages(processCids, uidArr);
startCleaningPackages();
+ mInstallerService.onSecureContainersAvailable();
} else {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Unloading packages");
@@ -12849,8 +12805,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
- ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
+ private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
int size = pkgList.size();
if (size > 0) {
// Send broadcasts here
@@ -12875,11 +12831,10 @@ public class PackageManagerService extends IPackageManager.Stub {
* the cid is added to list of removeCids. We currently don't delete stale
* containers.
*/
- private void loadMediaPackages(HashMap<AsecInstallArgs, String> processCids, int uidArr[],
- HashSet<String> removeCids) {
+ private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr) {
ArrayList<String> pkgList = new ArrayList<String>();
Set<AsecInstallArgs> keys = processCids.keySet();
- boolean doGc = false;
+
for (AsecInstallArgs args : keys) {
String codePath = processCids.get(args);
if (DEBUG_SD_INSTALL)
@@ -12907,7 +12862,6 @@ public class PackageManagerService extends IPackageManager.Stub {
parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
}
- doGc = true;
synchronized (mInstallLock) {
PackageParser.Package pkg = null;
try {
@@ -12937,9 +12891,7 @@ public class PackageManagerService extends IPackageManager.Stub {
} finally {
if (retCode != PackageManager.INSTALL_SUCCEEDED) {
- // Don't destroy container here. Wait till gc clears things
- // up.
- removeCids.add(args.cid);
+ Log.w(TAG, "Container " + args.cid + " is stale, retCode=" + retCode);
}
}
}
@@ -12974,21 +12926,6 @@ public class PackageManagerService extends IPackageManager.Stub {
if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(true, false, pkgList, uidArr, null);
}
- // Force gc to avoid any stale parser references that we might have.
- if (doGc) {
- Runtime.getRuntime().gc();
- }
- // List stale containers and destroy stale temporary containers.
- if (removeCids != null) {
- for (String cid : removeCids) {
- if (cid.startsWith(mTempContainerPrefix)) {
- Log.i(TAG, "Destroying stale temporary container " + cid);
- PackageHelper.destroySdDir(cid);
- } else {
- Log.w(TAG, "Container " + cid + " is stale");
- }
- }
- }
}
/*
@@ -13012,7 +12949,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* that we always have to post this message if status has been requested no
* matter what.
*/
- private void unloadMediaPackages(HashMap<AsecInstallArgs, String> processCids, int uidArr[],
+ private void unloadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int uidArr[],
final boolean reportStatus) {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "unloading media packages");