diff options
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/app/ApplicationPackageManager.java | 18 | ||||
-rw-r--r-- | core/java/android/content/pm/ApplicationInfo.java | 5 | ||||
-rw-r--r-- | core/java/android/content/pm/IPackageManager.aidl | 3 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageInstaller.java | 5 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageManager.java | 18 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageParser.java | 4 | ||||
-rw-r--r-- | core/java/android/os/storage/StorageManager.java | 40 | ||||
-rw-r--r-- | core/java/android/os/storage/VolumeInfo.java | 1 | ||||
-rw-r--r-- | core/java/com/android/internal/content/PackageHelper.java | 125 |
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, |