summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/ApplicationPackageManager.java18
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java5
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl3
-rw-r--r--core/java/android/content/pm/PackageInstaller.java5
-rw-r--r--core/java/android/content/pm/PackageManager.java18
-rw-r--r--core/java/android/content/pm/PackageParser.java4
-rw-r--r--core/java/android/os/storage/StorageManager.java40
-rw-r--r--core/java/android/os/storage/VolumeInfo.java1
-rw-r--r--core/java/com/android/internal/content/PackageHelper.java125
9 files changed, 178 insertions, 41 deletions
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6ec5457..10dcd85 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -62,19 +62,19 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
import android.util.Log;
import android.view.Display;
-import android.os.SystemProperties;
+
+import dalvik.system.VMRuntime;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.internal.util.UserIcons;
-import dalvik.system.VMRuntime;
-
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -1363,7 +1363,17 @@ final class ApplicationPackageManager extends PackageManager {
try {
mPM.movePackage(packageName, observer, flags);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void movePackageAndData(String packageName, String volumeUuid,
+ IPackageMoveObserver observer) {
+ try {
+ mPM.movePackageAndData(packageName, volumeUuid, observer);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 8567c01..5bdb7bb 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -459,6 +459,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public int largestWidthLimitDp = 0;
/** {@hide} */
+ public String volumeUuid;
+ /** {@hide} */
public String scanSourceDir;
/** {@hide} */
public String scanPublicSourceDir;
@@ -726,6 +728,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
requiresSmallestWidthDp = orig.requiresSmallestWidthDp;
compatibleWidthLimitDp = orig.compatibleWidthLimitDp;
largestWidthLimitDp = orig.largestWidthLimitDp;
+ volumeUuid = orig.volumeUuid;
scanSourceDir = orig.scanSourceDir;
scanPublicSourceDir = orig.scanPublicSourceDir;
sourceDir = orig.sourceDir;
@@ -778,6 +781,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(requiresSmallestWidthDp);
dest.writeInt(compatibleWidthLimitDp);
dest.writeInt(largestWidthLimitDp);
+ dest.writeString(volumeUuid);
dest.writeString(scanSourceDir);
dest.writeString(scanPublicSourceDir);
dest.writeString(sourceDir);
@@ -829,6 +833,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
requiresSmallestWidthDp = source.readInt();
compatibleWidthLimitDp = source.readInt();
largestWidthLimitDp = source.readInt();
+ volumeUuid = source.readString();
scanSourceDir = source.readString();
scanPublicSourceDir = source.readString();
sourceDir = source.readString();
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 649bb47..eed0df5 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -432,7 +432,8 @@ interface IPackageManager {
PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);
void movePackage(String packageName, IPackageMoveObserver observer, int flags);
-
+ void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer);
+
boolean addPermissionAsync(in PermissionInfo info);
boolean setInstallLocation(int loc);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 80efd0b..15a7bf9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -887,6 +887,8 @@ public class PackageInstaller {
public Uri referrerUri;
/** {@hide} */
public String abiOverride;
+ /** {@hide} */
+ public String volumeUuid;
/**
* Construct parameters for a new package install session.
@@ -911,6 +913,7 @@ public class PackageInstaller {
originatingUri = source.readParcelable(null);
referrerUri = source.readParcelable(null);
abiOverride = source.readString();
+ volumeUuid = source.readString();
}
/**
@@ -1008,6 +1011,7 @@ public class PackageInstaller {
pw.printPair("originatingUri", originatingUri);
pw.printPair("referrerUri", referrerUri);
pw.printPair("abiOverride", abiOverride);
+ pw.printPair("volumeUuid", volumeUuid);
pw.println();
}
@@ -1028,6 +1032,7 @@ public class PackageInstaller {
dest.writeParcelable(originatingUri, flags);
dest.writeParcelable(referrerUri, flags);
dest.writeString(abiOverride);
+ dest.writeString(volumeUuid);
}
public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 46d6ffb3..4c99d09 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -43,7 +43,9 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.storage.VolumeInfo;
import android.util.AndroidException;
+
import com.android.internal.util.ArrayUtils;
import java.io.File;
@@ -339,15 +341,17 @@ public abstract class PackageManager {
public static final int INSTALL_ALLOW_TEST = 0x00000004;
/**
- * Flag parameter for {@link #installPackage} to indicate that this
- * package has to be installed on the sdcard.
+ * Flag parameter for {@link #installPackage} to indicate that this package
+ * must be installed to an ASEC on a {@link VolumeInfo#TYPE_PUBLIC}.
+ *
* @hide
*/
public static final int INSTALL_EXTERNAL = 0x00000008;
/**
* Flag parameter for {@link #installPackage} to indicate that this package
- * has to be installed on the sdcard.
+ * must be installed to internal storage.
+ *
* @hide
*/
public static final int INSTALL_INTERNAL = 0x00000010;
@@ -4099,8 +4103,12 @@ public abstract class PackageManager {
*
* @hide
*/
- public abstract void movePackage(
- String packageName, IPackageMoveObserver observer, int flags);
+ @Deprecated
+ public abstract void movePackage(String packageName, IPackageMoveObserver observer, int flags);
+
+ /** {@hide} */
+ public abstract void movePackageAndData(String packageName, String volumeUuid,
+ IPackageMoveObserver observer);
/**
* Returns the device identity that verifiers can use to associate their scheme to a particular
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f4ad434..c1e6a4d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -604,7 +604,7 @@ public class PackageParser {
public final static int PARSE_MUST_BE_APK = 1<<2;
public final static int PARSE_IGNORE_PROCESSES = 1<<3;
public final static int PARSE_FORWARD_LOCK = 1<<4;
- public final static int PARSE_ON_SDCARD = 1<<5;
+ public final static int PARSE_EXTERNAL_STORAGE = 1<<5;
public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
public final static int PARSE_IS_PRIVILEGED = 1<<7;
public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
@@ -1408,7 +1408,7 @@ public class PackageParser {
}
/* Set the global "on SD card" flag */
- if ((flags & PARSE_ON_SDCARD) != 0) {
+ if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 486f9b4..5a5d8b7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -43,6 +43,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -231,8 +232,7 @@ public class StorageManager {
mLooper = looper;
mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
if (mMountService == null) {
- Log.e(TAG, "Unable to connect to mount service! - is it running yet?");
- return;
+ throw new IllegalStateException("Failed to find running mount service");
}
}
@@ -442,6 +442,42 @@ public class StorageManager {
}
/** {@hide} */
+ public @Nullable DiskInfo findDiskById(String id) {
+ Preconditions.checkNotNull(id);
+ // TODO; go directly to service to make this faster
+ for (DiskInfo disk : getDisks()) {
+ if (Objects.equals(disk.id, id)) {
+ return disk;
+ }
+ }
+ return null;
+ }
+
+ /** {@hide} */
+ public @Nullable VolumeInfo findVolumeById(String id) {
+ Preconditions.checkNotNull(id);
+ // TODO; go directly to service to make this faster
+ for (VolumeInfo vol : getVolumes()) {
+ if (Objects.equals(vol.id, id)) {
+ return vol;
+ }
+ }
+ return null;
+ }
+
+ /** {@hide} */
+ public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
+ Preconditions.checkNotNull(fsUuid);
+ // TODO; go directly to service to make this faster
+ for (VolumeInfo vol : getVolumes()) {
+ if (Objects.equals(vol.fsUuid, fsUuid)) {
+ return vol;
+ }
+ }
+ return null;
+ }
+
+ /** {@hide} */
public @NonNull List<VolumeInfo> getVolumes() {
try {
return Arrays.asList(mMountService.getVolumes());
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index c8c2f65..2dc0361 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -44,6 +44,7 @@ import java.io.File;
* @hide
*/
public class VolumeInfo implements Parcelable {
+ /** Real volume representing internal emulated storage */
public static final String ID_EMULATED_INTERNAL = "emulated";
public static final int TYPE_PUBLIC = 0;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 7bdb4be..255f1fd 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -25,15 +25,16 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser.PackageLite;
import android.os.Environment;
-import android.os.Environment.UserEnvironment;
import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
import android.os.storage.StorageResultCode;
+import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
+import android.util.ArraySet;
import android.util.Log;
import libcore.io.IoUtils;
@@ -335,6 +336,94 @@ public class PackageHelper {
/**
* Given a requested {@link PackageInfo#installLocation} and calculated
+ * install size, pick the actual volume to install the app. Only considers
+ * internal and private volumes, and prefers to keep an existing package on
+ * its current volume.
+ *
+ * @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 {
+ // TODO: handle existing apps installed in ASEC; currently assumes
+ // they'll end up back on internal storage
+ ApplicationInfo existingInfo = null;
+ try {
+ existingInfo = context.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (NameNotFoundException ignored) {
+ }
+
+ final StorageManager storageManager = context.getSystemService(StorageManager.class);
+ final boolean fitsOnInternal = fitsOnInternal(context, sizeBytes);
+
+ final ArraySet<String> allCandidates = new ArraySet<>();
+ VolumeInfo bestCandidate = null;
+ long bestCandidateAvailBytes = Long.MIN_VALUE;
+ for (VolumeInfo vol : storageManager.getVolumes()) {
+ if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.state == VolumeInfo.STATE_MOUNTED) {
+ final long availBytes = storageManager.getStorageBytesUntilLow(new File(vol.path));
+ if (availBytes >= sizeBytes) {
+ allCandidates.add(vol.fsUuid);
+ }
+ if (availBytes >= bestCandidateAvailBytes) {
+ bestCandidate = vol;
+ bestCandidateAvailBytes = availBytes;
+ }
+ }
+ }
+
+ // System apps always forced to internal storage
+ if (existingInfo != null && existingInfo.isSystemApp()) {
+ installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
+ }
+
+ // If app expresses strong desire for internal space, honor it
+ if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ if (fitsOnInternal) {
+ return null;
+ } else {
+ throw new IOException("Requested internal only, but not enough space");
+ }
+ }
+
+ // If app already exists somewhere, prefer to stay on that volume
+ if (existingInfo != null) {
+ if (existingInfo.volumeUuid == null && fitsOnInternal) {
+ return null;
+ }
+ if (allCandidates.contains(existingInfo.volumeUuid)) {
+ return existingInfo.volumeUuid;
+ }
+ }
+
+ // We're left with either preferring external or auto, so just pick
+ // volume with most space
+ if (bestCandidate != null) {
+ return bestCandidate.fsUuid;
+ } else if (fitsOnInternal) {
+ return null;
+ } else {
+ throw new IOException("No special requests, but no room anywhere");
+ }
+ }
+
+ public static boolean fitsOnInternal(Context context, long sizeBytes) {
+ final StorageManager storage = context.getSystemService(StorageManager.class);
+ final File target = Environment.getDataDirectory();
+ return (sizeBytes <= storage.getStorageBytesUntilLow(target));
+ }
+
+ public static boolean fitsOnExternal(Context context, long sizeBytes) {
+ final StorageManager storage = context.getSystemService(StorageManager.class);
+ final StorageVolume primary = storage.getPrimaryVolume();
+ return (sizeBytes > 0) && !primary.isEmulated()
+ && Environment.MEDIA_MOUNTED.equals(primary.getState())
+ && sizeBytes <= storage.getStorageBytesUntilLow(primary.getPathFile());
+ }
+
+ /**
+ * 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,
@@ -363,6 +452,7 @@ public class PackageHelper {
} else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
// When app is already installed, prefer same medium
if (existingInfo != null) {
+ // TODO: distinguish if this is external ASEC
if ((existingInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
prefer = RECOMMEND_INSTALL_EXTERNAL;
} else {
@@ -377,30 +467,21 @@ public class PackageHelper {
checkBoth = false;
}
- final boolean emulated = Environment.isExternalStorageEmulated();
- final StorageManager storage = StorageManager.from(context);
-
boolean fitsOnInternal = false;
if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {
- final File target = Environment.getDataDirectory();
- fitsOnInternal = (sizeBytes <= storage.getStorageBytesUntilLow(target));
+ fitsOnInternal = fitsOnInternal(context, sizeBytes);
}
boolean fitsOnExternal = false;
- if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)) {
- final File target = new UserEnvironment(UserHandle.USER_OWNER)
- .getExternalStorageDirectory();
- // External is only an option when size is known
- if (sizeBytes > 0) {
- fitsOnExternal = (sizeBytes <= storage.getStorageBytesUntilLow(target));
- }
+ if (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL) {
+ fitsOnExternal = fitsOnExternal(context, sizeBytes);
}
if (prefer == RECOMMEND_INSTALL_INTERNAL) {
if (fitsOnInternal) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
}
- } else if (!emulated && prefer == RECOMMEND_INSTALL_EXTERNAL) {
+ } else if (prefer == RECOMMEND_INSTALL_EXTERNAL) {
if (fitsOnExternal) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
@@ -409,22 +490,12 @@ public class PackageHelper {
if (checkBoth) {
if (fitsOnInternal) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- } else if (!emulated && fitsOnExternal) {
+ } else if (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;
- }
+ return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked,