Merge "Use allocatable space when measuring for install."
diff --git a/api/system-current.txt b/api/system-current.txt
index 0f42a54..5e6717c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -11086,6 +11086,7 @@
public static class PackageInstaller.SessionParams implements android.os.Parcelable {
ctor public PackageInstaller.SessionParams(int);
method public int describeContents();
+ method public void setAllocateAggressive(boolean);
method public void setAllowDowngrade(boolean);
method public void setAppIcon(android.graphics.Bitmap);
method public void setAppLabel(java.lang.CharSequence);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 320c733..9e4a86d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1153,6 +1153,16 @@
}
/** {@hide} */
+ @SystemApi
+ public void setAllocateAggressive(boolean allocateAggressive) {
+ if (allocateAggressive) {
+ installFlags |= PackageManager.INSTALL_ALLOCATE_AGGRESSIVE;
+ } else {
+ installFlags &= ~PackageManager.INSTALL_ALLOCATE_AGGRESSIVE;
+ }
+ }
+
+ /** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
pw.printHexPair("installFlags", installFlags);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 10ffab2..6dd1833 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -51,6 +51,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.util.AndroidException;
import android.util.Log;
@@ -610,6 +611,9 @@
INSTALL_FORCE_PERMISSION_PROMPT,
INSTALL_INSTANT_APP,
INSTALL_DONT_KILL_APP,
+ INSTALL_FORCE_SDK,
+ INSTALL_FULL_APP,
+ INSTALL_ALLOCATE_AGGRESSIVE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface InstallFlags {}
@@ -717,15 +721,6 @@
public static final int INSTALL_INSTANT_APP = 0x00000800;
/**
- * Flag parameter for {@link #installPackage} to indicate that this package is
- * to be installed as a heavy weight app. This is fundamentally the opposite of
- * {@link #INSTALL_INSTANT_APP}.
- *
- * @hide
- */
- public static final int INSTALL_FULL_APP = 0x00004000;
-
- /**
* Flag parameter for {@link #installPackage} to indicate that this package contains
* a feature split to an existing application and the existing application should not
* be killed during the installation process.
@@ -743,6 +738,24 @@
public static final int INSTALL_FORCE_SDK = 0x00002000;
/**
+ * Flag parameter for {@link #installPackage} to indicate that this package is
+ * to be installed as a heavy weight app. This is fundamentally the opposite of
+ * {@link #INSTALL_INSTANT_APP}.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FULL_APP = 0x00004000;
+
+ /**
+ * Flag parameter for {@link #installPackage} to indicate that this package
+ * is critical to system health or security, meaning the system should use
+ * {@link StorageManager#FLAG_ALLOCATE_AGGRESSIVE} internally.
+ *
+ * @hide
+ */
+ public static final int INSTALL_ALLOCATE_AGGRESSIVE = 0x00008000;
+
+ /**
* Flag parameter for
* {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
* that you don't want to kill the app containing the component. Be careful when you set this
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index e088717..9c361b3 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -20,6 +20,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser.PackageLite;
import android.os.Environment;
@@ -354,10 +355,12 @@
abstract public ApplicationInfo getExistingAppInfo(Context context, String packageName);
abstract public File getDataDirectory();
- public boolean fitsOnInternalStorage(Context context, long sizeBytes) {
+ public boolean fitsOnInternalStorage(Context context, SessionParams params)
+ throws IOException {
StorageManager storage = getStorageManager(context);
File target = getDataDirectory();
- return (sizeBytes <= storage.getStorageBytesUntilLow(target));
+ return (params.sizeBytes <= storage.getAllocatableBytes(target,
+ translateAllocateFlags(params.installFlags)));
}
}
@@ -401,6 +404,18 @@
return sDefaultTestableInterface;
}
+ @VisibleForTesting
+ @Deprecated
+ public static String resolveInstallVolume(Context context, String packageName,
+ int installLocation, long sizeBytes, TestableInterface testInterface)
+ throws IOException {
+ final SessionParams params = new SessionParams(SessionParams.MODE_INVALID);
+ params.appPackageName = packageName;
+ params.installLocation = installLocation;
+ params.sizeBytes = sizeBytes;
+ return resolveInstallVolume(context, params, testInterface);
+ }
+
/**
* Given a requested {@link PackageInfo#installLocation} and calculated
* install size, pick the actual volume to install the app. Only considers
@@ -410,25 +425,25 @@
* @return the {@link VolumeInfo#fsUuid} to install onto, or {@code null}
* for internal storage.
*/
- public static String resolveInstallVolume(Context context, String packageName,
- int installLocation, long sizeBytes) throws IOException {
+ public static String resolveInstallVolume(Context context, SessionParams params)
+ throws IOException {
TestableInterface testableInterface = getDefaultTestableInterface();
- return resolveInstallVolume(context, packageName,
- installLocation, sizeBytes, testableInterface);
+ return resolveInstallVolume(context, params.appPackageName, params.installLocation,
+ params.sizeBytes, testableInterface);
}
@VisibleForTesting
- public static String resolveInstallVolume(Context context, String packageName,
- int installLocation, long sizeBytes, TestableInterface testInterface)
- throws IOException {
+ public static String resolveInstallVolume(Context context, SessionParams params,
+ TestableInterface testInterface) throws IOException {
final boolean forceAllowOnExternal = testInterface.getForceAllowOnExternalSetting(context);
final boolean allow3rdPartyOnInternal =
testInterface.getAllow3rdPartyOnInternalConfig(context);
// TODO: handle existing apps installed in ASEC; currently assumes
// they'll end up back on internal storage
- ApplicationInfo existingInfo = testInterface.getExistingAppInfo(context, packageName);
+ ApplicationInfo existingInfo = testInterface.getExistingAppInfo(context,
+ params.appPackageName);
- final boolean fitsOnInternal = testInterface.fitsOnInternalStorage(context, sizeBytes);
+ final boolean fitsOnInternal = testInterface.fitsOnInternalStorage(context, params);
final StorageManager storageManager =
testInterface.getStorageManager(context);
@@ -438,7 +453,8 @@
return StorageManager.UUID_PRIVATE_INTERNAL;
} else {
throw new IOException("Not enough space on existing volume "
- + existingInfo.volumeUuid + " for system app " + packageName + " upgrade");
+ + existingInfo.volumeUuid + " for system app " + params.appPackageName
+ + " upgrade");
}
}
@@ -450,8 +466,9 @@
boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id);
if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()
&& (!isInternalStorage || allow3rdPartyOnInternal)) {
- final long availBytes = storageManager.getStorageBytesUntilLow(new File(vol.path));
- if (availBytes >= sizeBytes) {
+ final long availBytes = storageManager.getAllocatableBytes(new File(vol.path),
+ translateAllocateFlags(params.installFlags));
+ if (availBytes >= params.sizeBytes) {
allCandidates.add(vol.fsUuid);
}
if (availBytes >= bestCandidateAvailBytes) {
@@ -463,11 +480,11 @@
// If app expresses strong desire for internal storage, honor it
if (!forceAllowOnExternal
- && installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ && params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
if (existingInfo != null && !Objects.equals(existingInfo.volumeUuid,
StorageManager.UUID_PRIVATE_INTERNAL)) {
- throw new IOException("Cannot automatically move " + packageName + " from "
- + existingInfo.volumeUuid + " to internal storage");
+ throw new IOException("Cannot automatically move " + params.appPackageName
+ + " from " + existingInfo.volumeUuid + " to internal storage");
}
if (!allow3rdPartyOnInternal) {
@@ -490,7 +507,7 @@
return existingInfo.volumeUuid;
} else {
throw new IOException("Not enough space on existing volume "
- + existingInfo.volumeUuid + " for " + packageName + " upgrade");
+ + existingInfo.volumeUuid + " for " + params.appPackageName + " upgrade");
}
}
@@ -504,29 +521,45 @@
}
}
- public static boolean fitsOnInternal(Context context, long sizeBytes) {
+ public static boolean fitsOnInternal(Context context, SessionParams params) throws IOException {
final StorageManager storage = context.getSystemService(StorageManager.class);
final File target = Environment.getDataDirectory();
- return (sizeBytes <= storage.getStorageBytesUntilLow(target));
+ return (params.sizeBytes <= storage.getAllocatableBytes(target,
+ translateAllocateFlags(params.installFlags)));
}
- public static boolean fitsOnExternal(Context context, long sizeBytes) {
+ public static boolean fitsOnExternal(Context context, SessionParams params) {
final StorageManager storage = context.getSystemService(StorageManager.class);
final StorageVolume primary = storage.getPrimaryVolume();
- return (sizeBytes > 0) && !primary.isEmulated()
+ return (params.sizeBytes > 0) && !primary.isEmulated()
&& Environment.MEDIA_MOUNTED.equals(primary.getState())
- && sizeBytes <= storage.getStorageBytesUntilLow(primary.getPathFile());
+ && params.sizeBytes <= storage.getStorageBytesUntilLow(primary.getPathFile());
+ }
+
+ @Deprecated
+ public static int resolveInstallLocation(Context context, String packageName,
+ int installLocation, long sizeBytes, int installFlags) {
+ final SessionParams params = new SessionParams(SessionParams.MODE_INVALID);
+ params.appPackageName = packageName;
+ params.installLocation = installLocation;
+ params.sizeBytes = sizeBytes;
+ params.installFlags = installFlags;
+ try {
+ return resolveInstallLocation(context, params);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
}
/**
* Given a requested {@link PackageInfo#installLocation} and calculated
* install size, pick the actual location to install the app.
*/
- public static int resolveInstallLocation(Context context, String packageName,
- int installLocation, long sizeBytes, int installFlags) {
+ public static int resolveInstallLocation(Context context, SessionParams params)
+ throws IOException {
ApplicationInfo existingInfo = null;
try {
- existingInfo = context.getPackageManager().getApplicationInfo(packageName,
+ existingInfo = context.getPackageManager().getApplicationInfo(params.appPackageName,
PackageManager.MATCH_ANY_USER);
} catch (NameNotFoundException ignored) {
}
@@ -534,23 +567,23 @@
final int prefer;
final boolean checkBoth;
boolean ephemeral = false;
- if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
+ if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
prefer = RECOMMEND_INSTALL_INTERNAL;
ephemeral = true;
checkBoth = false;
- } else if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
+ } else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
prefer = RECOMMEND_INSTALL_INTERNAL;
checkBoth = false;
- } else if ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
prefer = RECOMMEND_INSTALL_EXTERNAL;
checkBoth = false;
- } else if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
prefer = RECOMMEND_INSTALL_INTERNAL;
checkBoth = false;
- } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+ } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
prefer = RECOMMEND_INSTALL_EXTERNAL;
checkBoth = true;
- } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
+ } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
// When app is already installed, prefer same medium
if (existingInfo != null) {
// TODO: distinguish if this is external ASEC
@@ -570,12 +603,12 @@
boolean fitsOnInternal = false;
if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {
- fitsOnInternal = fitsOnInternal(context, sizeBytes);
+ fitsOnInternal = fitsOnInternal(context, params);
}
boolean fitsOnExternal = false;
if (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL) {
- fitsOnExternal = fitsOnExternal(context, sizeBytes);
+ fitsOnExternal = fitsOnExternal(context, params);
}
if (prefer == RECOMMEND_INSTALL_INTERNAL) {
@@ -641,4 +674,12 @@
}
return str.substring(0, str.length() - before.length()) + after;
}
+
+ public static int translateAllocateFlags(int installFlags) {
+ if ((installFlags & PackageManager.INSTALL_ALLOCATE_AGGRESSIVE) != 0) {
+ return StorageManager.FLAG_ALLOCATE_AGGRESSIVE;
+ } else {
+ return 0;
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
index c4d00c6..5c497b4 100644
--- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
@@ -58,9 +58,9 @@
private static final long sAdoptedSize = 10000;
private static final long sPublicSize = 1000000;
- private static final StorageManager sStorageManager = createStorageManagerMock();
+ private static StorageManager sStorageManager;
- private static StorageManager createStorageManagerMock() {
+ private static StorageManager createStorageManagerMock() throws Exception {
VolumeInfo internalVol = new VolumeInfo("private",
VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/);
internalVol.path = sInternalVolPath;
@@ -93,6 +93,12 @@
Mockito.when(storageManager.getStorageBytesUntilLow(internalFile)).thenReturn(sInternalSize);
Mockito.when(storageManager.getStorageBytesUntilLow(adoptedFile)).thenReturn(sAdoptedSize);
Mockito.when(storageManager.getStorageBytesUntilLow(publicFile)).thenReturn(sPublicSize);
+ Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalFile), Mockito.anyInt()))
+ .thenReturn(sInternalSize);
+ Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(adoptedFile), Mockito.anyInt()))
+ .thenReturn(sAdoptedSize);
+ Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(publicFile), Mockito.anyInt()))
+ .thenReturn(sPublicSize);
return storageManager;
}
@@ -156,18 +162,10 @@
}
}
- void failStr(String errMsg) {
- Log.w(TAG, "errMsg=" + errMsg);
- fail(errMsg);
- }
-
- void failStr(Exception e) {
- failStr(e.getMessage());
- }
-
@Override
protected void setUp() throws Exception {
super.setUp();
+ sStorageManager = createStorageManagerMock();
if (localLOGV) Log.i(TAG, "Cleaning out old test containers");
cleanupContainers();
}
@@ -175,28 +173,25 @@
@Override
protected void tearDown() throws Exception {
super.tearDown();
+ sStorageManager = null;
if (localLOGV) Log.i(TAG, "Cleaning out old test containers");
cleanupContainers();
}
- public void testMountAndPullSdCard() {
- try {
- fullId = PREFIX;
- fullId2 = PackageHelper.createSdDir(1024 * MB_IN_BYTES, fullId, "none",
- android.os.Process.myUid(), true);
+ public void testMountAndPullSdCard() throws Exception {
+ fullId = PREFIX;
+ fullId2 = PackageHelper.createSdDir(1024 * MB_IN_BYTES, fullId, "none",
+ android.os.Process.myUid(), true);
- Log.d(TAG,PackageHelper.getSdDir(fullId));
- PackageHelper.unMountSdDir(fullId);
+ Log.d(TAG, "getSdDir=" + PackageHelper.getSdDir(fullId));
+ PackageHelper.unMountSdDir(fullId);
- Runnable r1 = getMountRunnable();
- Runnable r2 = getDestroyRunnable();
- Thread thread = new Thread(r1);
- Thread thread2 = new Thread(r2);
- thread2.start();
- thread.start();
- } catch (Exception e) {
- failStr(e);
- }
+ Runnable r1 = getMountRunnable();
+ Runnable r2 = getDestroyRunnable();
+ Thread thread = new Thread(r1);
+ Thread thread2 = new Thread(r2);
+ thread2.start();
+ thread.start();
}
public Runnable getMountRunnable() {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 37f78b4..a317ca5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -637,12 +637,12 @@
// If caller requested explicit location, sanity check it, otherwise
// resolve the best internal or adopted location.
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
- if (!PackageHelper.fitsOnInternal(mContext, params.sizeBytes)) {
+ if (!PackageHelper.fitsOnInternal(mContext, params)) {
throw new IOException("No suitable internal storage available");
}
} else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- if (!PackageHelper.fitsOnExternal(mContext, params.sizeBytes)) {
+ if (!PackageHelper.fitsOnExternal(mContext, params)) {
throw new IOException("No suitable external storage available");
}
@@ -660,8 +660,7 @@
// requested install flags, delta size, and manifest settings.
final long ident = Binder.clearCallingIdentity();
try {
- params.volumeUuid = PackageHelper.resolveInstallVolume(mContext,
- params.appPackageName, params.installLocation, params.sizeBytes);
+ params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 2e4a3a3..1129076 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -472,8 +472,8 @@
// If caller specified a total length, allocate it for them. Free up
// cache space to grow, if needed.
if (stageDir != null && lengthBytes > 0) {
- mContext.getSystemService(StorageManager.class).allocateBytes(targetFd,
- lengthBytes, 0);
+ mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes,
+ PackageHelper.translateAllocateFlags(params.installFlags));
}
if (offsetBytes > 0) {