DocumentsProvider.java revision 21de56a94668e0fda1b8bb4ee4f99a09b40d28fd
1aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey/*
2aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * Copyright (C) 2013 The Android Open Source Project
3aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey *
4aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * you may not use this file except in compliance with the License.
6aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * You may obtain a copy of the License at
7aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey *
8aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey *
10aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * Unless required by applicable law or agreed to in writing, software
11aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * See the License for the specific language governing permissions and
14aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * limitations under the License.
15aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */
16aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
17aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeypackage android.provider;
18aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
19aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport static android.provider.DocumentsContract.EXTRA_THUMBNAIL_SIZE;
20aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT;
21aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
22ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkeyimport static android.provider.DocumentsContract.getDocumentId;
23ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkeyimport static android.provider.DocumentsContract.getRootId;
24ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkeyimport static android.provider.DocumentsContract.getSearchDocumentsQuery;
25aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
26aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.ContentProvider;
27e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkeyimport android.content.ContentResolver;
28aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.ContentValues;
29aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.Context;
30aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.Intent;
31aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.UriMatcher;
32e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkeyimport android.content.pm.PackageManager;
33aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.pm.ProviderInfo;
34aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.res.AssetFileDescriptor;
35aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.database.Cursor;
36aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.graphics.Point;
37aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.net.Uri;
38aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.os.Bundle;
39aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.os.CancellationSignal;
40aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.os.ParcelFileDescriptor;
41aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.os.ParcelFileDescriptor.OnCloseListener;
42ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkeyimport android.provider.DocumentsContract.Document;
43e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkeyimport android.provider.DocumentsContract.Root;
44aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.util.Log;
45aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
46aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport libcore.io.IoUtils;
47aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
48aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport java.io.FileNotFoundException;
4921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkeyimport java.util.Objects;
50aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
51aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey/**
52e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Base class for a document provider. A document provider offers read and write
53e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * access to durable files, such as files stored on a local disk, or files in a
54e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * cloud storage service. To create a document provider, extend this class,
55e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * implement the abstract methods, and add it to your manifest like this:
56e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *
57e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <pre class="prettyprint">&lt;manifest&gt;
58e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *    ...
59e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *    &lt;application&gt;
60e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *        ...
61e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *        &lt;provider
62e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            android:name="com.example.MyCloudProvider"
63e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            android:authorities="com.example.mycloudprovider"
64e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            android:exported="true"
65e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            android:grantUriPermissions="true"
663b945405cf96eae8b882f87934222a453718a559Jeff Sharkey *            android:permission="android.permission.MANAGE_DOCUMENTS"
673b945405cf96eae8b882f87934222a453718a559Jeff Sharkey *            android:enabled="@bool/isAtLeastKitKat"&gt;
68e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            &lt;intent-filter&gt;
69e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *                &lt;action android:name="android.content.action.DOCUMENTS_PROVIDER" /&gt;
70e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            &lt;/intent-filter&gt;
71e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *        &lt;/provider&gt;
72e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *        ...
73e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *    &lt;/application&gt;
74e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *&lt;/manifest&gt;</pre>
75aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * <p>
76e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * When defining your provider, you must protect it with
77e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link android.Manifest.permission#MANAGE_DOCUMENTS}, which is a permission
78e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * only the system can obtain. Applications cannot use a documents provider
79e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * directly; they must go through {@link Intent#ACTION_OPEN_DOCUMENT} or
80e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Intent#ACTION_CREATE_DOCUMENT} which requires a user to actively
819352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * navigate and select documents. When a user selects documents through that UI,
829352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * the system issues narrow URI permission grants to the requesting application.
83e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p>
84e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <h3>Documents</h3>
85aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * <p>
86e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * A document can be either an openable stream (with a specific MIME type), or a
87aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * directory containing additional documents (with the
88e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Document#MIME_TYPE_DIR} MIME type). Each directory represents the top
89e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * of a subtree containing zero or more documents, which can recursively contain
90e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * even more documents and directories.
91e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p>
92e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p>
93e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Each document can have different capabilities, as described by
94e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Document#COLUMN_FLAGS}. For example, if a document can be represented
959352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * as a thumbnail, your provider can set
969352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * {@link Document#FLAG_SUPPORTS_THUMBNAIL} and implement
97e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link #openDocumentThumbnail(String, Point, CancellationSignal)} to return
98e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * that thumbnail.
99e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p>
100e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p>
101e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Each document under a provider is uniquely referenced by its
102e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Document#COLUMN_DOCUMENT_ID}, which must not change once returned. A
103e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * single document can be included in multiple directories when responding to
104e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link #queryChildDocuments(String, String[], String)}. For example, a
105e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * provider might surface a single photo in multiple locations: once in a
1069352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * directory of geographic locations, and again in a directory of dates.
107e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p>
108e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <h3>Roots</h3>
109aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * <p>
110e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * All documents are surfaced through one or more "roots." Each root represents
111e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * the top of a document tree that a user can navigate. For example, a root
112e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * could represent an account or a physical storage device. Similar to
113e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * documents, each root can have capabilities expressed through
114e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Root#COLUMN_FLAGS}.
115e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p>
116aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey *
117aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @see Intent#ACTION_OPEN_DOCUMENT
118aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @see Intent#ACTION_CREATE_DOCUMENT
119aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */
120aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeypublic abstract class DocumentsProvider extends ContentProvider {
121aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    private static final String TAG = "DocumentsProvider";
122aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
123a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey    private static final int MATCH_ROOTS = 1;
124a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey    private static final int MATCH_ROOT = 2;
125a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey    private static final int MATCH_RECENT = 3;
1263e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey    private static final int MATCH_SEARCH = 4;
1273e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey    private static final int MATCH_DOCUMENT = 5;
1283e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey    private static final int MATCH_CHILDREN = 6;
12921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    private static final int MATCH_DOCUMENT_VIA = 7;
13021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    private static final int MATCH_CHILDREN_VIA = 8;
131aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
132aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    private String mAuthority;
133aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
134aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    private UriMatcher mMatcher;
135aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
136ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
137ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class.
138ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
139aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
140aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public void attachInfo(Context context, ProviderInfo info) {
141aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        mAuthority = info.authority;
142aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
143aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
144a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey        mMatcher.addURI(mAuthority, "root", MATCH_ROOTS);
145a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey        mMatcher.addURI(mAuthority, "root/*", MATCH_ROOT);
146ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT);
1473e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey        mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH);
148ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
149ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
15021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        mMatcher.addURI(mAuthority, "via/*/document/*", MATCH_DOCUMENT_VIA);
15121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        mMatcher.addURI(mAuthority, "via/*/document/*/children", MATCH_CHILDREN_VIA);
152aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
153aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        // Sanity check our setup
154aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (!info.exported) {
155aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throw new SecurityException("Provider must be exported");
156aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
157aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (!info.grantUriPermissions) {
158aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throw new SecurityException("Provider must grantUriPermissions");
159aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
160aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (!android.Manifest.permission.MANAGE_DOCUMENTS.equals(info.readPermission)
161aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                || !android.Manifest.permission.MANAGE_DOCUMENTS.equals(info.writePermission)) {
162aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throw new SecurityException("Provider must be protected by MANAGE_DOCUMENTS");
163aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
164aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
165aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        super.attachInfo(context, info);
166aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
167aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
168aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
16921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * Test if a document is descendant (child, grandchild, etc) from the given
17021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * parent. Providers must override this to support directory selection. You
17121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * should avoid making network requests to keep this request fast.
17221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     *
17321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @param parentDocumentId parent to verify against.
17421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @param documentId child to verify.
17521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @return if given document is a descendant of the given parent.
17621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @see DocumentsContract.Root#FLAG_SUPPORTS_DIR_SELECTION
17721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     */
17821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    public boolean isChildDocument(String parentDocumentId, String documentId) {
17921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        return false;
18021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
18121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
18221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /** {@hide} */
18321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    private void enforceVia(Uri documentUri) {
18421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        if (DocumentsContract.isViaUri(documentUri)) {
18521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            final String parent = DocumentsContract.getViaDocumentId(documentUri);
18621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            final String child = DocumentsContract.getDocumentId(documentUri);
18721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            if (Objects.equals(parent, child)) {
18821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                return;
18921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            }
19021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            if (!isChildDocument(parent, child)) {
19121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                throw new SecurityException(
19221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                        "Document " + child + " is not a descendant of " + parent);
19321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            }
19421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        }
19521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
19621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
19721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /**
198e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Create a new document and return its newly generated
1999352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link Document#COLUMN_DOCUMENT_ID}. You must allocate a new
200e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * {@link Document#COLUMN_DOCUMENT_ID} to represent the document, which must
201e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * not change once returned.
202aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
203e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param parentDocumentId the parent directory to create the new document
204e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            under.
205e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param mimeType the concrete MIME type associated with the new document.
206e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            If the MIME type is not supported, the provider must throw.
207e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param displayName the display name of the new document. The provider may
208e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            alter this name to meet any internal constraints, such as
209e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            conflicting names.
210aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
211aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @SuppressWarnings("unused")
212e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey    public String createDocument(String parentDocumentId, String mimeType, String displayName)
213aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throws FileNotFoundException {
214aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Create not supported");
215aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
216aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
217aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
218e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Delete the requested document. Upon returning, any URI permission grants
21921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * for the given document will be revoked. If additional documents were
22021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * deleted as a side effect of this call (such as documents inside a
22121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * directory) the implementor is responsible for revoking those permissions
22221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * using {@link #revokeDocumentPermission(String)}.
223aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
224ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @param documentId the document to delete.
225aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
226aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @SuppressWarnings("unused")
227ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public void deleteDocument(String documentId) throws FileNotFoundException {
228ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        throw new UnsupportedOperationException("Delete not supported");
229aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
230aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
231e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey    /**
2329352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * Return all roots currently provided. To display to users, you must define
2339352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * at least one root. You should avoid making network requests to keep this
2349352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * request fast.
235e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
236e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Each root is defined by the metadata columns described in {@link Root},
237e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * including {@link Root#COLUMN_DOCUMENT_ID} which points to a directory
238e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * representing a tree of documents to display under that root.
239e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
240e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * If this set of roots changes, you must call {@link ContentResolver#notifyChange(Uri,
2419352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * android.database.ContentObserver, boolean)} with
2429352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link DocumentsContract#buildRootsUri(String)} to notify the system.
243e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *
244e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param projection list of {@link Root} columns to put into the cursor. If
245e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            {@code null} all supported columns should be included.
246e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     */
247ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public abstract Cursor queryRoots(String[] projection) throws FileNotFoundException;
248ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey
249e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey    /**
250e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Return recently modified documents under the requested root. This will
251e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * only be called for roots that advertise
252e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * {@link Root#FLAG_SUPPORTS_RECENTS}. The returned documents should be
253e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * sorted by {@link Document#COLUMN_LAST_MODIFIED} in descending order, and
254e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * limited to only return the 64 most recently modified documents.
25537ed78e504ef3666dd5fce15ff4994f151c44fcdJeff Sharkey     * <p>
25637ed78e504ef3666dd5fce15ff4994f151c44fcdJeff Sharkey     * Recent documents do not support change notifications.
257e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *
258e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param projection list of {@link Document} columns to put into the
259e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            cursor. If {@code null} all supported columns should be
260e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            included.
261e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_LOADING
262e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     */
263aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @SuppressWarnings("unused")
264ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public Cursor queryRecentDocuments(String rootId, String[] projection)
265ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            throws FileNotFoundException {
266ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        throw new UnsupportedOperationException("Recent not supported");
267aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
268aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
269aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
2709352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * Return metadata for the single requested document. You should avoid
2719352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * making network requests to keep this request fast.
272aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
273ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @param documentId the document to return.
274e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param projection list of {@link Document} columns to put into the
275e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            cursor. If {@code null} all supported columns should be
276e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            included.
277aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
278ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public abstract Cursor queryDocument(String documentId, String[] projection)
279ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            throws FileNotFoundException;
280aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
281aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
282e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Return the children documents contained in the requested directory. This
283e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * must only return immediate descendants, as additional queries will be
284e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * issued to recursively explore the tree.
285e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
286e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * If your provider is cloud-based, and you have some data cached or pinned
287e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * locally, you may return the local data immediately, setting
288e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that
2899352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * you are still fetching additional data. Then, when the network data is
2909352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * available, you can send a change notification to trigger a requery and
2913b945405cf96eae8b882f87934222a453718a559Jeff Sharkey     * return the complete contents. To return a Cursor with extras, you need to
2923b945405cf96eae8b882f87934222a453718a559Jeff Sharkey     * extend and override {@link Cursor#getExtras()}.
2939352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * <p>
2949352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * To support change notifications, you must
2959352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant
2969352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * Uri, such as
2979352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link DocumentsContract#buildChildDocumentsUri(String, String)}. Then
2989352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * you can call {@link ContentResolver#notifyChange(Uri,
2999352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * android.database.ContentObserver, boolean)} with that Uri to send change
3009352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * notifications.
301aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
302ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @param parentDocumentId the directory to return children for.
303e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param projection list of {@link Document} columns to put into the
304e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            cursor. If {@code null} all supported columns should be
305e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            included.
306e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param sortOrder how to order the rows, formatted as an SQL
307e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            {@code ORDER BY} clause (excluding the ORDER BY itself).
308e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            Passing {@code null} will use the default sort order, which
309e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            may be unordered. This ordering is a hint that can be used to
310e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            prioritize how data is fetched from the network, but UI may
311e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            always enforce a specific ordering.
312e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_LOADING
313e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_INFO
314e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_ERROR
315aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
316ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public abstract Cursor queryChildDocuments(
317ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            String parentDocumentId, String[] projection, String sortOrder)
318ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            throws FileNotFoundException;
319aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
3204ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    /** {@hide} */
3214ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    @SuppressWarnings("unused")
3224ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    public Cursor queryChildDocumentsForManage(
3234ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey            String parentDocumentId, String[] projection, String sortOrder)
3244ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey            throws FileNotFoundException {
3254ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey        throw new UnsupportedOperationException("Manage not supported");
3264ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    }
3274ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey
328aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
329e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Return documents that that match the given query under the requested
330e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * root. The returned documents should be sorted by relevance in descending
331e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * order. How documents are matched against the query string is an
332e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * implementation detail left to each provider, but it's suggested that at
333e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * least {@link Document#COLUMN_DISPLAY_NAME} be matched in a
334e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * case-insensitive fashion.
335e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
336e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Only documents may be returned; directories are not supported in search
337e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * results.
3389352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * <p>
3399352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * If your provider is cloud-based, and you have some data cached or pinned
3409352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * locally, you may return the local data immediately, setting
3419352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that
3429352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * you are still fetching additional data. Then, when the network data is
3439352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * available, you can send a change notification to trigger a requery and
3449352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * return the complete contents.
3459352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * <p>
3469352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * To support change notifications, you must
3479352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant
3489352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * Uri, such as {@link DocumentsContract#buildSearchDocumentsUri(String,
3499352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * String, String)}. Then you can call {@link ContentResolver#notifyChange(Uri,
3509352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * android.database.ContentObserver, boolean)} with that Uri to send change
3519352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * notifications.
352aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
3533e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey     * @param rootId the root to search under.
354e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param query string to match documents against.
355e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param projection list of {@link Document} columns to put into the
356e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            cursor. If {@code null} all supported columns should be
357e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            included.
358e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_LOADING
359e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_INFO
360e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_ERROR
361aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
362aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @SuppressWarnings("unused")
3633e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey    public Cursor querySearchDocuments(String rootId, String query, String[] projection)
364ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            throws FileNotFoundException {
365aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Search not supported");
366aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
367aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
368aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
369e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Return concrete MIME type of the requested document. Must match the value
370e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * of {@link Document#COLUMN_MIME_TYPE} for this document. The default
371e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * implementation queries {@link #queryDocument(String, String[])}, so
372e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * providers may choose to override this as an optimization.
373aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
374ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public String getDocumentType(String documentId) throws FileNotFoundException {
375ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        final Cursor cursor = queryDocument(documentId, null);
376aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        try {
377aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            if (cursor.moveToFirst()) {
378ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                return cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE));
379aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            } else {
380aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                return null;
381aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            }
382aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } finally {
383aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            IoUtils.closeQuietly(cursor);
384aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
385aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
386aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
387aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
388e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Open and return the requested document.
389e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
3909352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * Your provider should return a reliable {@link ParcelFileDescriptor} to
391e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * detect when the remote caller has finished reading or writing the
3929352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * document. You may return a pipe or socket pair if the mode is exclusively
3939352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * "r" or "w", but complex modes like "rw" imply a normal file on disk that
3949352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * supports seeking.
395e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
3969352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * If you block while downloading content, you should periodically check
3979352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link CancellationSignal#isCanceled()} to abort abandoned open requests.
398aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
399e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param documentId the document to return.
400aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @param mode the mode to open with, such as 'r', 'w', or 'rw'.
401aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @param signal used by the caller to signal if the request should be
4023b945405cf96eae8b882f87934222a453718a559Jeff Sharkey     *            cancelled. May be null.
403aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @see ParcelFileDescriptor#open(java.io.File, int, android.os.Handler,
404aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *      OnCloseListener)
405aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @see ParcelFileDescriptor#createReliablePipe()
406aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @see ParcelFileDescriptor#createReliableSocketPair()
407e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see ParcelFileDescriptor#parseMode(String)
408aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
409aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public abstract ParcelFileDescriptor openDocument(
410e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey            String documentId, String mode, CancellationSignal signal) throws FileNotFoundException;
411aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
412aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
413e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Open and return a thumbnail of the requested document.
414e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
415e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * A provider should return a thumbnail closely matching the hinted size,
416e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * attempting to serve from a local cache if possible. A provider should
417e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * never return images more than double the hinted size.
418e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
4199352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * If you perform expensive operations to download or generate a thumbnail,
4209352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * you should periodically check {@link CancellationSignal#isCanceled()} to
4219352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * abort abandoned thumbnail requests.
422aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
423e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param documentId the document to return.
424aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @param sizeHint hint of the optimal thumbnail dimensions.
425aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @param signal used by the caller to signal if the request should be
4263b945405cf96eae8b882f87934222a453718a559Jeff Sharkey     *            cancelled. May be null.
427ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see Document#FLAG_SUPPORTS_THUMBNAIL
428aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
429aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @SuppressWarnings("unused")
430aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public AssetFileDescriptor openDocumentThumbnail(
431e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey            String documentId, Point sizeHint, CancellationSignal signal)
432e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey            throws FileNotFoundException {
433aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Thumbnails not supported");
434aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
435aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
436ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
437ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
438ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
439ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #queryRoots(String[])
440ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #queryRecentDocuments(String, String[])
441ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #queryDocument(String, String[])
442ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #queryChildDocuments(String, String[], String)
443ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #querySearchDocuments(String, String, String[])
444ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
445aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
446ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public final Cursor query(Uri uri, String[] projection, String selection,
447ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            String[] selectionArgs, String sortOrder) {
448aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        try {
449aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            switch (mMatcher.match(uri)) {
450a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey                case MATCH_ROOTS:
451ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    return queryRoots(projection);
452ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                case MATCH_RECENT:
453ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    return queryRecentDocuments(getRootId(uri), projection);
4543e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey                case MATCH_SEARCH:
4553e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey                    return querySearchDocuments(
4563e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey                            getRootId(uri), getSearchDocumentsQuery(uri), projection);
457aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                case MATCH_DOCUMENT:
45821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                case MATCH_DOCUMENT_VIA:
45921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                    enforceVia(uri);
460ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    return queryDocument(getDocumentId(uri), projection);
461aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                case MATCH_CHILDREN:
46221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                case MATCH_CHILDREN_VIA:
46321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                    enforceVia(uri);
4644ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                    if (DocumentsContract.isManageMode(uri)) {
4654ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                        return queryChildDocumentsForManage(
4664ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                                getDocumentId(uri), projection, sortOrder);
4674ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                    } else {
4684ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                        return queryChildDocuments(getDocumentId(uri), projection, sortOrder);
4694ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                    }
470aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                default:
471aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                    throw new UnsupportedOperationException("Unsupported Uri " + uri);
472aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            }
473aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } catch (FileNotFoundException e) {
474aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            Log.w(TAG, "Failed during query", e);
475aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            return null;
476aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
477aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
478aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
479ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
480ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
481ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
482ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #getDocumentType(String)
483ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
484aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
485aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final String getType(Uri uri) {
486aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        try {
487aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            switch (mMatcher.match(uri)) {
488a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey                case MATCH_ROOT:
489a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey                    return DocumentsContract.Root.MIME_TYPE_ITEM;
490aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                case MATCH_DOCUMENT:
49121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                case MATCH_DOCUMENT_VIA:
49221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                    enforceVia(uri);
493ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    return getDocumentType(getDocumentId(uri));
494aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                default:
495aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                    return null;
496aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            }
497aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } catch (FileNotFoundException e) {
498aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            Log.w(TAG, "Failed during getType", e);
499aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            return null;
500aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
501aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
502aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
503ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
50421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * Implementation is provided by the parent class. Can be overridden to
50521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * provide additional functionality, but subclasses <em>must</em> always
50621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * call the superclass. If the superclass returns {@code null}, the subclass
50721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * may implement custom behavior.
50821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * <p>
50921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * This is typically used to resolve a "via" URI into a concrete document
51021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * reference, issuing a narrower single-document URI permission grant along
51121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * the way.
51221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     *
51321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @see DocumentsContract#buildDocumentViaUri(Uri, String)
51421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     */
51521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    @Override
51621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    public Uri canonicalize(Uri uri) {
51721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        final Context context = getContext();
51821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        switch (mMatcher.match(uri)) {
51921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            case MATCH_DOCUMENT_VIA:
52021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                enforceVia(uri);
52121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
52221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                final Uri narrowUri = DocumentsContract.buildDocumentUri(uri.getAuthority(),
52321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                        DocumentsContract.getDocumentId(uri));
52421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
52521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                // Caller may only have prefix grant, so extend them a grant to
52621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                // the narrow Uri. Caller already holds read grant to get here,
52721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                // so check for any other modes we should extend.
52821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                int modeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION;
52921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                if (context.checkCallingOrSelfUriPermission(uri,
53021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
53121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                        == PackageManager.PERMISSION_GRANTED) {
53221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                    modeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
53321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                }
53421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                if (context.checkCallingOrSelfUriPermission(uri,
53521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                        Intent.FLAG_GRANT_READ_URI_PERMISSION
53621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                        | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
53721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                        == PackageManager.PERMISSION_GRANTED) {
53821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                    modeFlags |= Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
53921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                }
54021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                context.grantUriPermission(getCallingPackage(), narrowUri, modeFlags);
54121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                return narrowUri;
54221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        }
54321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        return null;
54421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
54521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
54621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /**
547ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class. Throws by default, and
548ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * cannot be overriden.
549ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
550ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #createDocument(String, String, String)
551ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
552aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
553aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final Uri insert(Uri uri, ContentValues values) {
554aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Insert not supported");
555aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
556aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
557ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
558ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class. Throws by default, and
559ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * cannot be overriden.
560ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
561ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #deleteDocument(String)
562ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
563aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
564aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final int delete(Uri uri, String selection, String[] selectionArgs) {
565aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Delete not supported");
566aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
567aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
568ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
569ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class. Throws by default, and
570ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * cannot be overriden.
571ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
572aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
573aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final int update(
574aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            Uri uri, ContentValues values, String selection, String[] selectionArgs) {
575aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Update not supported");
576aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
577aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
578911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey    /**
579911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     * Implementation is provided by the parent class. Can be overridden to
580911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     * provide additional functionality, but subclasses <em>must</em> always
581911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     * call the superclass. If the superclass returns {@code null}, the subclass
582911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     * may implement custom behavior.
583911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     */
584aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
585911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey    public Bundle call(String method, String arg, Bundle extras) {
586aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (!method.startsWith("android:")) {
58721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            // Ignore non-platform methods
588911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey            return super.call(method, arg, extras);
589aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
590aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
59121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
59221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        final String authority = documentUri.getAuthority();
59321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        final String documentId = DocumentsContract.getDocumentId(documentUri);
594e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey
59521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        if (!mAuthority.equals(authority)) {
59621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            throw new SecurityException(
59721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                    "Requested authority " + authority + " doesn't match provider " + mAuthority);
59821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        }
59921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        enforceVia(documentUri);
600aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
601aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        final Bundle out = new Bundle();
602aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        try {
603ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            if (METHOD_CREATE_DOCUMENT.equals(method)) {
60421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                enforceWritePermissionInner(documentUri);
60521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
606ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
607ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
608aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
609ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                final String newDocumentId = createDocument(documentId, mimeType, displayName);
61021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
61121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                // No need to issue new grants here, since caller either has
61221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                // manage permission or a prefix grant. We might generate a
61321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                // "via" style URI if that's how they called us.
61421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                final Uri newDocumentUri = DocumentsContract.buildDocumentMaybeViaUri(documentUri,
61521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                        newDocumentId);
61621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
617e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey
618aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            } else if (METHOD_DELETE_DOCUMENT.equals(method)) {
61921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                enforceWritePermissionInner(documentUri);
620e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                deleteDocument(documentId);
621e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey
622e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                // Document no longer exists, clean up any grants
62321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                revokeDocumentPermission(documentId);
624aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
625aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            } else {
626aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                throw new UnsupportedOperationException("Method not supported " + method);
627aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            }
628aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } catch (FileNotFoundException e) {
629aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throw new IllegalStateException("Failed call " + method, e);
630aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
631aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        return out;
632aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
633aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
634ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
63521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * Revoke any active permission grants for the given
63621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * {@link Document#COLUMN_DOCUMENT_ID}, usually called when a document
63721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * becomes invalid. Follows the same semantics as
63821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * {@link Context#revokeUriPermission(Uri, int)}.
63921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     */
64021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    public final void revokeDocumentPermission(String documentId) {
64121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        final Context context = getContext();
64221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        context.revokeUriPermission(DocumentsContract.buildDocumentUri(mAuthority, documentId), ~0);
64321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        context.revokeUriPermission(DocumentsContract.buildViaUri(mAuthority, documentId), ~0);
64421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
64521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
64621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /**
647e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
648ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
649ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #openDocument(String, String, CancellationSignal)
650ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
651aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
652aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
65321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        enforceVia(uri);
654ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        return openDocument(getDocumentId(uri), mode, null);
655aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
656aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
657ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
658e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
659ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
660ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #openDocument(String, String, CancellationSignal)
661ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
662aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
663aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal)
664aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throws FileNotFoundException {
66521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        enforceVia(uri);
666ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        return openDocument(getDocumentId(uri), mode, signal);
667aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
668aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
669ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
670e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
671ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
67221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @see #openDocument(String, String, CancellationSignal)
67321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     */
67421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    @Override
67521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    @SuppressWarnings("resource")
67621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    public final AssetFileDescriptor openAssetFile(Uri uri, String mode)
67721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            throws FileNotFoundException {
67821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        enforceVia(uri);
67921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, null);
68021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null;
68121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
68221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
68321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /**
68421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
68521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     *
68621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @see #openDocument(String, String, CancellationSignal)
68721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     */
68821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    @Override
68921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    @SuppressWarnings("resource")
69021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    public final AssetFileDescriptor openAssetFile(Uri uri, String mode, CancellationSignal signal)
69121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            throws FileNotFoundException {
69221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        enforceVia(uri);
69321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, signal);
69421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null;
69521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
69621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
69721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /**
69821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
69921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     *
700ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #openDocumentThumbnail(String, Point, CancellationSignal)
701ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
702aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
703aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
704aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throws FileNotFoundException {
70521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        enforceVia(uri);
706aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
707aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
708ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            return openDocumentThumbnail(getDocumentId(uri), sizeHint, null);
709aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } else {
710aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            return super.openTypedAssetFile(uri, mimeTypeFilter, opts);
711aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
712aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
713aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
714ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
715e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
716ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
717ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #openDocumentThumbnail(String, Point, CancellationSignal)
718ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
719aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
720aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final AssetFileDescriptor openTypedAssetFile(
721aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
722aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throws FileNotFoundException {
72321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        enforceVia(uri);
724aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
725aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
726ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal);
727aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } else {
728aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            return super.openTypedAssetFile(uri, mimeTypeFilter, opts, signal);
729aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
730aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
731aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey}
732