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

Commit f14673f5 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add findPath API to SAF."

parents f01d9179 aba97f3c
Loading
Loading
Loading
Loading
+86 −0
Original line number Diff line number Diff line
@@ -36,8 +36,10 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.storage.StorageVolume;
import android.system.ErrnoException;
@@ -644,6 +646,8 @@ public final class DocumentsContract {
    public static final String METHOD_REMOVE_DOCUMENT = "android:removeDocument";
    /** {@hide} */
    public static final String METHOD_EJECT_ROOT = "android:ejectRoot";
    /** {@hide} */
    public static final String METHOD_FIND_PATH = "android:findPath";

    /** {@hide} */
    public static final String EXTRA_PARENT_URI = "parentUri";
@@ -1306,6 +1310,41 @@ public final class DocumentsContract {
        return out.getBoolean(DocumentsContract.EXTRA_RESULT);
    }

    /**
     * Finds the canonical path to the root. Document id should be unique across
     * roots.
     *
     * @param documentUri uri of the document which path is requested.
     * @return the path to the root of the document, or {@code null} if failed.
     * @see DocumentsProvider#findPath(String)
     *
     * {@hide}
     */
    public static Path findPath(ContentResolver resolver, Uri documentUri)
            throws RemoteException {
        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
                documentUri.getAuthority());
        try {
            return findPath(client, documentUri);
        } catch (Exception e) {
            Log.w(TAG, "Failed to find path", e);
            return null;
        } finally {
            ContentProviderClient.releaseQuietly(client);
        }
    }

    /** {@hide} */
    public static Path findPath(ContentProviderClient client, Uri documentUri)
            throws RemoteException {
        final Bundle in = new Bundle();
        in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);

        final Bundle out = client.call(METHOD_FIND_PATH, null, in);

        return out.getParcelable(DocumentsContract.EXTRA_RESULT);
    }

    /**
     * Open the given image for thumbnail purposes, using any embedded EXIF
     * thumbnail if available, and providing orientation hints from the parent
@@ -1345,4 +1384,51 @@ public final class DocumentsContract {

        return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
    }

    /**
     * Holds a path from a root to a particular document under it.
     *
     * @hide
     */
    public static final class Path implements Parcelable {

        public final String mRootId;
        public final List<String> mPath;

        /**
         * Creates a Path.
         * @param rootId the id of the root
         * @param path the list of document ids from the root document
         *             at position 0 to the target document
         */
        public Path(String rootId, List<String> path) {
            mRootId = rootId;
            mPath = path;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(mRootId);
            dest.writeStringList(mPath);
        }

        @Override
        public int describeContents() {
            return 0;
        }

        public static final Creator<Path> CREATOR = new Creator<Path>() {
            @Override
            public Path createFromParcel(Parcel in) {
                final String rootId = in.readString();
                final List<String> path = in.createStringArrayList();
                return new Path(rootId, path);
            }

            @Override
            public Path[] newArray(int size) {
                return new Path[size];
            }
        };
    }
}
+29 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ 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_FIND_PATH;
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;
@@ -33,6 +34,7 @@ import static android.provider.DocumentsContract.getSearchDocumentsQuery;
import static android.provider.DocumentsContract.getTreeDocumentId;
import static android.provider.DocumentsContract.isTreeUri;

import android.Manifest;
import android.annotation.CallSuper;
import android.content.ClipDescription;
import android.content.ContentProvider;
@@ -53,6 +55,7 @@ import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract.Path;
import android.util.Log;

import libcore.io.IoUtils;
@@ -322,6 +325,26 @@ public abstract class DocumentsProvider extends ContentProvider {
        throw new UnsupportedOperationException("Remove not supported");
    }

    /**
     * Finds the canonical path to the root for the requested document. If there are
     * more than one path to this document, return the most typical one.
     *
     * <p>This API assumes that document id has enough info to infer the root.
     * Different roots should use different document id to refer to the same
     * document.
     *
     * @param documentId the document which path is requested.
     * @return the path of the requested document to the root, or null if
     *      such operation is not supported.
     *
     * @hide
     */
    public Path findPath(String documentId)
            throws FileNotFoundException {
        Log.w(TAG, "findPath is called on an unsupported provider.");
        return null;
    }

    /**
     * Return all roots currently provided. To display to users, you must define
     * at least one root. You should avoid making network requests to keep this
@@ -873,6 +896,12 @@ 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_FIND_PATH.equals(method)) {
            getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null);

            final Path path = findPath(documentId);

            out.putParcelable(DocumentsContract.EXTRA_RESULT, path);
        } else {
            throw new UnsupportedOperationException("Method not supported " + method);
        }
+32 −3
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.os.storage.VolumeInfo;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract.Path;
import android.provider.DocumentsProvider;
import android.provider.MediaStore;
import android.provider.Settings;
@@ -48,6 +49,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.Log;
import android.util.Pair;
import android.webkit.MimeTypeMap;

import com.android.internal.annotations.GuardedBy;
@@ -183,7 +185,8 @@ public class ExternalStorageProvider extends DocumentsProvider {
            root.rootId = rootId;
            root.volumeId = volume.id;
            root.flags = Root.FLAG_LOCAL_ONLY
                    | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD;
                    | Root.FLAG_SUPPORTS_SEARCH
                    | Root.FLAG_SUPPORTS_IS_CHILD;

            final DiskInfo disk = volume.getDisk();
            if (DEBUG) Log.d(TAG, "Disk for root " + rootId + " is " + disk);
@@ -270,7 +273,6 @@ public class ExternalStorageProvider extends DocumentsProvider {
        return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
    }


    private String getDocIdForFile(File file) throws FileNotFoundException {
        return getDocIdForFileMaybeCreate(file, false);
    }
@@ -323,6 +325,11 @@ public class ExternalStorageProvider extends DocumentsProvider {
    }

    private File getFileForDocId(String docId, boolean visible) throws FileNotFoundException {
        return resolveDocId(docId, visible).second;
    }

    private Pair<RootInfo, File> resolveDocId(String docId, boolean visible)
            throws FileNotFoundException {
        final int splitIndex = docId.indexOf(':', 1);
        final String tag = docId.substring(0, splitIndex);
        final String path = docId.substring(splitIndex + 1);
@@ -346,7 +353,7 @@ public class ExternalStorageProvider extends DocumentsProvider {
        if (!target.exists()) {
            throw new FileNotFoundException("Missing file for " + docId + " at " + target);
        }
        return target;
        return Pair.create(root, target);
    }

    private void includeFile(MatrixCursor result, String docId, File file)
@@ -422,6 +429,28 @@ public class ExternalStorageProvider extends DocumentsProvider {
        }
    }

    @Override
    public Path findPath(String documentId)
            throws FileNotFoundException {
        LinkedList<String> path = new LinkedList<>();

        final Pair<RootInfo, File> resolvedDocId = resolveDocId(documentId, false);
        RootInfo root = resolvedDocId.first;
        File file = resolvedDocId.second;

        if (!file.exists()) {
            throw new FileNotFoundException();
        }

        while (file != null && file.getAbsolutePath().startsWith(root.path.getAbsolutePath())) {
            path.addFirst(getDocIdForFile(file));

            file = file.getParentFile();
        }

        return new Path(root.rootId, path);
    }

    @Override
    public String createDocument(String docId, String mimeType, String displayName)
            throws FileNotFoundException {