DocumentsProvider.java revision b9fbb7290b02de1ce621deaa2d28a5e42f2e0937
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; 22b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkeyimport static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT; 23b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkeyimport static android.provider.DocumentsContract.buildDocumentUri; 24b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkeyimport static android.provider.DocumentsContract.buildDocumentUriMaybeUsingTree; 25b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkeyimport static android.provider.DocumentsContract.buildTreeDocumentUri; 26ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkeyimport static android.provider.DocumentsContract.getDocumentId; 27ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkeyimport static android.provider.DocumentsContract.getRootId; 28ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkeyimport static android.provider.DocumentsContract.getSearchDocumentsQuery; 29b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkeyimport static android.provider.DocumentsContract.getTreeDocumentId; 30b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkeyimport static android.provider.DocumentsContract.isTreeUri; 31aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 32aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.ContentProvider; 33e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkeyimport android.content.ContentResolver; 34aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.ContentValues; 35aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.Context; 36aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.Intent; 37aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.UriMatcher; 38e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkeyimport android.content.pm.PackageManager; 39aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.pm.ProviderInfo; 40aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.content.res.AssetFileDescriptor; 41aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.database.Cursor; 42aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.graphics.Point; 43aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.net.Uri; 44aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.os.Bundle; 45aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.os.CancellationSignal; 46aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.os.ParcelFileDescriptor; 47aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.os.ParcelFileDescriptor.OnCloseListener; 48ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkeyimport android.provider.DocumentsContract.Document; 49e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkeyimport android.provider.DocumentsContract.Root; 50aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport android.util.Log; 51aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 52aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport libcore.io.IoUtils; 53aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 54aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport java.io.FileNotFoundException; 5521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkeyimport java.util.Objects; 56aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 57aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey/** 58e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Base class for a document provider. A document provider offers read and write 59e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * access to durable files, such as files stored on a local disk, or files in a 60e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * cloud storage service. To create a document provider, extend this class, 61e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * implement the abstract methods, and add it to your manifest like this: 62e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * 63e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <pre class="prettyprint"><manifest> 64e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * ... 65e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <application> 66e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * ... 67e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <provider 68e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * android:name="com.example.MyCloudProvider" 69e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * android:authorities="com.example.mycloudprovider" 70e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * android:exported="true" 71e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * android:grantUriPermissions="true" 723b945405cf96eae8b882f87934222a453718a559Jeff Sharkey * android:permission="android.permission.MANAGE_DOCUMENTS" 733b945405cf96eae8b882f87934222a453718a559Jeff Sharkey * android:enabled="@bool/isAtLeastKitKat"> 74e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <intent-filter> 75e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> 76e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </intent-filter> 77e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </provider> 78e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * ... 79e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </application> 80e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey *</manifest></pre> 81aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * <p> 82e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * When defining your provider, you must protect it with 83e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link android.Manifest.permission#MANAGE_DOCUMENTS}, which is a permission 84e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * only the system can obtain. Applications cannot use a documents provider 85e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * directly; they must go through {@link Intent#ACTION_OPEN_DOCUMENT} or 86e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Intent#ACTION_CREATE_DOCUMENT} which requires a user to actively 879352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * navigate and select documents. When a user selects documents through that UI, 889352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * the system issues narrow URI permission grants to the requesting application. 89e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p> 90e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <h3>Documents</h3> 91aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * <p> 92e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * A document can be either an openable stream (with a specific MIME type), or a 93aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * directory containing additional documents (with the 94e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Document#MIME_TYPE_DIR} MIME type). Each directory represents the top 95e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * of a subtree containing zero or more documents, which can recursively contain 96e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * even more documents and directories. 97e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p> 98e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p> 99e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Each document can have different capabilities, as described by 100e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Document#COLUMN_FLAGS}. For example, if a document can be represented 1019352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * as a thumbnail, your provider can set 1029352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * {@link Document#FLAG_SUPPORTS_THUMBNAIL} and implement 103e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link #openDocumentThumbnail(String, Point, CancellationSignal)} to return 104e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * that thumbnail. 105e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p> 106e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p> 107e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Each document under a provider is uniquely referenced by its 108e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Document#COLUMN_DOCUMENT_ID}, which must not change once returned. A 109e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * single document can be included in multiple directories when responding to 110e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link #queryChildDocuments(String, String[], String)}. For example, a 111e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * provider might surface a single photo in multiple locations: once in a 1129352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * directory of geographic locations, and again in a directory of dates. 113e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p> 114e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <h3>Roots</h3> 115aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * <p> 116e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * All documents are surfaced through one or more "roots." Each root represents 117e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * the top of a document tree that a user can navigate. For example, a root 118e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * could represent an account or a physical storage device. Similar to 119e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * documents, each root can have capabilities expressed through 120e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Root#COLUMN_FLAGS}. 121e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * </p> 122aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * 123aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @see Intent#ACTION_OPEN_DOCUMENT 124b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey * @see Intent#ACTION_OPEN_DOCUMENT_TREE 125aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @see Intent#ACTION_CREATE_DOCUMENT 126aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */ 127aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeypublic abstract class DocumentsProvider extends ContentProvider { 128aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey private static final String TAG = "DocumentsProvider"; 129aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 130a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey private static final int MATCH_ROOTS = 1; 131a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey private static final int MATCH_ROOT = 2; 132a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey private static final int MATCH_RECENT = 3; 1333e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey private static final int MATCH_SEARCH = 4; 1343e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey private static final int MATCH_DOCUMENT = 5; 1353e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey private static final int MATCH_CHILDREN = 6; 136b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey private static final int MATCH_DOCUMENT_TREE = 7; 137b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey private static final int MATCH_CHILDREN_TREE = 8; 138aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 139aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey private String mAuthority; 140aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 141aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey private UriMatcher mMatcher; 142aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 143ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey /** 144ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * Implementation is provided by the parent class. 145ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey */ 146aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @Override 147aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey public void attachInfo(Context context, ProviderInfo info) { 148aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey mAuthority = info.authority; 149aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 150aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey mMatcher = new UriMatcher(UriMatcher.NO_MATCH); 151a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey mMatcher.addURI(mAuthority, "root", MATCH_ROOTS); 152a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey mMatcher.addURI(mAuthority, "root/*", MATCH_ROOT); 153ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT); 1543e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH); 155ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT); 156ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN); 157b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey mMatcher.addURI(mAuthority, "tree/*/document/*", MATCH_DOCUMENT_TREE); 158b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey mMatcher.addURI(mAuthority, "tree/*/document/*/children", MATCH_CHILDREN_TREE); 159aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 160aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey // Sanity check our setup 161aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey if (!info.exported) { 162aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new SecurityException("Provider must be exported"); 163aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 164aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey if (!info.grantUriPermissions) { 165aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new SecurityException("Provider must grantUriPermissions"); 166aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 167aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey if (!android.Manifest.permission.MANAGE_DOCUMENTS.equals(info.readPermission) 168aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey || !android.Manifest.permission.MANAGE_DOCUMENTS.equals(info.writePermission)) { 169aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new SecurityException("Provider must be protected by MANAGE_DOCUMENTS"); 170aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 171aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 172aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey super.attachInfo(context, info); 173aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 174aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 175aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey /** 17621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * Test if a document is descendant (child, grandchild, etc) from the given 177b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey * parent. For example, providers must implement this to support 178b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. You should avoid making network 179b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey * requests to keep this request fast. 18021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * 18121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * @param parentDocumentId parent to verify against. 18221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * @param documentId child to verify. 18321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * @return if given document is a descendant of the given parent. 184b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey * @see DocumentsContract.Root#FLAG_SUPPORTS_IS_CHILD 18521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey */ 18621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey public boolean isChildDocument(String parentDocumentId, String documentId) { 18721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey return false; 18821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey } 18921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey 19021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey /** {@hide} */ 191b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey private void enforceTree(Uri documentUri) { 192b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey if (isTreeUri(documentUri)) { 193b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey final String parent = getTreeDocumentId(documentUri); 194b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey final String child = getDocumentId(documentUri); 19521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey if (Objects.equals(parent, child)) { 19621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey return; 19721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey } 19821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey if (!isChildDocument(parent, child)) { 19921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey throw new SecurityException( 20021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey "Document " + child + " is not a descendant of " + parent); 20121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey } 20221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey } 20321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey } 20421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey 20521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey /** 206e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Create a new document and return its newly generated 2079352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * {@link Document#COLUMN_DOCUMENT_ID}. You must allocate a new 208e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Document#COLUMN_DOCUMENT_ID} to represent the document, which must 209e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * not change once returned. 210aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * 211e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param parentDocumentId the parent directory to create the new document 212e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * under. 213e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param mimeType the concrete MIME type associated with the new document. 214e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * If the MIME type is not supported, the provider must throw. 215e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param displayName the display name of the new document. The provider may 216e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * alter this name to meet any internal constraints, such as 217b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * avoiding conflicting names. 218aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */ 219aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @SuppressWarnings("unused") 220e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey public String createDocument(String parentDocumentId, String mimeType, String displayName) 221aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throws FileNotFoundException { 222aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new UnsupportedOperationException("Create not supported"); 223aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 224aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 225aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey /** 226b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * Rename an existing document. 227b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * <p> 228b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * If a different {@link Document#COLUMN_DOCUMENT_ID} must be used to 229b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * represent the renamed document, generate and return it. Any outstanding 230b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * URI permission grants will be updated to point at the new document. If 231b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * the original {@link Document#COLUMN_DOCUMENT_ID} is still valid after the 232b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * rename, return {@code null}. 233b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * 234b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * @param documentId the document to rename. 235b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * @param displayName the updated display name of the document. The provider 236b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * may alter this name to meet any internal constraints, such as 237b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * avoiding conflicting names. 238b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey */ 239b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey @SuppressWarnings("unused") 240b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey public String renameDocument(String documentId, String displayName) 241b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey throws FileNotFoundException { 242b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey throw new UnsupportedOperationException("Rename not supported"); 243b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey } 244b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey 245b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey /** 246b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * Delete the requested document. 247b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * <p> 248b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * Upon returning, any URI permission grants for the given document will be 249b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * revoked. If additional documents were deleted as a side effect of this 250b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * call (such as documents inside a directory) the implementor is 251b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * responsible for revoking those permissions using 252b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey * {@link #revokeDocumentPermission(String)}. 253aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * 254ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @param documentId the document to delete. 255aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */ 256aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @SuppressWarnings("unused") 257ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey public void deleteDocument(String documentId) throws FileNotFoundException { 258ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey throw new UnsupportedOperationException("Delete not supported"); 259aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 260aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 261e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey /** 2629352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * Return all roots currently provided. To display to users, you must define 2639352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * at least one root. You should avoid making network requests to keep this 2649352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * request fast. 265e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p> 266e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Each root is defined by the metadata columns described in {@link Root}, 267e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * including {@link Root#COLUMN_DOCUMENT_ID} which points to a directory 268e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * representing a tree of documents to display under that root. 269e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p> 270e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * If this set of roots changes, you must call {@link ContentResolver#notifyChange(Uri, 2719352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * android.database.ContentObserver, boolean)} with 2729352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * {@link DocumentsContract#buildRootsUri(String)} to notify the system. 273e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * 274e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param projection list of {@link Root} columns to put into the cursor. If 275e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@code null} all supported columns should be included. 276e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey */ 277ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey public abstract Cursor queryRoots(String[] projection) throws FileNotFoundException; 278ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey 279e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey /** 280e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Return recently modified documents under the requested root. This will 281e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * only be called for roots that advertise 282e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link Root#FLAG_SUPPORTS_RECENTS}. The returned documents should be 283e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * sorted by {@link Document#COLUMN_LAST_MODIFIED} in descending order, and 284e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * limited to only return the 64 most recently modified documents. 28537ed78e504ef3666dd5fce15ff4994f151c44fcdJeff Sharkey * <p> 28637ed78e504ef3666dd5fce15ff4994f151c44fcdJeff Sharkey * Recent documents do not support change notifications. 287e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * 288e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param projection list of {@link Document} columns to put into the 289e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * cursor. If {@code null} all supported columns should be 290e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * included. 291e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @see DocumentsContract#EXTRA_LOADING 292e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey */ 293aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @SuppressWarnings("unused") 294ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey public Cursor queryRecentDocuments(String rootId, String[] projection) 295ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey throws FileNotFoundException { 296ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey throw new UnsupportedOperationException("Recent not supported"); 297aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 298aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 299aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey /** 3009352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * Return metadata for the single requested document. You should avoid 3019352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * making network requests to keep this request fast. 302aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * 303ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @param documentId the document to return. 304e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param projection list of {@link Document} columns to put into the 305e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * cursor. If {@code null} all supported columns should be 306e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * included. 307aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */ 308ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey public abstract Cursor queryDocument(String documentId, String[] projection) 309ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey throws FileNotFoundException; 310aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 311aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey /** 312e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Return the children documents contained in the requested directory. This 313e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * must only return immediate descendants, as additional queries will be 314e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * issued to recursively explore the tree. 315e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p> 316e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * If your provider is cloud-based, and you have some data cached or pinned 317e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * locally, you may return the local data immediately, setting 318e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that 3199352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * you are still fetching additional data. Then, when the network data is 3209352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * available, you can send a change notification to trigger a requery and 3213b945405cf96eae8b882f87934222a453718a559Jeff Sharkey * return the complete contents. To return a Cursor with extras, you need to 3223b945405cf96eae8b882f87934222a453718a559Jeff Sharkey * extend and override {@link Cursor#getExtras()}. 3239352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * <p> 3249352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * To support change notifications, you must 3259352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant 3269352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * Uri, such as 3279352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * {@link DocumentsContract#buildChildDocumentsUri(String, String)}. Then 3289352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * you can call {@link ContentResolver#notifyChange(Uri, 3299352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * android.database.ContentObserver, boolean)} with that Uri to send change 3309352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * notifications. 331aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * 332ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @param parentDocumentId the directory to return children for. 333e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param projection list of {@link Document} columns to put into the 334e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * cursor. If {@code null} all supported columns should be 335e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * included. 336e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param sortOrder how to order the rows, formatted as an SQL 337e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * {@code ORDER BY} clause (excluding the ORDER BY itself). 338e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Passing {@code null} will use the default sort order, which 339e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * may be unordered. This ordering is a hint that can be used to 340e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * prioritize how data is fetched from the network, but UI may 341e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * always enforce a specific ordering. 342e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @see DocumentsContract#EXTRA_LOADING 343e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @see DocumentsContract#EXTRA_INFO 344e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @see DocumentsContract#EXTRA_ERROR 345aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */ 346ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey public abstract Cursor queryChildDocuments( 347ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey String parentDocumentId, String[] projection, String sortOrder) 348ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey throws FileNotFoundException; 349aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 3504ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey /** {@hide} */ 3514ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey @SuppressWarnings("unused") 3524ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey public Cursor queryChildDocumentsForManage( 3534ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey String parentDocumentId, String[] projection, String sortOrder) 3544ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey throws FileNotFoundException { 3554ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey throw new UnsupportedOperationException("Manage not supported"); 3564ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey } 3574ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey 358aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey /** 359e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Return documents that that match the given query under the requested 360e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * root. The returned documents should be sorted by relevance in descending 361e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * order. How documents are matched against the query string is an 362e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * implementation detail left to each provider, but it's suggested that at 363e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * least {@link Document#COLUMN_DISPLAY_NAME} be matched in a 364e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * case-insensitive fashion. 365e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p> 366e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Only documents may be returned; directories are not supported in search 367e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * results. 3689352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * <p> 3699352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * If your provider is cloud-based, and you have some data cached or pinned 3709352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * locally, you may return the local data immediately, setting 3719352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that 3729352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * you are still fetching additional data. Then, when the network data is 3739352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * available, you can send a change notification to trigger a requery and 3749352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * return the complete contents. 3759352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * <p> 3769352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * To support change notifications, you must 3779352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant 3789352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * Uri, such as {@link DocumentsContract#buildSearchDocumentsUri(String, 3799352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * String, String)}. Then you can call {@link ContentResolver#notifyChange(Uri, 3809352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * android.database.ContentObserver, boolean)} with that Uri to send change 3819352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * notifications. 382aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * 3833e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey * @param rootId the root to search under. 384e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param query string to match documents against. 385e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param projection list of {@link Document} columns to put into the 386e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * cursor. If {@code null} all supported columns should be 387e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * included. 388e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @see DocumentsContract#EXTRA_LOADING 389e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @see DocumentsContract#EXTRA_INFO 390e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @see DocumentsContract#EXTRA_ERROR 391aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */ 392aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @SuppressWarnings("unused") 3933e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey public Cursor querySearchDocuments(String rootId, String query, String[] projection) 394ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey throws FileNotFoundException { 395aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new UnsupportedOperationException("Search not supported"); 396aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 397aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 398aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey /** 399e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Return concrete MIME type of the requested document. Must match the value 400e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * of {@link Document#COLUMN_MIME_TYPE} for this document. The default 401e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * implementation queries {@link #queryDocument(String, String[])}, so 402e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * providers may choose to override this as an optimization. 403aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */ 404ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey public String getDocumentType(String documentId) throws FileNotFoundException { 405ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey final Cursor cursor = queryDocument(documentId, null); 406aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey try { 407aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey if (cursor.moveToFirst()) { 408ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey return cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)); 409aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } else { 410aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey return null; 411aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 412aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } finally { 413aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey IoUtils.closeQuietly(cursor); 414aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 415aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 416aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 417aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey /** 418e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Open and return the requested document. 419e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p> 4209352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * Your provider should return a reliable {@link ParcelFileDescriptor} to 421e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * detect when the remote caller has finished reading or writing the 4229352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * document. You may return a pipe or socket pair if the mode is exclusively 4239352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * "r" or "w", but complex modes like "rw" imply a normal file on disk that 4249352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * supports seeking. 425e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p> 4269352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * If you block while downloading content, you should periodically check 4279352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * {@link CancellationSignal#isCanceled()} to abort abandoned open requests. 428aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * 429e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param documentId the document to return. 430aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @param mode the mode to open with, such as 'r', 'w', or 'rw'. 431aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @param signal used by the caller to signal if the request should be 4323b945405cf96eae8b882f87934222a453718a559Jeff Sharkey * cancelled. May be null. 433aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @see ParcelFileDescriptor#open(java.io.File, int, android.os.Handler, 434aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * OnCloseListener) 435aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @see ParcelFileDescriptor#createReliablePipe() 436aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @see ParcelFileDescriptor#createReliableSocketPair() 437e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @see ParcelFileDescriptor#parseMode(String) 438aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */ 439aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey public abstract ParcelFileDescriptor openDocument( 440e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey String documentId, String mode, CancellationSignal signal) throws FileNotFoundException; 441aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 442aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey /** 443e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Open and return a thumbnail of the requested document. 444e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p> 445e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * A provider should return a thumbnail closely matching the hinted size, 446e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * attempting to serve from a local cache if possible. A provider should 447e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * never return images more than double the hinted size. 448e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * <p> 4499352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * If you perform expensive operations to download or generate a thumbnail, 4509352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * you should periodically check {@link CancellationSignal#isCanceled()} to 4519352c383e95c3e1facd8a7a2d49ff488fb7bbcafJeff Sharkey * abort abandoned thumbnail requests. 452aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * 453e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * @param documentId the document to return. 454aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @param sizeHint hint of the optimal thumbnail dimensions. 455aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @param signal used by the caller to signal if the request should be 4563b945405cf96eae8b882f87934222a453718a559Jeff Sharkey * cancelled. May be null. 457ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see Document#FLAG_SUPPORTS_THUMBNAIL 458aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey */ 459aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @SuppressWarnings("unused") 460aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey public AssetFileDescriptor openDocumentThumbnail( 461e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey String documentId, Point sizeHint, CancellationSignal signal) 462e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey throws FileNotFoundException { 463aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new UnsupportedOperationException("Thumbnails not supported"); 464aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 465aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 466ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey /** 467ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * Implementation is provided by the parent class. Cannot be overriden. 468ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * 469ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #queryRoots(String[]) 470ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #queryRecentDocuments(String, String[]) 471ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #queryDocument(String, String[]) 472ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #queryChildDocuments(String, String[], String) 473ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #querySearchDocuments(String, String, String[]) 474ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey */ 475aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @Override 476ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey public final Cursor query(Uri uri, String[] projection, String selection, 477ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey String[] selectionArgs, String sortOrder) { 478aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey try { 479aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey switch (mMatcher.match(uri)) { 480a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey case MATCH_ROOTS: 481ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey return queryRoots(projection); 482ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey case MATCH_RECENT: 483ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey return queryRecentDocuments(getRootId(uri), projection); 4843e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey case MATCH_SEARCH: 4853e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey return querySearchDocuments( 4863e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey getRootId(uri), getSearchDocumentsQuery(uri), projection); 487aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey case MATCH_DOCUMENT: 488b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey case MATCH_DOCUMENT_TREE: 489b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey enforceTree(uri); 490ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey return queryDocument(getDocumentId(uri), projection); 491aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey case MATCH_CHILDREN: 492b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey case MATCH_CHILDREN_TREE: 493b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey enforceTree(uri); 4944ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey if (DocumentsContract.isManageMode(uri)) { 4954ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey return queryChildDocumentsForManage( 4964ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey getDocumentId(uri), projection, sortOrder); 4974ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey } else { 4984ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey return queryChildDocuments(getDocumentId(uri), projection, sortOrder); 4994ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey } 500aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey default: 501aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new UnsupportedOperationException("Unsupported Uri " + uri); 502aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 503aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } catch (FileNotFoundException e) { 504aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey Log.w(TAG, "Failed during query", e); 505aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey return null; 506aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 507aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 508aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 509ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey /** 510ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * Implementation is provided by the parent class. Cannot be overriden. 511ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * 512ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #getDocumentType(String) 513ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey */ 514aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @Override 515aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey public final String getType(Uri uri) { 516aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey try { 517aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey switch (mMatcher.match(uri)) { 518a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey case MATCH_ROOT: 519a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey return DocumentsContract.Root.MIME_TYPE_ITEM; 520aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey case MATCH_DOCUMENT: 521b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey case MATCH_DOCUMENT_TREE: 522b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey enforceTree(uri); 523ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey return getDocumentType(getDocumentId(uri)); 524aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey default: 525aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey return null; 526aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 527aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } catch (FileNotFoundException e) { 528aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey Log.w(TAG, "Failed during getType", e); 529aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey return null; 530aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 531aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 532aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 533ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey /** 53421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * Implementation is provided by the parent class. Can be overridden to 53521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * provide additional functionality, but subclasses <em>must</em> always 53621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * call the superclass. If the superclass returns {@code null}, the subclass 53721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * may implement custom behavior. 53821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * <p> 539b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey * This is typically used to resolve a subtree URI into a concrete document 54021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * reference, issuing a narrower single-document URI permission grant along 54121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * the way. 54221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * 543b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey * @see DocumentsContract#buildDocumentUriUsingTree(Uri, String) 54421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey */ 54521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey @Override 54621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey public Uri canonicalize(Uri uri) { 54721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey final Context context = getContext(); 54821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey switch (mMatcher.match(uri)) { 549b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey case MATCH_DOCUMENT_TREE: 550b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey enforceTree(uri); 55121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey 552b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey final Uri narrowUri = buildDocumentUri(uri.getAuthority(), getDocumentId(uri)); 55321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey 55421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey // Caller may only have prefix grant, so extend them a grant to 555b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey // the narrow URI. 556b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context, uri); 55721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey context.grantUriPermission(getCallingPackage(), narrowUri, modeFlags); 55821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey return narrowUri; 55921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey } 56021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey return null; 56121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey } 56221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey 563b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey private static int getCallingOrSelfUriPermissionModeFlags(Context context, Uri uri) { 564b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey // TODO: move this to a direct AMS call 565b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey int modeFlags = 0; 566b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey if (context.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) 567b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey == PackageManager.PERMISSION_GRANTED) { 568b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey modeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION; 569b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey } 570b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey if (context.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) 571b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey == PackageManager.PERMISSION_GRANTED) { 572b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey modeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION; 573b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey } 574b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey if (context.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION 575b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) 576b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey == PackageManager.PERMISSION_GRANTED) { 577b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey modeFlags |= Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION; 578b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey } 579b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey return modeFlags; 580b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey } 581b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey 58221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey /** 583ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * Implementation is provided by the parent class. Throws by default, and 584ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * cannot be overriden. 585ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * 586ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #createDocument(String, String, String) 587ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey */ 588aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @Override 589aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey public final Uri insert(Uri uri, ContentValues values) { 590aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new UnsupportedOperationException("Insert not supported"); 591aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 592aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 593ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey /** 594ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * Implementation is provided by the parent class. Throws by default, and 595ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * cannot be overriden. 596ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * 597ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #deleteDocument(String) 598ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey */ 599aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @Override 600aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey public final int delete(Uri uri, String selection, String[] selectionArgs) { 601aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new UnsupportedOperationException("Delete not supported"); 602aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 603aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 604ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey /** 605ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * Implementation is provided by the parent class. Throws by default, and 606ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * cannot be overriden. 607ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey */ 608aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @Override 609aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey public final int update( 610aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey Uri uri, ContentValues values, String selection, String[] selectionArgs) { 611aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new UnsupportedOperationException("Update not supported"); 612aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 613aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 614911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey /** 615911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey * Implementation is provided by the parent class. Can be overridden to 616911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey * provide additional functionality, but subclasses <em>must</em> always 617911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey * call the superclass. If the superclass returns {@code null}, the subclass 618911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey * may implement custom behavior. 619911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey */ 620aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @Override 621911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey public Bundle call(String method, String arg, Bundle extras) { 622aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey if (!method.startsWith("android:")) { 62321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey // Ignore non-platform methods 624911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey return super.call(method, arg, extras); 625aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 626aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 627b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey final Context context = getContext(); 62821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI); 62921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey final String authority = documentUri.getAuthority(); 63021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey final String documentId = DocumentsContract.getDocumentId(documentUri); 631e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey 63221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey if (!mAuthority.equals(authority)) { 63321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey throw new SecurityException( 63421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey "Requested authority " + authority + " doesn't match provider " + mAuthority); 63521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey } 636b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey enforceTree(documentUri); 637aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 638aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey final Bundle out = new Bundle(); 639aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey try { 640ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey if (METHOD_CREATE_DOCUMENT.equals(method)) { 64121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey enforceWritePermissionInner(documentUri); 64221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey 643ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE); 644ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); 645ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey final String newDocumentId = createDocument(documentId, mimeType, displayName); 64621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey 64721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey // No need to issue new grants here, since caller either has 64821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey // manage permission or a prefix grant. We might generate a 649b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey // tree style URI if that's how they called us. 650b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, 65121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey newDocumentId); 65221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); 653e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey 654b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey } else if (METHOD_RENAME_DOCUMENT.equals(method)) { 655b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey enforceWritePermissionInner(documentUri); 656b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey 657b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); 658b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey final String newDocumentId = renameDocument(documentId, displayName); 659b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey 660b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey if (newDocumentId != null) { 661b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, 662b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey newDocumentId); 663b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey 664b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey // If caller came in with a narrow grant, issue them a 665b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey // narrow grant for the newly renamed document. 666b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey if (!isTreeUri(newDocumentUri)) { 667b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context, 668b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey documentUri); 669b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags); 670b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey } 671b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey 672b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); 673b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey 674b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey // Original document no longer exists, clean up any grants 675b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey revokeDocumentPermission(documentId); 676b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey } 677b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey 678aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } else if (METHOD_DELETE_DOCUMENT.equals(method)) { 67921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey enforceWritePermissionInner(documentUri); 680e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey deleteDocument(documentId); 681e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey 682e37ea6123d1aa3cd3e8804988886b1f6046d79d6Jeff Sharkey // Document no longer exists, clean up any grants 68321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey revokeDocumentPermission(documentId); 684aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 685aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } else { 686aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new UnsupportedOperationException("Method not supported " + method); 687aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 688aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } catch (FileNotFoundException e) { 689aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throw new IllegalStateException("Failed call " + method, e); 690aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 691aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey return out; 692aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 693aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 694ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey /** 69521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * Revoke any active permission grants for the given 69621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * {@link Document#COLUMN_DOCUMENT_ID}, usually called when a document 69721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * becomes invalid. Follows the same semantics as 69821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * {@link Context#revokeUriPermission(Uri, int)}. 69921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey */ 70021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey public final void revokeDocumentPermission(String documentId) { 70121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey final Context context = getContext(); 702b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey context.revokeUriPermission(buildDocumentUri(mAuthority, documentId), ~0); 703b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey context.revokeUriPermission(buildTreeDocumentUri(mAuthority, documentId), ~0); 70421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey } 70521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey 70621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey /** 707e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Implementation is provided by the parent class. Cannot be overriden. 708ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * 709ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #openDocument(String, String, CancellationSignal) 710ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey */ 711aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @Override 712aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey public final ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 713b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey enforceTree(uri); 714ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey return openDocument(getDocumentId(uri), mode, null); 715aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 716aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 717ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey /** 718e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Implementation is provided by the parent class. Cannot be overriden. 719ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * 720ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #openDocument(String, String, CancellationSignal) 721ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey */ 722aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @Override 723aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey public final ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal) 724aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throws FileNotFoundException { 725b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey enforceTree(uri); 726ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey return openDocument(getDocumentId(uri), mode, signal); 727aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 728aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 729ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey /** 730e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Implementation is provided by the parent class. Cannot be overriden. 731ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * 73221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * @see #openDocument(String, String, CancellationSignal) 73321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey */ 73421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey @Override 73521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey @SuppressWarnings("resource") 73621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey public final AssetFileDescriptor openAssetFile(Uri uri, String mode) 73721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey throws FileNotFoundException { 738b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey enforceTree(uri); 73921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, null); 74021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null; 74121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey } 74221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey 74321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey /** 74421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * Implementation is provided by the parent class. Cannot be overriden. 74521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * 74621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * @see #openDocument(String, String, CancellationSignal) 74721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey */ 74821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey @Override 74921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey @SuppressWarnings("resource") 75021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey public final AssetFileDescriptor openAssetFile(Uri uri, String mode, CancellationSignal signal) 75121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey throws FileNotFoundException { 752b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey enforceTree(uri); 75321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, signal); 75421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null; 75521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey } 75621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey 75721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey /** 75821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * Implementation is provided by the parent class. Cannot be overriden. 75921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * 760ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #openDocumentThumbnail(String, Point, CancellationSignal) 761ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey */ 762aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @Override 763aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts) 764aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throws FileNotFoundException { 765b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey enforceTree(uri); 766aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) { 767aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE); 768ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey return openDocumentThumbnail(getDocumentId(uri), sizeHint, null); 769aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } else { 770aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey return super.openTypedAssetFile(uri, mimeTypeFilter, opts); 771aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 772aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 773aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey 774ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey /** 775e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey * Implementation is provided by the parent class. Cannot be overriden. 776ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * 777ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey * @see #openDocumentThumbnail(String, Point, CancellationSignal) 778ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey */ 779aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey @Override 780aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey public final AssetFileDescriptor openTypedAssetFile( 781aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal) 782aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey throws FileNotFoundException { 783b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey enforceTree(uri); 784aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) { 785aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE); 786ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal); 787aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } else { 788aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey return super.openTypedAssetFile(uri, mimeTypeFilter, opts, signal); 789aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 790aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey } 791aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey} 792