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

Commit da3b55a1 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Reroute Uri conversions though MediaProvider."

parents 4ea7d34d 643e99ef
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -36802,6 +36802,7 @@ package android.provider {
    ctor public MediaStore();
    method public static android.net.Uri getDocumentUri(android.content.Context, android.net.Uri);
    method public static android.net.Uri getMediaScannerUri();
    method public static android.net.Uri getMediaUri(android.content.Context, android.net.Uri);
    method public static java.lang.String getVersion(android.content.Context);
    field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
    field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
+2 −0
Original line number Diff line number Diff line
@@ -731,6 +731,8 @@ public final class DocumentsContract {
    public static final String EXTRA_PARENT_URI = "parentUri";
    /** {@hide} */
    public static final String EXTRA_URI = "uri";
    /** {@hide} */
    public static final String EXTRA_URI_PERMISSIONS = "uriPermissions";

    /**
     * @see #createWebLinkIntent(ContentResolver, Uri, Bundle)
+48 −68
Original line number Diff line number Diff line
@@ -46,9 +46,6 @@ import android.util.Log;

import com.android.internal.annotations.GuardedBy;

import libcore.io.IoUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -82,6 +79,11 @@ public final class MediaStore {
     */
    public static final String RETRANSLATE_CALL = "update_titles";

    /** {@hide} */
    public static final String GET_DOCUMENT_URI_CALL = "get_document_uri";
    /** {@hide} */
    public static final String GET_MEDIA_URI_CALL = "get_media_uri";

    /**
     * This is for internal use by the media scanner only.
     * Name of the (optional) Uri parameter that determines whether to skip deleting
@@ -2275,84 +2277,62 @@ public final class MediaStore {
    }

    /**
     * Gets a URI backed by a {@link DocumentsProvider} that points to the same media
     * file as the specified mediaUri. This allows apps who have permissions to access
     * media files in Storage Access Framework to perform file operations through that
     * on media files.
     * Return a {@link DocumentsProvider} Uri that is an equivalent to the given
     * {@link MediaStore} Uri.
     * <p>
     * Note: this method doesn't grant any URI permission. Callers need to obtain
     * permission before calling this method. One way to obtain permission is through
     * a 3-step process:
     * <ol>
     *     <li>Call {@link android.os.storage.StorageManager#getStorageVolume(File)} to
     *     obtain the {@link android.os.storage.StorageVolume} of a media file;</li>
     * This allows apps with Storage Access Framework permissions to convert
     * between {@link MediaStore} and {@link DocumentsProvider} Uris that refer
     * to the same underlying item. Note that this method doesn't grant any new
     * permissions; callers must already hold permissions obtained with
     * {@link Intent#ACTION_OPEN_DOCUMENT} or related APIs.
     *
     *     <li>Invoke the intent returned by
     *     {@link android.os.storage.StorageVolume#createAccessIntent(String)} to
     *     obtain the access of the volume or one of its specific subdirectories;</li>
     *
     *     <li>Check whether permission is granted and take persistent permission.</li>
     * </ol>
     * @param mediaUri the media URI which document URI is requested
     * @return the document URI
     * @param mediaUri The {@link MediaStore} Uri to convert.
     * @return An equivalent {@link DocumentsProvider} Uri. Returns {@code null}
     *         if no equivalent was found.
     * @see #getMediaUri(Context, Uri)
     */
    public static Uri getDocumentUri(Context context, Uri mediaUri) {

        try {
        final ContentResolver resolver = context.getContentResolver();

            final String path = getFilePath(resolver, mediaUri);
        final List<UriPermission> uriPermissions = resolver.getPersistedUriPermissions();

            return getDocumentUri(resolver, path, uriPermissions);
        try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
            final Bundle in = new Bundle();
            in.putParcelable(DocumentsContract.EXTRA_URI, mediaUri);
            in.putParcelableList(DocumentsContract.EXTRA_URI_PERMISSIONS, uriPermissions);
            final Bundle out = client.call(GET_DOCUMENT_URI_CALL, null, in);
            return out.getParcelable(DocumentsContract.EXTRA_URI);
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
    }

    private static String getFilePath(ContentResolver resolver, Uri mediaUri)
            throws RemoteException {

        try (ContentProviderClient client =
                     resolver.acquireUnstableContentProviderClient(AUTHORITY)) {
            final Cursor c = client.query(
                    mediaUri,
                    new String[]{ MediaColumns.DATA },
                    null, /* selection */
                    null, /* selectionArg */
                    null /* sortOrder */);

            final String path;
            try {
                if (c.getCount() == 0) {
                    throw new IllegalStateException("Not found media file under URI: " + mediaUri);
                }

                if (!c.moveToFirst()) {
                    throw new IllegalStateException("Failed to move cursor to the first item.");
                }

                path = c.getString(0);
            } finally {
                IoUtils.closeQuietly(c);
            }

            return path;
        }
    }

    private static Uri getDocumentUri(
            ContentResolver resolver, String path, List<UriPermission> uriPermissions)
            throws RemoteException {
    /**
     * Return a {@link MediaStore} Uri that is an equivalent to the given
     * {@link DocumentsProvider} Uri.
     * <p>
     * This allows apps with Storage Access Framework permissions to convert
     * between {@link MediaStore} and {@link DocumentsProvider} Uris that refer
     * to the same underlying item. Note that this method doesn't grant any new
     * permissions; callers must already hold permissions obtained with
     * {@link Intent#ACTION_OPEN_DOCUMENT} or related APIs.
     *
     * @param documentUri The {@link DocumentsProvider} Uri to convert.
     * @return An equivalent {@link MediaStore} Uri. Returns {@code null} if no
     *         equivalent was found.
     * @see #getDocumentUri(Context, Uri)
     */
    public static Uri getMediaUri(Context context, Uri documentUri) {
        final ContentResolver resolver = context.getContentResolver();
        final List<UriPermission> uriPermissions = resolver.getPersistedUriPermissions();

        try (ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
                DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY)) {
        try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
            final Bundle in = new Bundle();
            in.putParcelableList(
                    DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY + ".extra.uriPermissions",
                    uriPermissions);
            final Bundle out = client.call("getDocumentId", path, in);
            in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
            in.putParcelableList(DocumentsContract.EXTRA_URI_PERMISSIONS, uriPermissions);
            final Bundle out = client.call(GET_MEDIA_URI_CALL, null, in);
            return out.getParcelable(DocumentsContract.EXTRA_URI);
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -17,6 +17,10 @@
            <intent-filter>
                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
            </intent-filter>
            <!-- Stub that allows MediaProvider to make incoming calls -->
            <path-permission
                android:path="/media_internal"
                android:permission="android.permission.WRITE_MEDIA_STORAGE" />
        </provider>

        <receiver android:name=".MountReceiver">
+25 −4
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Path;
import android.provider.DocumentsContract.Root;
import android.provider.MediaStore;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
@@ -606,11 +607,16 @@ public class ExternalStorageProvider extends FileSystemProvider {
                    }
                    break;
                }
                case "getDocumentId": {
                    final String path = arg;
                    final List<UriPermission> accessUriPermissions =
                            extras.getParcelableArrayList(AUTHORITY + ".extra.uriPermissions");
                case MediaStore.GET_DOCUMENT_URI_CALL: {
                    // All callers must go through MediaProvider
                    getContext().enforceCallingPermission(
                            android.Manifest.permission.WRITE_MEDIA_STORAGE, TAG);

                    final Uri fileUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
                    final List<UriPermission> accessUriPermissions = extras
                            .getParcelableArrayList(DocumentsContract.EXTRA_URI_PERMISSIONS);

                    final String path = fileUri.getPath();
                    try {
                        final Bundle out = new Bundle();
                        final Uri uri = getDocumentUri(path, accessUriPermissions);
@@ -619,7 +625,22 @@ public class ExternalStorageProvider extends FileSystemProvider {
                    } catch (FileNotFoundException e) {
                        throw new IllegalStateException("File in " + path + " is not found.", e);
                    }
                }
                case MediaStore.GET_MEDIA_URI_CALL: {
                    // All callers must go through MediaProvider
                    getContext().enforceCallingPermission(
                            android.Manifest.permission.WRITE_MEDIA_STORAGE, TAG);

                    final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
                    final String docId = DocumentsContract.getDocumentId(documentUri);
                    try {
                        final Bundle out = new Bundle();
                        final Uri uri = Uri.fromFile(getFileForDocId(docId));
                        out.putParcelable(DocumentsContract.EXTRA_URI, uri);
                        return out;
                    } catch (FileNotFoundException e) {
                        throw new IllegalStateException(e);
                    }
                }
                default:
                    Log.w(TAG, "unknown method passed to call(): " + method);