Browse mode for DocumentsUI, removed volume state.
The existing management mode is too specific, and requires that
storage backends add queryChildDocumentsForManage(), etc. Instead,
to offer more natural browsing support, add a new BROWSE_ROOT intent.
It behaves mostly like MANAGE_ROOT, except that it doesn't mutate
its Uris with setManageMode(), and it shortcuts straight to VIEW on
clicked documents.
It can be launched like this:
$ adb shell am start -a android.provider.action.BROWSE_ROOT
-d content://com.android.externalstorage.documents/root/8405-1DFB
-c android.intent.category.DEFAULT
Also rename a MetricsConstants to make it clearer, and don't
auto-mount all emulated volumes.
Fix bugs around parceling of DiskInfo/VolumeInfo. Method to resolve
the best description for a VolumeInfo, which might need to fall
back to DiskInfo.
Add back "removed" volume state so we send broadcast when a volume
is destroyed, matching the expected public API behavior.
Bug: 19993667
Change-Id: I13aff32c5e11dfc63da44aee9e93a27f4690a43f
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index 56c55f1..4704b67 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -16,7 +16,7 @@
package android.os.storage;
-import android.content.Context;
+import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DebugUtils;
@@ -57,12 +57,12 @@
volumes = parcel.readStringArray();
}
- public String getDescription(Context context) {
+ public String getDescription() {
// TODO: splice vendor label into these strings
if ((flags & FLAG_SD) != 0) {
- return context.getString(com.android.internal.R.string.storage_sd_card);
+ return Resources.getSystem().getString(com.android.internal.R.string.storage_sd_card);
} else if ((flags & FLAG_USB) != 0) {
- return context.getString(com.android.internal.R.string.storage_usb);
+ return Resources.getSystem().getString(com.android.internal.R.string.storage_usb);
} else {
return null;
}
@@ -119,7 +119,7 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(id);
- parcel.writeInt(flags);
+ parcel.writeInt(this.flags);
parcel.writeLong(size);
parcel.writeString(label);
parcel.writeStringArray(volumes);
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 5a5d8b7..bd42f6a 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -30,10 +30,12 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import java.io.File;
@@ -454,6 +456,18 @@
}
/** {@hide} */
+ public @Nullable DiskInfo findDiskByVolumeId(String volId) {
+ Preconditions.checkNotNull(volId);
+ // TODO; go directly to service to make this faster
+ for (DiskInfo disk : getDisks()) {
+ if (ArrayUtils.contains(disk.volumes, volId)) {
+ return disk;
+ }
+ }
+ return null;
+ }
+
+ /** {@hide} */
public @Nullable VolumeInfo findVolumeById(String id) {
Preconditions.checkNotNull(id);
// TODO; go directly to service to make this faster
@@ -487,6 +501,23 @@
}
/** {@hide} */
+ public @Nullable String getBestVolumeDescription(String volId) {
+ String descrip = null;
+
+ final VolumeInfo vol = findVolumeById(volId);
+ if (vol != null) {
+ descrip = vol.getDescription();
+ }
+
+ final DiskInfo disk = findDiskByVolumeId(volId);
+ if (disk != null && TextUtils.isEmpty(descrip)) {
+ descrip = disk.getDescription();
+ }
+
+ return descrip;
+ }
+
+ /** {@hide} */
public void mount(String volId) {
try {
mMountService.mount(volId);
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 2dc0361..beca8b8 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.mtp.MtpStorage;
import android.os.Environment;
import android.os.Parcel;
@@ -44,6 +45,8 @@
* @hide
*/
public class VolumeInfo implements Parcelable {
+ /** Stub volume representing internal private storage */
+ public static final String ID_PRIVATE_INTERNAL = "private";
/** Real volume representing internal emulated storage */
public static final String ID_EMULATED_INTERNAL = "emulated";
@@ -59,6 +62,7 @@
public static final int STATE_FORMATTING = 3;
public static final int STATE_UNMOUNTING = 4;
public static final int STATE_UNMOUNTABLE = 5;
+ public static final int STATE_REMOVED = 6;
public static final int FLAG_PRIMARY = 1 << 0;
public static final int FLAG_VISIBLE = 1 << 1;
@@ -73,12 +77,14 @@
sStateToEnvironment.put(VolumeInfo.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED);
sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTING, Environment.MEDIA_EJECTING);
sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTABLE, Environment.MEDIA_UNMOUNTABLE);
+ sStateToEnvironment.put(VolumeInfo.STATE_REMOVED, Environment.MEDIA_REMOVED);
sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED);
sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING);
sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED);
sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT);
sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE);
+ sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED);
}
/** vold state */
@@ -96,8 +102,6 @@
public final int mtpIndex;
public String nickname;
- public DiskInfo disk;
-
public VolumeInfo(String id, int type, int mtpIndex) {
this.id = Preconditions.checkNotNull(id);
this.type = type;
@@ -135,9 +139,9 @@
return getBroadcastForEnvironment(getEnvironmentForState(state));
}
- public String getDescription(Context context) {
- if (ID_EMULATED_INTERNAL.equals(id)) {
- return context.getString(com.android.internal.R.string.storage_internal);
+ public @Nullable String getDescription() {
+ if (ID_PRIVATE_INTERNAL.equals(id)) {
+ return Resources.getSystem().getString(com.android.internal.R.string.storage_internal);
} else if (!TextUtils.isEmpty(nickname)) {
return nickname;
} else if (!TextUtils.isEmpty(fsLabel)) {
@@ -189,7 +193,7 @@
userPath = new File("/dev/null");
}
- String description = getDescription(context);
+ String description = getDescription();
if (description == null) {
description = context.getString(android.R.string.unknownName);
}
@@ -283,7 +287,7 @@
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(id);
parcel.writeInt(type);
- parcel.writeInt(flags);
+ parcel.writeInt(this.flags);
parcel.writeInt(userId);
parcel.writeInt(state);
parcel.writeString(fsType);
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 9a0858a..b0a600b 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -106,6 +106,9 @@
/** {@hide} */
public static final String ACTION_MANAGE_DOCUMENT = "android.provider.action.MANAGE_DOCUMENT";
+ /** {@hide} */
+ public static final String ACTION_BROWSE_ROOT = "android.provider.action.BROWSE_ROOT";
+
/**
* Buffer is large enough to rewind past any EXIF headers.
*/
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
index ee225a1..6aa81ce 100644
--- a/core/java/com/android/internal/logging/MetricsConstants.java
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -69,7 +69,9 @@
public static final int DEVELOPMENT = 39;
public static final int DEVICEINFO = 40;
public static final int DEVICEINFO_IMEI_INFORMATION = 41;
+ @Deprecated
public static final int DEVICEINFO_MEMORY = 42;
+ public static final int DEVICEINFO_STORAGE = 42;
public static final int DEVICEINFO_SIM_STATUS = 43;
public static final int DEVICEINFO_STATUS = 44;
public static final int DEVICEINFO_USB = 45;
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index f908fcb..62e724a 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -16,6 +16,8 @@
package com.android.internal.util;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.util.ArraySet;
import dalvik.system.VMRuntime;
@@ -24,13 +26,13 @@
import java.lang.reflect.Array;
import java.util.ArrayList;
+import java.util.Objects;
/**
* ArrayUtils contains some methods that you can call to find out
* the most efficient increments by which to grow arrays.
*/
-public class ArrayUtils
-{
+public class ArrayUtils {
private static final int CACHE_SIZE = 73;
private static Object[] sCache = new Object[CACHE_SIZE];
@@ -158,11 +160,7 @@
public static <T> int indexOf(T[] array, T value) {
if (array == null) return -1;
for (int i = 0; i < array.length; i++) {
- if (array[i] == null) {
- if (value == null) return i;
- } else {
- if (value != null && array[i].equals(value)) return i;
- }
+ if (Objects.equals(array[i], value)) return i;
}
return -1;
}
@@ -209,17 +207,15 @@
}
/**
- * Appends an element to a copy of the array and returns the copy.
- * @param array The original array, or null to represent an empty array.
- * @param element The element to add.
- * @return A new array that contains all of the elements of the original array
- * with the specified element added at the end.
+ * Adds value to given array if not already present, providing set-like
+ * behavior.
*/
@SuppressWarnings("unchecked")
- public static <T> T[] appendElement(Class<T> kind, T[] array, T element) {
+ public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
final T[] result;
final int end;
if (array != null) {
+ if (contains(array, element)) return array;
end = array.length;
result = (T[])Array.newInstance(kind, end + 1);
System.arraycopy(array, 0, result, 0, end);
@@ -232,21 +228,15 @@
}
/**
- * Removes an element from a copy of the array and returns the copy.
- * If the element is not present, then the original array is returned unmodified.
- * @param array The original array, or null to represent an empty array.
- * @param element The element to remove.
- * @return A new array that contains all of the elements of the original array
- * except the first copy of the specified element removed. If the specified element
- * was not present, then returns the original array. Returns null if the result
- * would be an empty array.
+ * Removes value from given array if present, providing set-like behavior.
*/
@SuppressWarnings("unchecked")
- public static <T> T[] removeElement(Class<T> kind, T[] array, T element) {
+ public static @Nullable <T> T[] removeElement(Class<T> kind, @Nullable T[] array, T element) {
if (array != null) {
+ if (!contains(array, element)) return array;
final int length = array.length;
for (int i = 0; i < length; i++) {
- if (array[i] == element) {
+ if (Objects.equals(array[i], element)) {
if (length == 1) {
return null;
}
@@ -261,14 +251,10 @@
}
/**
- * Appends a new value to a copy of the array and returns the copy. If
- * the value is already present, the original array is returned
- * @param cur The original array, or null to represent an empty array.
- * @param val The value to add.
- * @return A new array that contains all of the values of the original array
- * with the new value added, or the original array.
+ * Adds value to given array if not already present, providing set-like
+ * behavior.
*/
- public static int[] appendInt(int[] cur, int val) {
+ public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
if (cur == null) {
return new int[] { val };
}
@@ -284,7 +270,10 @@
return ret;
}
- public static int[] removeInt(int[] cur, int val) {
+ /**
+ * Removes value from given array if present, providing set-like behavior.
+ */
+ public static @Nullable int[] removeInt(@Nullable int[] cur, int val) {
if (cur == null) {
return null;
}
@@ -305,14 +294,10 @@
}
/**
- * Appends a new value to a copy of the array and returns the copy. If
- * the value is already present, the original array is returned
- * @param cur The original array, or null to represent an empty array.
- * @param val The value to add.
- * @return A new array that contains all of the values of the original array
- * with the new value added, or the original array.
+ * Adds value to given array if not already present, providing set-like
+ * behavior.
*/
- public static long[] appendLong(long[] cur, long val) {
+ public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
if (cur == null) {
return new long[] { val };
}
@@ -328,7 +313,10 @@
return ret;
}
- public static long[] removeLong(long[] cur, long val) {
+ /**
+ * Removes value from given array if present, providing set-like behavior.
+ */
+ public static @Nullable long[] removeLong(@Nullable long[] cur, long val) {
if (cur == null) {
return null;
}