DocumentsProvider.java revision 3b945405cf96eae8b882f87934222a453718a559
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;
49aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
50aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey/**
51e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Base class for a document provider. A document provider offers read and write
52e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * access to durable files, such as files stored on a local disk, or files in a
53e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * cloud storage service. To create a document provider, extend this class,
54e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * implement the abstract methods, and add it to your manifest like this:
55e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *
56e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <pre class="prettyprint">&lt;manifest&gt;
57e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *    ...
58e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *    &lt;application&gt;
59e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *        ...
60e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *        &lt;provider
61e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            android:name="com.example.MyCloudProvider"
62e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            android:authorities="com.example.mycloudprovider"
63e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            android:exported="true"
64e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            android:grantUriPermissions="true"
653b945405cf96eae8b882f87934222a453718a559Jeff Sharkey *            android:permission="android.permission.MANAGE_DOCUMENTS"
663b945405cf96eae8b882f87934222a453718a559Jeff Sharkey *            android:enabled="@bool/isAtLeastKitKat"&gt;
67e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            &lt;intent-filter&gt;
68e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *                &lt;action android:name="android.content.action.DOCUMENTS_PROVIDER" /&gt;
69e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *            &lt;/intent-filter&gt;
70e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *        &lt;/provider&gt;
71e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *        ...
72e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *    &lt;/application&gt;
73e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *&lt;/manifest&gt;</pre>
74aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * <p>
75e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * When defining your provider, you must protect it with
76e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link android.Manifest.permission#MANAGE_DOCUMENTS}, which is a permission
77e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * only the system can obtain. Applications cannot use a documents provider
78e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * directly; they must go through {@link Intent#ACTION_OPEN_DOCUMENT} or
79e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Intent#ACTION_CREATE_DOCUMENT} which requires a user to actively
809352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * navigate and select documents. When a user selects documents through that UI,
819352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * the system issues narrow URI permission grants to the requesting application.
82e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p>
83e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <h3>Documents</h3>
84aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * <p>
85e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * A document can be either an openable stream (with a specific MIME type), or a
86aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * directory containing additional documents (with the
87e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Document#MIME_TYPE_DIR} MIME type). Each directory represents the top
88e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * of a subtree containing zero or more documents, which can recursively contain
89e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * even more documents and directories.
90e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p>
91e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p>
92e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Each document can have different capabilities, as described by
93e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Document#COLUMN_FLAGS}. For example, if a document can be represented
949352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * as a thumbnail, your provider can set
959352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * {@link Document#FLAG_SUPPORTS_THUMBNAIL} and implement
96e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link #openDocumentThumbnail(String, Point, CancellationSignal)} to return
97e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * that thumbnail.
98e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p>
99e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p>
100e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Each document under a provider is uniquely referenced by its
101e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Document#COLUMN_DOCUMENT_ID}, which must not change once returned. A
102e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * single document can be included in multiple directories when responding to
103e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link #queryChildDocuments(String, String[], String)}. For example, a
104e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * provider might surface a single photo in multiple locations: once in a
1059352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * directory of geographic locations, and again in a directory of dates.
106e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p>
107e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <h3>Roots</h3>
108aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * <p>
109e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * All documents are surfaced through one or more "roots." Each root represents
110e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * the top of a document tree that a user can navigate. For example, a root
111e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * could represent an account or a physical storage device. Similar to
112e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * documents, each root can have capabilities expressed through
113e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Root#COLUMN_FLAGS}.
114e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p>
115aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey *
116aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @see Intent#ACTION_OPEN_DOCUMENT
117aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @see Intent#ACTION_CREATE_DOCUMENT
118aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */
119aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeypublic abstract class DocumentsProvider extends ContentProvider {
120aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    private static final String TAG = "DocumentsProvider";
121aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
122a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey    private static final int MATCH_ROOTS = 1;
123a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey    private static final int MATCH_ROOT = 2;
124a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey    private static final int MATCH_RECENT = 3;
1253e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey    private static final int MATCH_SEARCH = 4;
1263e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey    private static final int MATCH_DOCUMENT = 5;
1273e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey    private static final int MATCH_CHILDREN = 6;
128aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
129aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    private String mAuthority;
130aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
131aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    private UriMatcher mMatcher;
132aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
133ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
134ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class.
135ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
136aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
137aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public void attachInfo(Context context, ProviderInfo info) {
138aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        mAuthority = info.authority;
139aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
140aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
141a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey        mMatcher.addURI(mAuthority, "root", MATCH_ROOTS);
142a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey        mMatcher.addURI(mAuthority, "root/*", MATCH_ROOT);
143ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT);
1443e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey        mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH);
145ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
146ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
147aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
148aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        // Sanity check our setup
149aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (!info.exported) {
150aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throw new SecurityException("Provider must be exported");
151aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
152aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (!info.grantUriPermissions) {
153aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throw new SecurityException("Provider must grantUriPermissions");
154aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
155aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (!android.Manifest.permission.MANAGE_DOCUMENTS.equals(info.readPermission)
156aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                || !android.Manifest.permission.MANAGE_DOCUMENTS.equals(info.writePermission)) {
157aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throw new SecurityException("Provider must be protected by MANAGE_DOCUMENTS");
158aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
159aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
160aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        super.attachInfo(context, info);
161aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
162aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
163aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
164e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Create a new document and return its newly generated
1659352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link Document#COLUMN_DOCUMENT_ID}. You must allocate a new
166e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * {@link Document#COLUMN_DOCUMENT_ID} to represent the document, which must
167e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * not change once returned.
168aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
169e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param parentDocumentId the parent directory to create the new document
170e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            under.
171e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param mimeType the concrete MIME type associated with the new document.
172e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            If the MIME type is not supported, the provider must throw.
173e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param displayName the display name of the new document. The provider may
174e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            alter this name to meet any internal constraints, such as
175e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            conflicting names.
176aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
177aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @SuppressWarnings("unused")
178e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey    public String createDocument(String parentDocumentId, String mimeType, String displayName)
179aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throws FileNotFoundException {
180aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Create not supported");
181aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
182aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
183aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
184e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Delete the requested document. Upon returning, any URI permission grants
185e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * for the requested document will be revoked. If additional documents were
186e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * deleted as a side effect of this call, such as documents inside a
187e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * directory, the implementor is responsible for revoking those permissions.
188aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
189ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @param documentId the document to delete.
190aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
191aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @SuppressWarnings("unused")
192ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public void deleteDocument(String documentId) throws FileNotFoundException {
193ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        throw new UnsupportedOperationException("Delete not supported");
194aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
195aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
196e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey    /**
1979352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * Return all roots currently provided. To display to users, you must define
1989352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * at least one root. You should avoid making network requests to keep this
1999352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * request fast.
200e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
201e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Each root is defined by the metadata columns described in {@link Root},
202e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * including {@link Root#COLUMN_DOCUMENT_ID} which points to a directory
203e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * representing a tree of documents to display under that root.
204e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
205e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * If this set of roots changes, you must call {@link ContentResolver#notifyChange(Uri,
2069352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * android.database.ContentObserver, boolean)} with
2079352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link DocumentsContract#buildRootsUri(String)} to notify the system.
208e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *
209e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param projection list of {@link Root} columns to put into the cursor. If
210e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            {@code null} all supported columns should be included.
211e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     */
212ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public abstract Cursor queryRoots(String[] projection) throws FileNotFoundException;
213ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey
214e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey    /**
215e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Return recently modified documents under the requested root. This will
216e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * only be called for roots that advertise
217e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * {@link Root#FLAG_SUPPORTS_RECENTS}. The returned documents should be
218e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * sorted by {@link Document#COLUMN_LAST_MODIFIED} in descending order, and
219e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * limited to only return the 64 most recently modified documents.
22037ed78e504ef3666dd5fce15ff4994f151c44fcdJeff Sharkey     * <p>
22137ed78e504ef3666dd5fce15ff4994f151c44fcdJeff Sharkey     * Recent documents do not support change notifications.
222e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *
223e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param projection list of {@link Document} columns to put into the
224e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            cursor. If {@code null} all supported columns should be
225e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            included.
226e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_LOADING
227e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     */
228aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @SuppressWarnings("unused")
229ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public Cursor queryRecentDocuments(String rootId, String[] projection)
230ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            throws FileNotFoundException {
231ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        throw new UnsupportedOperationException("Recent not supported");
232aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
233aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
234aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
2359352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * Return metadata for the single requested document. You should avoid
2369352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * making network requests to keep this request fast.
237aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
238ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @param documentId the document to return.
239e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param projection list of {@link Document} columns to put into the
240e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            cursor. If {@code null} all supported columns should be
241e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            included.
242aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
243ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public abstract Cursor queryDocument(String documentId, String[] projection)
244ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            throws FileNotFoundException;
245aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
246aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
247e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Return the children documents contained in the requested directory. This
248e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * must only return immediate descendants, as additional queries will be
249e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * issued to recursively explore the tree.
250e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
251e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * If your provider is cloud-based, and you have some data cached or pinned
252e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * locally, you may return the local data immediately, setting
253e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that
2549352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * you are still fetching additional data. Then, when the network data is
2559352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * available, you can send a change notification to trigger a requery and
2563b945405cf96eae8b882f87934222a453718a559Jeff Sharkey     * return the complete contents. To return a Cursor with extras, you need to
2573b945405cf96eae8b882f87934222a453718a559Jeff Sharkey     * extend and override {@link Cursor#getExtras()}.
2589352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * <p>
2599352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * To support change notifications, you must
2609352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant
2619352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * Uri, such as
2629352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link DocumentsContract#buildChildDocumentsUri(String, String)}. Then
2639352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * you can call {@link ContentResolver#notifyChange(Uri,
2649352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * android.database.ContentObserver, boolean)} with that Uri to send change
2659352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * notifications.
266aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
267ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @param parentDocumentId the directory to return children for.
268e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param projection list of {@link Document} columns to put into the
269e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            cursor. If {@code null} all supported columns should be
270e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            included.
271e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param sortOrder how to order the rows, formatted as an SQL
272e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            {@code ORDER BY} clause (excluding the ORDER BY itself).
273e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            Passing {@code null} will use the default sort order, which
274e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            may be unordered. This ordering is a hint that can be used to
275e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            prioritize how data is fetched from the network, but UI may
276e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            always enforce a specific ordering.
277e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_LOADING
278e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_INFO
279e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_ERROR
280aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
281ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public abstract Cursor queryChildDocuments(
282ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            String parentDocumentId, String[] projection, String sortOrder)
283ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            throws FileNotFoundException;
284aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
2854ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    /** {@hide} */
2864ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    @SuppressWarnings("unused")
2874ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    public Cursor queryChildDocumentsForManage(
2884ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey            String parentDocumentId, String[] projection, String sortOrder)
2894ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey            throws FileNotFoundException {
2904ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey        throw new UnsupportedOperationException("Manage not supported");
2914ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    }
2924ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey
293aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
294e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Return documents that that match the given query under the requested
295e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * root. The returned documents should be sorted by relevance in descending
296e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * order. How documents are matched against the query string is an
297e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * implementation detail left to each provider, but it's suggested that at
298e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * least {@link Document#COLUMN_DISPLAY_NAME} be matched in a
299e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * case-insensitive fashion.
300e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
301e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Only documents may be returned; directories are not supported in search
302e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * results.
3039352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * <p>
3049352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * If your provider is cloud-based, and you have some data cached or pinned
3059352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * locally, you may return the local data immediately, setting
3069352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that
3079352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * you are still fetching additional data. Then, when the network data is
3089352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * available, you can send a change notification to trigger a requery and
3099352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * return the complete contents.
3109352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * <p>
3119352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * To support change notifications, you must
3129352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant
3139352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * Uri, such as {@link DocumentsContract#buildSearchDocumentsUri(String,
3149352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * String, String)}. Then you can call {@link ContentResolver#notifyChange(Uri,
3159352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * android.database.ContentObserver, boolean)} with that Uri to send change
3169352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * notifications.
317aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
3183e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey     * @param rootId the root to search under.
319e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param query string to match documents against.
320e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param projection list of {@link Document} columns to put into the
321e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            cursor. If {@code null} all supported columns should be
322e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     *            included.
323e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_LOADING
324e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_INFO
325e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see DocumentsContract#EXTRA_ERROR
326aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
327aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @SuppressWarnings("unused")
3283e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey    public Cursor querySearchDocuments(String rootId, String query, String[] projection)
329ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            throws FileNotFoundException {
330aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Search not supported");
331aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
332aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
333aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
334e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Return concrete MIME type of the requested document. Must match the value
335e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * of {@link Document#COLUMN_MIME_TYPE} for this document. The default
336e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * implementation queries {@link #queryDocument(String, String[])}, so
337e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * providers may choose to override this as an optimization.
338aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
339ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public String getDocumentType(String documentId) throws FileNotFoundException {
340ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        final Cursor cursor = queryDocument(documentId, null);
341aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        try {
342aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            if (cursor.moveToFirst()) {
343ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                return cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE));
344aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            } else {
345aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                return null;
346aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            }
347aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } finally {
348aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            IoUtils.closeQuietly(cursor);
349aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
350aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
351aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
352aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
353e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Open and return the requested document.
354e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
3559352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * Your provider should return a reliable {@link ParcelFileDescriptor} to
356e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * detect when the remote caller has finished reading or writing the
3579352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * document. You may return a pipe or socket pair if the mode is exclusively
3589352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * "r" or "w", but complex modes like "rw" imply a normal file on disk that
3599352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * supports seeking.
360e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
3619352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * If you block while downloading content, you should periodically check
3629352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * {@link CancellationSignal#isCanceled()} to abort abandoned open requests.
363aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
364e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param documentId the document to return.
365aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @param mode the mode to open with, such as 'r', 'w', or 'rw'.
366aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @param signal used by the caller to signal if the request should be
3673b945405cf96eae8b882f87934222a453718a559Jeff Sharkey     *            cancelled. May be null.
368aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @see ParcelFileDescriptor#open(java.io.File, int, android.os.Handler,
369aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *      OnCloseListener)
370aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @see ParcelFileDescriptor#createReliablePipe()
371aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @see ParcelFileDescriptor#createReliableSocketPair()
372e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @see ParcelFileDescriptor#parseMode(String)
373aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
374aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public abstract ParcelFileDescriptor openDocument(
375e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey            String documentId, String mode, CancellationSignal signal) throws FileNotFoundException;
376aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
377aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    /**
378e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Open and return a thumbnail of the requested document.
379e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
380e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * A provider should return a thumbnail closely matching the hinted size,
381e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * attempting to serve from a local cache if possible. A provider should
382e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * never return images more than double the hinted size.
383e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * <p>
3849352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * If you perform expensive operations to download or generate a thumbnail,
3859352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * you should periodically check {@link CancellationSignal#isCanceled()} to
3869352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey     * abort abandoned thumbnail requests.
387aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
388e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param documentId the document to return.
389aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @param sizeHint hint of the optimal thumbnail dimensions.
390aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @param signal used by the caller to signal if the request should be
3913b945405cf96eae8b882f87934222a453718a559Jeff Sharkey     *            cancelled. May be null.
392ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see Document#FLAG_SUPPORTS_THUMBNAIL
393aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     */
394aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @SuppressWarnings("unused")
395aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public AssetFileDescriptor openDocumentThumbnail(
396e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey            String documentId, Point sizeHint, CancellationSignal signal)
397e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey            throws FileNotFoundException {
398aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Thumbnails not supported");
399aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
400aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
401ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
402ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
403ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
404ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #queryRoots(String[])
405ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #queryRecentDocuments(String, String[])
406ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #queryDocument(String, String[])
407ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #queryChildDocuments(String, String[], String)
408ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #querySearchDocuments(String, String, String[])
409ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
410aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
411ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public final Cursor query(Uri uri, String[] projection, String selection,
412ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            String[] selectionArgs, String sortOrder) {
413aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        try {
414aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            switch (mMatcher.match(uri)) {
415a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey                case MATCH_ROOTS:
416ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    return queryRoots(projection);
417ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                case MATCH_RECENT:
418ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    return queryRecentDocuments(getRootId(uri), projection);
4193e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey                case MATCH_SEARCH:
4203e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey                    return querySearchDocuments(
4213e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey                            getRootId(uri), getSearchDocumentsQuery(uri), projection);
422aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                case MATCH_DOCUMENT:
423ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    return queryDocument(getDocumentId(uri), projection);
424aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                case MATCH_CHILDREN:
4254ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                    if (DocumentsContract.isManageMode(uri)) {
4264ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                        return queryChildDocumentsForManage(
4274ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                                getDocumentId(uri), projection, sortOrder);
4284ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                    } else {
4294ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                        return queryChildDocuments(getDocumentId(uri), projection, sortOrder);
4304ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                    }
431aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                default:
432aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                    throw new UnsupportedOperationException("Unsupported Uri " + uri);
433aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            }
434aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } catch (FileNotFoundException e) {
435aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            Log.w(TAG, "Failed during query", e);
436aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            return null;
437aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
438aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
439aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
440ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
441ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
442ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
443ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #getDocumentType(String)
444ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
445aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
446aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final String getType(Uri uri) {
447aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        try {
448aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            switch (mMatcher.match(uri)) {
449a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey                case MATCH_ROOT:
450a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey                    return DocumentsContract.Root.MIME_TYPE_ITEM;
451aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                case MATCH_DOCUMENT:
452ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    return getDocumentType(getDocumentId(uri));
453aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                default:
454aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                    return null;
455aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            }
456aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } catch (FileNotFoundException e) {
457aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            Log.w(TAG, "Failed during getType", e);
458aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            return null;
459aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
460aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
461aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
462ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
463ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class. Throws by default, and
464ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * cannot be overriden.
465ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
466ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #createDocument(String, String, String)
467ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
468aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
469aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final Uri insert(Uri uri, ContentValues values) {
470aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Insert not supported");
471aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
472aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
473ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
474ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class. Throws by default, and
475ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * cannot be overriden.
476ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
477ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #deleteDocument(String)
478ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
479aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
480aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final int delete(Uri uri, String selection, String[] selectionArgs) {
481aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Delete not supported");
482aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
483aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
484ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
485ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Implementation is provided by the parent class. Throws by default, and
486ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * cannot be overriden.
487ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
488aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
489aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final int update(
490aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            Uri uri, ContentValues values, String selection, String[] selectionArgs) {
491aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        throw new UnsupportedOperationException("Update not supported");
492aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
493aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
494911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey    /**
495911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     * Implementation is provided by the parent class. Can be overridden to
496911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     * provide additional functionality, but subclasses <em>must</em> always
497911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     * call the superclass. If the superclass returns {@code null}, the subclass
498911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     * may implement custom behavior.
499911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     *
500911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     * @see #openDocument(String, String, CancellationSignal)
501911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     * @see #deleteDocument(String)
502911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey     */
503aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
504911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey    public Bundle call(String method, String arg, Bundle extras) {
505e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey        final Context context = getContext();
506e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey
507aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (!method.startsWith("android:")) {
508aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            // Let non-platform methods pass through
509911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey            return super.call(method, arg, extras);
510aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
511aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
512ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        final String documentId = extras.getString(Document.COLUMN_DOCUMENT_ID);
513ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        final Uri documentUri = DocumentsContract.buildDocumentUri(mAuthority, documentId);
514e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey
515e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey        // Require that caller can manage requested document
516e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey        final boolean callerHasManage =
517e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                context.checkCallingOrSelfPermission(android.Manifest.permission.MANAGE_DOCUMENTS)
518e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                == PackageManager.PERMISSION_GRANTED;
5198a2998eade93032a78d681c66ebadbfa6f802f76Jeff Sharkey        enforceWritePermissionInner(documentUri);
520aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
521aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        final Bundle out = new Bundle();
522aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        try {
523ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            if (METHOD_CREATE_DOCUMENT.equals(method)) {
524ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
525ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
526aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
527ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                final String newDocumentId = createDocument(documentId, mimeType, displayName);
528ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                out.putString(Document.COLUMN_DOCUMENT_ID, newDocumentId);
529aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
530e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                // Extend permission grant towards caller if needed
531e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                if (!callerHasManage) {
532e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                    final Uri newDocumentUri = DocumentsContract.buildDocumentUri(
533e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                            mAuthority, newDocumentId);
534911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey                    context.grantUriPermission(getCallingPackage(), newDocumentUri,
535e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                            Intent.FLAG_GRANT_READ_URI_PERMISSION
536e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
537e66c1778f80f4b18e29e018eca3a338f125f23b9Jeff Sharkey                            | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
538e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                }
539e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey
540aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            } else if (METHOD_DELETE_DOCUMENT.equals(method)) {
541e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                deleteDocument(documentId);
542e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey
543e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                // Document no longer exists, clean up any grants
544e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                context.revokeUriPermission(documentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
545e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
546e66c1778f80f4b18e29e018eca3a338f125f23b9Jeff Sharkey                        | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
547aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
548aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            } else {
549aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                throw new UnsupportedOperationException("Method not supported " + method);
550aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            }
551aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } catch (FileNotFoundException e) {
552aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throw new IllegalStateException("Failed call " + method, e);
553aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
554aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        return out;
555aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
556aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
557ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
558e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
559ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
560ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #openDocument(String, String, CancellationSignal)
561ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
562aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
563aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
564ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        return openDocument(getDocumentId(uri), mode, null);
565aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
566aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
567ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
568e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
569ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
570ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #openDocument(String, String, CancellationSignal)
571ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
572aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
573aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal)
574aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throws FileNotFoundException {
575ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        return openDocument(getDocumentId(uri), mode, signal);
576aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
577aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
578ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
579e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
580ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
581ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #openDocumentThumbnail(String, Point, CancellationSignal)
582ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
583aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
584aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
585aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throws FileNotFoundException {
586aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
587aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
588ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            return openDocumentThumbnail(getDocumentId(uri), sizeHint, null);
589aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } else {
590aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            return super.openTypedAssetFile(uri, mimeTypeFilter, opts);
591aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
592aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
593aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
594ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
595e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Implementation is provided by the parent class. Cannot be overriden.
596ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
597ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #openDocumentThumbnail(String, Point, CancellationSignal)
598ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
599aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    @Override
600aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    public final AssetFileDescriptor openTypedAssetFile(
601aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
602aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            throws FileNotFoundException {
603aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
604aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
605ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal);
606aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        } else {
607aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey            return super.openTypedAssetFile(uri, mimeTypeFilter, opts, signal);
608aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
609aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
610aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey}
611