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