Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 00888522 authored by Ben Lin's avatar Ben Lin Committed by Android (Google) Code Review
Browse files

Merge "Provider-level changes for implementing direct eject of a root in Files app."

parents b18ff38a e7822fb7
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -593,6 +593,9 @@ public final class DocumentsContract {
         * @hide
         */
        public static final int FLAG_REMOVABLE_USB = 1 << 20;

        /** {@hide} */
        public static final int FLAG_SUPPORTS_EJECT = 1 << 21;
    }

    /**
@@ -643,6 +646,8 @@ public final class DocumentsContract {
    public static final String METHOD_IS_CHILD_DOCUMENT = "android:isChildDocument";
    /** {@hide} */
    public static final String METHOD_REMOVE_DOCUMENT = "android:removeDocument";
    /** {@hide} */
    public static final String METHOD_EJECT_ROOT = "android:ejectRoot";

    /** {@hide} */
    public static final String EXTRA_PARENT_URI = "parentUri";
@@ -1274,6 +1279,37 @@ public final class DocumentsContract {
        client.call(METHOD_REMOVE_DOCUMENT, null, in);
    }

    /** {@hide} */
    public static boolean 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;
        } finally {
            ContentProviderClient.releaseQuietly(client);
        }
    }

    /** {@hide} */
    public static boolean 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);
    }

    /**
     * Open the given image for thumbnail purposes, using any embedded EXIF
     * thumbnail if available, and providing orientation hints from the parent
+18 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.provider;
import static android.provider.DocumentsContract.METHOD_COPY_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_EJECT_ROOT;
import static android.provider.DocumentsContract.METHOD_IS_CHILD_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_MOVE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_REMOVE_DOCUMENT;
@@ -458,6 +459,12 @@ public abstract class DocumentsProvider extends ContentProvider {
        throw new UnsupportedOperationException("Search not supported");
    }

    /** {@hide} */
    @SuppressWarnings("unused")
    public boolean ejectRoot(String rootId) {
        throw new UnsupportedOperationException("Eject not supported");
    }

    /**
     * Return concrete MIME type of the requested document. Must match the value
     * of {@link Document#COLUMN_MIME_TYPE} for this document. The default
@@ -851,7 +858,17 @@ public abstract class DocumentsProvider extends ContentProvider {

            // It's responsibility of the provider to revoke any grants, as the document may be
            // still attached to another parents.

        } else 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);
            final Uri rootUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
            final String rootId = DocumentsContract.getRootId(rootUri);
            final boolean ejected = ejectRoot(rootId);

            out.putBoolean(DocumentsContract.EXTRA_RESULT, ejected);
        } else {
            throw new UnsupportedOperationException("Method not supported " + method);
        }
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

    <application android:label="@string/app_label">
        <provider
+25 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.graphics.Point;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Environment;
@@ -86,6 +87,7 @@ public class ExternalStorageProvider extends DocumentsProvider {

    private static class RootInfo {
        public String rootId;
        public String volumeId;
        public int flags;
        public String title;
        public String docId;
@@ -182,6 +184,7 @@ public class ExternalStorageProvider extends DocumentsProvider {
            mRoots.put(rootId, root);

            root.rootId = rootId;
            root.volumeId = volume.id;
            root.flags = Root.FLAG_LOCAL_ONLY
                    | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD;

@@ -193,6 +196,10 @@ public class ExternalStorageProvider extends DocumentsProvider {
                root.flags |= Root.FLAG_REMOVABLE_USB;
            }

            if (!VolumeInfo.ID_EMULATED_INTERNAL.equals(volume.getId())) {
                root.flags |= Root.FLAG_SUPPORTS_EJECT;
            }

            if (volume.isPrimary()) {
                // save off the primary volume for subsequent "Home" dir initialization.
                primaryVolume = volume;
@@ -583,6 +590,24 @@ public class ExternalStorageProvider extends DocumentsProvider {
        return result;
    }

    @Override
    public boolean 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");
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
        return ejected;
    }

    @Override
    public String getDocumentType(String documentId) throws FileNotFoundException {
        if (mArchiveHelper.isArchivedDocument(documentId)) {