Unhide FLAG_SUPPORT_EJECT and related methods.
Test: Builds and CTS tests pass. Some manual tests as well.
Bug: 36483910
Change-Id: Idd9b1c9d9573222ee12127044ff11b9ab2487f0a
diff --git a/api/current.txt b/api/current.txt
index 360c4f2..627eedc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -34230,6 +34230,7 @@
method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static void ejectRoot(android.content.ContentResolver, android.net.Uri);
method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
method public static java.lang.String getDocumentId(android.net.Uri);
method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
@@ -34298,6 +34299,7 @@
field public static final java.lang.String COLUMN_TITLE = "title";
field public static final int FLAG_LOCAL_ONLY = 2; // 0x2
field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1
+ field public static final int FLAG_SUPPORTS_EJECT = 32; // 0x20
field public static final int FLAG_SUPPORTS_IS_CHILD = 16; // 0x10
field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4
field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8
@@ -34311,6 +34313,7 @@
method public android.content.IntentSender createWebLinkIntent(java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public void deleteDocument(java.lang.String) throws java.io.FileNotFoundException;
+ method public void ejectRoot(java.lang.String);
method public android.provider.DocumentsContract.Path findDocumentPath(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
method public java.lang.String[] getDocumentStreamTypes(java.lang.String, java.lang.String);
method public java.lang.String getDocumentType(java.lang.String) throws java.io.FileNotFoundException;
diff --git a/api/system-current.txt b/api/system-current.txt
index 1a0a8bb..afcfa04 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -37136,6 +37136,7 @@
method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static void ejectRoot(android.content.ContentResolver, android.net.Uri);
method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
method public static java.lang.String getDocumentId(android.net.Uri);
method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
@@ -37204,6 +37205,7 @@
field public static final java.lang.String COLUMN_TITLE = "title";
field public static final int FLAG_LOCAL_ONLY = 2; // 0x2
field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1
+ field public static final int FLAG_SUPPORTS_EJECT = 32; // 0x20
field public static final int FLAG_SUPPORTS_IS_CHILD = 16; // 0x10
field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4
field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8
@@ -37217,6 +37219,7 @@
method public android.content.IntentSender createWebLinkIntent(java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public void deleteDocument(java.lang.String) throws java.io.FileNotFoundException;
+ method public void ejectRoot(java.lang.String);
method public android.provider.DocumentsContract.Path findDocumentPath(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
method public java.lang.String[] getDocumentStreamTypes(java.lang.String, java.lang.String);
method public java.lang.String getDocumentType(java.lang.String) throws java.io.FileNotFoundException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 674ffa6..6db9a04 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34358,6 +34358,7 @@
method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static void ejectRoot(android.content.ContentResolver, android.net.Uri);
method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
method public static java.lang.String getDocumentId(android.net.Uri);
method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
@@ -34426,6 +34427,7 @@
field public static final java.lang.String COLUMN_TITLE = "title";
field public static final int FLAG_LOCAL_ONLY = 2; // 0x2
field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1
+ field public static final int FLAG_SUPPORTS_EJECT = 32; // 0x20
field public static final int FLAG_SUPPORTS_IS_CHILD = 16; // 0x10
field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4
field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8
@@ -34439,6 +34441,7 @@
method public android.content.IntentSender createWebLinkIntent(java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public void deleteDocument(java.lang.String) throws java.io.FileNotFoundException;
+ method public void ejectRoot(java.lang.String);
method public android.provider.DocumentsContract.Path findDocumentPath(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
method public java.lang.String[] getDocumentStreamTypes(java.lang.String, java.lang.String);
method public java.lang.String getDocumentType(java.lang.String) throws java.io.FileNotFoundException;
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 56d4ff7..16eb358 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -594,6 +594,15 @@
public static final int FLAG_SUPPORTS_IS_CHILD = 1 << 4;
/**
+ * Flag indicating that this root can be ejected.
+ *
+ * @see #COLUMN_FLAGS
+ * @see DocumentsContract#ejectRoot(ContentResolver, Uri)
+ * @see DocumentsProvider#ejectRoot(String)
+ */
+ public static final int FLAG_SUPPORTS_EJECT = 1 << 5;
+
+ /**
* Flag indicating that this root is currently empty. This may be used
* to hide the root when opening documents, but the root will still be
* shown when creating documents and {@link #FLAG_SUPPORTS_CREATE} is
@@ -641,9 +650,6 @@
* @hide
*/
public static final int FLAG_REMOVABLE_USB = 1 << 20;
-
- /** {@hide} */
- public static final int FLAG_SUPPORTS_EJECT = 1 << 21;
}
/**
@@ -1345,35 +1351,30 @@
client.call(METHOD_REMOVE_DOCUMENT, null, in);
}
- /** {@hide} */
- public static boolean ejectRoot(ContentResolver resolver, Uri rootUri) {
+ /**
+ * Ejects the given root. It throws {@link IllegalStateException} when ejection failed.
+ *
+ * @param rootUri root with {@link Root#FLAG_SUPPORTS_EJECT} to be ejected
+ */
+ public static void ejectRoot(ContentResolver resolver, Uri rootUri) {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
rootUri.getAuthority());
try {
- return ejectRoot(client, rootUri);
- } catch (Exception e) {
- Log.w(TAG, "Failed to eject root", e);
- return false;
+ ejectRoot(client, rootUri);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
} finally {
ContentProviderClient.releaseQuietly(client);
}
}
/** {@hide} */
- public static boolean ejectRoot(ContentProviderClient client, Uri rootUri)
+ public static void ejectRoot(ContentProviderClient client, Uri rootUri)
throws RemoteException {
final Bundle in = new Bundle();
in.putParcelable(DocumentsContract.EXTRA_URI, rootUri);
- final Bundle out = client.call(METHOD_EJECT_ROOT, null, in);
-
- if (out == null) {
- throw new RemoteException("Failed to get a reponse from ejectRoot.");
- }
- if (!out.containsKey(DocumentsContract.EXTRA_RESULT)) {
- throw new RemoteException("Response did not include result field..");
- }
- return out.getBoolean(DocumentsContract.EXTRA_RESULT);
+ client.call(METHOD_EJECT_ROOT, null, in);
}
/**
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 620d33a5..24c9a6a 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -629,9 +629,14 @@
throw new UnsupportedOperationException("Search not supported");
}
- /** {@hide} */
+ /**
+ * Ejects the root. Throws {@link IllegalStateException} if ejection failed.
+ *
+ * @param rootId the root to be ejected.
+ * @see Root#FLAG_SUPPORTS_EJECT
+ */
@SuppressWarnings("unused")
- public boolean ejectRoot(String rootId) {
+ public void ejectRoot(String rootId) {
throw new UnsupportedOperationException("Eject not supported");
}
@@ -963,14 +968,12 @@
if (METHOD_EJECT_ROOT.equals(method)) {
// Given that certain system apps can hold MOUNT_UNMOUNT permission, but only apps
// signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for
- // MANAGE_DOCUMENTS here instead
- getContext().enforceCallingPermission(
- android.Manifest.permission.MANAGE_DOCUMENTS, null);
+ // MANAGE_DOCUMENTS or associated URI permission here instead
final Uri rootUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
- final String rootId = DocumentsContract.getRootId(rootUri);
- final boolean ejected = ejectRoot(rootId);
+ enforceWritePermissionInner(rootUri, getCallingPackage(), null);
- out.putBoolean(DocumentsContract.EXTRA_RESULT, ejected);
+ final String rootId = DocumentsContract.getRootId(rootUri);
+ ejectRoot(rootId);
return out;
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 8802010..b60e2fe 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -480,21 +480,18 @@
}
@Override
- public boolean ejectRoot(String rootId) {
+ public void ejectRoot(String rootId) {
final long token = Binder.clearCallingIdentity();
- boolean ejected = false;
RootInfo root = mRoots.get(rootId);
if (root != null) {
try {
mStorageManager.unmount(root.volumeId);
- ejected = true;
} catch (RuntimeException e) {
- Log.w(TAG, "Root '" + root.title + "' could not be ejected");
+ throw new IllegalStateException(e);
} finally {
Binder.restoreCallingIdentity(token);
}
}
- return ejected;
}
@Override