diff options
7 files changed, 143 insertions, 1 deletions
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 907ae26..e71713a 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -67,6 +67,8 @@ import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.os.storage.StorageManager; +import android.os.storage.VolumeInfo; import android.util.ArrayMap; import android.util.Log; import android.view.Display; @@ -1425,6 +1427,61 @@ final class ApplicationPackageManager extends PackageManager { } @Override + public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + if (app.isInternal()) { + return Preconditions.checkNotNull( + storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL)); + } else if (app.isExternalAsec()) { + final List<VolumeInfo> vols = storage.getVolumes(); + for (VolumeInfo vol : vols) { + if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) { + return vol; + } + } + throw new IllegalStateException("Failed to find primary public volume"); + } else { + return Preconditions.checkNotNull(storage.findVolumeByUuid(app.volumeUuid)); + } + } + + @Override + public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + final List<VolumeInfo> vols = storage.getVolumes(); + final List<VolumeInfo> candidates = new ArrayList<>(); + for (VolumeInfo vol : vols) { + if (isCandidateVolume(app, vol)) { + candidates.add(vol); + } + } + return candidates; + } + + private static boolean isCandidateVolume(ApplicationInfo app, VolumeInfo vol) { + // Private internal is always an option + if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { + return true; + } + + // System apps and apps demanding internal storage can't be moved + // anywhere else + if (app.isSystemApp() + || app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { + return false; + } + + // Moving into an ASEC on public primary is only an option when app is + // internal, or already in ASEC + if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) { + return app.isInternal() || app.isExternalAsec(); + } + + // Otherwise we can move to any private volume + return (vol.getType() == VolumeInfo.TYPE_PRIVATE); + } + + @Override public String getInstallerPackageName(String packageName) { try { return mPM.getInstallerPackageName(packageName); diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 5bdb7bb..caf069f 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -21,6 +21,7 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import android.util.Printer; import com.android.internal.util.ArrayUtils; @@ -937,6 +938,17 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; } + /** @hide */ + public boolean isInternal() { + return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0; + } + + /** @hide */ + public boolean isExternalAsec() { + return TextUtils.isEmpty(volumeUuid) + && (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; + } + /** * @hide */ diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 8844ea8..a128872 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -20,6 +20,7 @@ import android.annotation.CheckResult; import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.StringRes; @@ -4162,6 +4163,12 @@ public abstract class PackageManager { public abstract void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer); + /** {@hide} */ + public abstract @Nullable VolumeInfo getApplicationCurrentVolume(ApplicationInfo app); + + /** {@hide} */ + public abstract @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app); + /** * Returns the device identity that verifiers can use to associate their scheme to a particular * device. This should not be used by anything other than a package verifier. diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java index dc96640..2c60d41 100644 --- a/core/java/android/os/storage/DiskInfo.java +++ b/core/java/android/os/storage/DiskInfo.java @@ -26,6 +26,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import java.io.CharArrayWriter; +import java.util.Objects; /** * Information about a physical disk which may contain one or more @@ -118,6 +119,20 @@ public class DiskInfo implements Parcelable { } } + @Override + public boolean equals(Object o) { + if (o instanceof DiskInfo) { + return Objects.equals(id, ((DiskInfo) o).id); + } else { + return false; + } + } + + @Override + public int hashCode() { + return id.hashCode(); + } + public static final Creator<DiskInfo> CREATOR = new Creator<DiskInfo>() { @Override public DiskInfo createFromParcel(Parcel in) { diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index f06fc8c..a241728 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -38,6 +38,8 @@ import com.android.internal.util.Preconditions; import java.io.CharArrayWriter; import java.io.File; +import java.util.Comparator; +import java.util.Objects; /** * Information about a storage volume that may be mounted. A volume may be a @@ -77,6 +79,22 @@ public class VolumeInfo implements Parcelable { private static SparseArray<String> sStateToEnvironment = new SparseArray<>(); private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>(); + private static final Comparator<VolumeInfo> + sDescriptionComparator = new Comparator<VolumeInfo>() { + @Override + public int compare(VolumeInfo lhs, VolumeInfo rhs) { + if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(lhs.getId())) { + return -1; + } else if (lhs.getDescription() == null) { + return 1; + } else if (rhs.getDescription() == null) { + return -1; + } else { + return lhs.getDescription().compareTo(rhs.getDescription()); + } + } + }; + static { sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED); sStateToEnvironment.put(VolumeInfo.STATE_MOUNTING, Environment.MEDIA_CHECKING); @@ -150,6 +168,10 @@ public class VolumeInfo implements Parcelable { return getBroadcastForEnvironment(getEnvironmentForState(state)); } + public static @NonNull Comparator<VolumeInfo> getDescriptionComparator() { + return sDescriptionComparator; + } + public @NonNull String getId() { return id; } @@ -344,6 +366,20 @@ public class VolumeInfo implements Parcelable { } } + @Override + public boolean equals(Object o) { + if (o instanceof VolumeInfo) { + return Objects.equals(id, ((VolumeInfo) o).id); + } else { + return false; + } + } + + @Override + public int hashCode() { + return id.hashCode(); + } + public static final Creator<VolumeInfo> CREATOR = new Creator<VolumeInfo>() { @Override public VolumeInfo createFromParcel(Parcel in) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a260b0e..c12545b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -6280,7 +6280,8 @@ public class PackageManagerService extends IPackageManager.Stub { !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) { final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir; for (int userId : userIds) { - if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, nativeLibPath, userId) < 0) { + if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName, + nativeLibPath, userId) < 0) { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Failed linking native library dir (user=" + userId + ")"); } diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index e78750c..5a8c7ff 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -16,6 +16,7 @@ package android.test.mock; +import android.annotation.NonNull; import android.app.PackageInstallObserver; import android.content.ComponentName; import android.content.Intent; @@ -51,6 +52,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.UserHandle; +import android.os.storage.VolumeInfo; import java.util.List; @@ -498,6 +500,18 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + /** {@hide} */ + @Override + public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) { + throw new UnsupportedOperationException(); + } + + /** {@hide} */ + @Override + public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) { + throw new UnsupportedOperationException(); + } + @Override public String getInstallerPackageName(String packageName) { throw new UnsupportedOperationException(); |