• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.documentsui;
18 
19 import static android.content.ContentResolver.wrap;
20 
21 import android.content.ContentProviderClient;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.database.Cursor;
25 import android.net.Uri;
26 import android.os.ParcelFileDescriptor;
27 import android.os.RemoteException;
28 import android.provider.DocumentsContract;
29 import android.provider.DocumentsContract.Path;
30 import android.util.Log;
31 
32 import androidx.annotation.Nullable;
33 
34 import com.android.documentsui.archives.ArchivesProvider;
35 import com.android.documentsui.base.DocumentInfo;
36 import com.android.documentsui.base.RootInfo;
37 import com.android.documentsui.base.State;
38 import com.android.documentsui.base.UserId;
39 
40 import java.io.FileNotFoundException;
41 import java.util.ArrayList;
42 import java.util.List;
43 
44 /**
45  * Provides synchronous access to {@link DocumentInfo} instances given some identifying information
46  * and some documents API.
47  */
48 public interface DocumentsAccess {
49 
getRootDocument(RootInfo root)50     @Nullable DocumentInfo getRootDocument(RootInfo root);
getDocument(Uri uri, UserId userId)51     @Nullable DocumentInfo getDocument(Uri uri, UserId userId);
getArchiveDocument(Uri uri, UserId userId)52     @Nullable DocumentInfo getArchiveDocument(Uri uri, UserId userId);
53 
isDocumentUri(Uri uri)54     boolean isDocumentUri(Uri uri);
55 
56     @Nullable
findDocumentPath(Uri uri, UserId userId)57     Path findDocumentPath(Uri uri, UserId userId)
58             throws RemoteException, FileNotFoundException, CrossProfileNoPermissionException;
59 
getDocuments(UserId userId, String authority, List<String> docIds)60     List<DocumentInfo> getDocuments(UserId userId, String authority, List<String> docIds)
61             throws RemoteException, CrossProfileNoPermissionException;
62 
createDocument(DocumentInfo parentDoc, String mimeType, String displayName)63     @Nullable Uri createDocument(DocumentInfo parentDoc, String mimeType, String displayName);
64 
create(Context context, State state)65     public static DocumentsAccess create(Context context, State state) {
66         return new RuntimeDocumentAccess(context, state);
67     }
68 
69     public final class RuntimeDocumentAccess implements DocumentsAccess {
70 
71         private static final String TAG = "DocumentAccess";
72 
73         private final Context mContext;
74         private final State mState;
75 
RuntimeDocumentAccess(Context context, State state)76         private RuntimeDocumentAccess(Context context, State state) {
77             mContext = context;
78             mState = state;
79         }
80 
81         @Override
82         @Nullable
getRootDocument(RootInfo root)83         public DocumentInfo getRootDocument(RootInfo root) {
84             return getDocument(DocumentsContract.buildDocumentUri(root.authority, root.documentId),
85                     root.userId);
86         }
87 
88         @Override
getDocument(Uri uri, UserId userId)89         public @Nullable DocumentInfo getDocument(Uri uri, UserId userId) {
90             try {
91                 if (mState.canInteractWith(userId)) {
92                     return DocumentInfo.fromUri(userId.getContentResolver(mContext), uri, userId);
93                 }
94             } catch (FileNotFoundException e) {
95                 Log.w(TAG, "Couldn't create DocumentInfo for uri: " + uri);
96             }
97 
98             return null;
99         }
100 
101         @Override
getDocuments(UserId userId, String authority, List<String> docIds)102         public List<DocumentInfo> getDocuments(UserId userId, String authority, List<String> docIds)
103                 throws RemoteException, CrossProfileNoPermissionException {
104             if (!mState.canInteractWith(userId)) {
105                 throw new CrossProfileNoPermissionException();
106             }
107             try (ContentProviderClient client = DocumentsApplication.acquireUnstableProviderOrThrow(
108                     userId.getContentResolver(mContext), authority)) {
109 
110                 List<DocumentInfo> result = new ArrayList<>(docIds.size());
111                 for (String docId : docIds) {
112                     final Uri uri = DocumentsContract.buildDocumentUri(authority, docId);
113                     try (final Cursor cursor = client.query(uri, null, null, null, null)) {
114                         if (!cursor.moveToNext()) {
115                             Log.e(TAG, "Couldn't create DocumentInfo for Uri: " + uri);
116                             throw new RemoteException("Failed to move cursor.");
117                         }
118 
119                         result.add(DocumentInfo.fromCursor(cursor, userId, authority));
120                     }
121                 }
122 
123                 return result;
124             }
125         }
126 
127         @Override
getArchiveDocument(Uri uri, UserId userId)128         public DocumentInfo getArchiveDocument(Uri uri, UserId userId) {
129             return getDocument(
130                     ArchivesProvider.buildUriForArchive(uri, ParcelFileDescriptor.MODE_READ_ONLY),
131                     userId);
132         }
133 
134         @Override
isDocumentUri(Uri uri)135         public boolean isDocumentUri(Uri uri) {
136             return DocumentsContract.isDocumentUri(mContext, uri);
137         }
138 
139         @Override
findDocumentPath(Uri docUri, UserId userId)140         public Path findDocumentPath(Uri docUri, UserId userId)
141                 throws RemoteException, FileNotFoundException, CrossProfileNoPermissionException {
142             if (!mState.canInteractWith(userId)) {
143                 throw new CrossProfileNoPermissionException();
144             }
145             final ContentResolver resolver = userId.getContentResolver(mContext);
146             try (final ContentProviderClient client = DocumentsApplication
147                     .acquireUnstableProviderOrThrow(resolver, docUri.getAuthority())) {
148                 return DocumentsContract.findDocumentPath(wrap(client), docUri);
149             }
150         }
151 
152         @Override
createDocument(DocumentInfo parentDoc, String mimeType, String displayName)153         public Uri createDocument(DocumentInfo parentDoc, String mimeType, String displayName) {
154             final ContentResolver resolver = parentDoc.userId.getContentResolver(mContext);
155             try (ContentProviderClient client = DocumentsApplication.acquireUnstableProviderOrThrow(
156                     resolver, parentDoc.derivedUri.getAuthority())) {
157                 Uri createUri = DocumentsContract.createDocument(
158                         wrap(client), parentDoc.derivedUri, mimeType, displayName);
159                 // If the document info's user is the current user, we can simply return the uri.
160                 // Otherwise, we need to create document with the content resolver from the other
161                 // user. The uri returned from that content resolver does not contain the user
162                 // info. Hence we need to append the other user info to the uri otherwise an app
163                 // will think the uri is from the current user.
164                 // The way to append a userInfo is to use the authority which contains user info
165                 // obtained from the parentDoc.getDocumentUri().
166                 return UserId.CURRENT_USER.equals(parentDoc.userId)
167                         ? createUri : appendEncodedParentAuthority(parentDoc, createUri);
168             } catch (Exception e) {
169                 Log.w(TAG, "Failed to create document", e);
170                 return null;
171             }
172         }
173 
appendEncodedParentAuthority(DocumentInfo parentDoc, Uri uri)174         private Uri appendEncodedParentAuthority(DocumentInfo parentDoc, Uri uri) {
175             return uri.buildUpon().encodedAuthority(
176                     parentDoc.getDocumentUri().getAuthority()).build();
177         }
178     }
179 }
180