• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 import static android.provider.DocumentsContract.buildChildDocumentsUri;
21 import static android.provider.DocumentsContract.buildDocumentUri;
22 import static android.provider.DocumentsContract.buildRootsUri;
23 import static com.android.documentsui.base.DocumentInfo.getCursorString;
24 import static androidx.core.util.Preconditions.checkArgument;
25 import static junit.framework.Assert.assertEquals;
26 import static junit.framework.Assert.assertNotNull;
27 import static junit.framework.Assert.fail;
28 
29 import android.content.ContentProviderClient;
30 import android.database.Cursor;
31 import android.net.Uri;
32 import android.os.Bundle;
33 import android.os.ParcelFileDescriptor;
34 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
35 import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
36 import android.os.RemoteException;
37 import android.provider.DocumentsContract;
38 import android.provider.DocumentsContract.Document;
39 import android.provider.DocumentsContract.Root;
40 import androidx.annotation.Nullable;
41 import android.test.MoreAsserts;
42 import android.text.TextUtils;
43 
44 import com.android.documentsui.base.DocumentInfo;
45 import com.android.documentsui.base.RootInfo;
46 import com.android.documentsui.roots.RootCursorWrapper;
47 
48 import android.os.FileUtils;
49 import libcore.io.Streams;
50 
51 import com.google.common.collect.Lists;
52 
53 import java.io.FileNotFoundException;
54 import java.io.IOException;
55 import java.util.ArrayList;
56 import java.util.Arrays;
57 import java.util.List;
58 
59 /**
60  * Provides support for creation of documents in a test settings.
61  */
62 public class DocumentsProviderHelper {
63 
64     private final String mAuthority;
65     private final ContentProviderClient mClient;
66 
DocumentsProviderHelper(String authority, ContentProviderClient client)67     public DocumentsProviderHelper(String authority, ContentProviderClient client) {
68         checkArgument(!TextUtils.isEmpty(authority));
69         mAuthority = authority;
70         mClient = client;
71     }
72 
getRoot(String documentId)73     public RootInfo getRoot(String documentId) throws RemoteException {
74         final Uri rootsUri = buildRootsUri(mAuthority);
75         Cursor cursor = null;
76         try {
77             cursor = mClient.query(rootsUri, null, null, null, null);
78             while (cursor.moveToNext()) {
79                 if (documentId.equals(getCursorString(cursor, Root.COLUMN_ROOT_ID))) {
80                     return RootInfo.fromRootsCursor(mAuthority, cursor);
81                 }
82             }
83             throw new IllegalArgumentException("Can't find matching root for id=" + documentId);
84         } catch (Exception e) {
85             throw new RuntimeException("Can't load root for id=" + documentId , e);
86         } finally {
87             FileUtils.closeQuietly(cursor);
88         }
89     }
90 
createDocument(Uri parentUri, String mimeType, String name)91     public Uri createDocument(Uri parentUri, String mimeType, String name) {
92         if (name.contains("/")) {
93             throw new IllegalArgumentException("Name and mimetype probably interposed.");
94         }
95         try {
96             Uri uri = DocumentsContract.createDocument(wrap(mClient), parentUri, mimeType, name);
97             return uri;
98         } catch (FileNotFoundException e) {
99             throw new RuntimeException("Couldn't create document: " + name + " with mimetype "
100                     + mimeType, e);
101         }
102     }
103 
createDocument(String parentId, String mimeType, String name)104     public Uri createDocument(String parentId, String mimeType, String name) {
105         Uri parentUri = buildDocumentUri(mAuthority, parentId);
106         return createDocument(parentUri, mimeType, name);
107     }
108 
createDocument(RootInfo root, String mimeType, String name)109     public Uri createDocument(RootInfo root, String mimeType, String name) {
110         return createDocument(root.documentId, mimeType, name);
111     }
112 
createDocumentWithFlags(String documentId, String mimeType, String name, int flags, String... streamTypes)113     public Uri createDocumentWithFlags(String documentId, String mimeType, String name, int flags,
114             String... streamTypes)
115             throws RemoteException {
116         Bundle in = new Bundle();
117         in.putInt(StubProvider.EXTRA_FLAGS, flags);
118         in.putString(StubProvider.EXTRA_PARENT_ID, documentId);
119         in.putString(Document.COLUMN_MIME_TYPE, mimeType);
120         in.putString(Document.COLUMN_DISPLAY_NAME, name);
121         in.putStringArrayList(StubProvider.EXTRA_STREAM_TYPES, Lists.newArrayList(streamTypes));
122 
123         Bundle out = mClient.call("createDocumentWithFlags", null, in);
124         Uri uri = out.getParcelable(DocumentsContract.EXTRA_URI);
125         return uri;
126     }
127 
createFolder(Uri parentUri, String name)128     public Uri createFolder(Uri parentUri, String name) {
129         return createDocument(parentUri, Document.MIME_TYPE_DIR, name);
130     }
131 
createFolder(String parentId, String name)132     public Uri createFolder(String parentId, String name) {
133         Uri parentUri = buildDocumentUri(mAuthority, parentId);
134         return createDocument(parentUri, Document.MIME_TYPE_DIR, name);
135     }
136 
createFolder(RootInfo root, String name)137     public Uri createFolder(RootInfo root, String name) {
138         return createDocument(root, Document.MIME_TYPE_DIR, name);
139     }
140 
writeDocument(Uri documentUri, byte[] contents)141     public void writeDocument(Uri documentUri, byte[] contents)
142             throws RemoteException, IOException {
143         ParcelFileDescriptor file = mClient.openFile(documentUri, "w", null);
144         try (AutoCloseOutputStream out = new AutoCloseOutputStream(file)) {
145             out.write(contents, 0, contents.length);
146         }
147         waitForWrite();
148     }
149 
writeAppendDocument(Uri documentUri, byte[] contents)150     public void writeAppendDocument(Uri documentUri, byte[] contents)
151             throws RemoteException, IOException {
152         ParcelFileDescriptor file = mClient.openFile(documentUri, "wa", null);
153         try (AutoCloseOutputStream out = new AutoCloseOutputStream(file)) {
154             out.write(contents);
155         }
156         waitForWrite();
157     }
158 
waitForWrite()159     public void waitForWrite() throws RemoteException {
160         mClient.call("waitForWrite", null, null);
161     }
162 
readDocument(Uri documentUri)163     public byte[] readDocument(Uri documentUri) throws RemoteException, IOException {
164         ParcelFileDescriptor file = mClient.openFile(documentUri, "r", null);
165         byte[] buf = null;
166         try (AutoCloseInputStream in = new AutoCloseInputStream(file)) {
167             buf = Streams.readFully(in);
168         }
169         return buf;
170     }
171 
assertChildCount(Uri parentUri, int expected)172     public void assertChildCount(Uri parentUri, int expected) throws Exception {
173         List<DocumentInfo> children = listChildren(parentUri);
174         assertEquals("Incorrect file count after copy", expected, children.size());
175     }
176 
assertChildCount(String parentId, int expected)177     public void assertChildCount(String parentId, int expected) throws Exception {
178         List<DocumentInfo> children = listChildren(parentId, -1);
179         assertEquals("Incorrect file count after copy", expected, children.size());
180     }
181 
assertChildCount(RootInfo root, int expected)182     public void assertChildCount(RootInfo root, int expected) throws Exception {
183         assertChildCount(root.documentId, expected);
184     }
185 
assertHasFile(Uri parentUri, String name)186     public void assertHasFile(Uri parentUri, String name) throws Exception {
187         List<DocumentInfo> children = listChildren(parentUri);
188         for (DocumentInfo child : children) {
189             if (name.equals(child.displayName) && !child.isDirectory()) {
190                 return;
191             }
192         }
193         fail("Could not find file named=" + name + " in children " + children);
194     }
195 
assertHasFile(String parentId, String name)196     public void assertHasFile(String parentId, String name) throws Exception {
197         Uri parentUri = buildDocumentUri(mAuthority, parentId);
198         assertHasFile(parentUri, name);
199     }
200 
assertHasFile(RootInfo root, String name)201     public void assertHasFile(RootInfo root, String name) throws Exception {
202         assertHasFile(root.documentId, name);
203     }
204 
assertHasDirectory(Uri parentUri, String name)205     public void assertHasDirectory(Uri parentUri, String name) throws Exception {
206         List<DocumentInfo> children = listChildren(parentUri);
207         for (DocumentInfo child : children) {
208             if (name.equals(child.displayName) && child.isDirectory()) {
209                 return;
210             }
211         }
212         fail("Could not find name=" + name + " in children " + children);
213     }
214 
assertHasDirectory(String parentId, String name)215     public void assertHasDirectory(String parentId, String name) throws Exception {
216         Uri parentUri = buildDocumentUri(mAuthority, parentId);
217         assertHasDirectory(parentUri, name);
218     }
219 
assertHasDirectory(RootInfo root, String name)220     public void assertHasDirectory(RootInfo root, String name) throws Exception {
221         assertHasDirectory(root.documentId, name);
222     }
223 
assertDoesNotExist(Uri parentUri, String name)224     public void assertDoesNotExist(Uri parentUri, String name) throws Exception {
225         List<DocumentInfo> children = listChildren(parentUri);
226         for (DocumentInfo child : children) {
227             if (name.equals(child.displayName)) {
228                 fail("Found name=" + name + " in children " + children);
229             }
230         }
231     }
232 
assertDoesNotExist(String parentId, String name)233     public void assertDoesNotExist(String parentId, String name) throws Exception {
234         Uri parentUri = buildDocumentUri(mAuthority, parentId);
235         assertDoesNotExist(parentUri, name);
236     }
237 
assertDoesNotExist(RootInfo root, String name)238     public void assertDoesNotExist(RootInfo root, String name) throws Exception {
239         assertDoesNotExist(root.getUri(), name);
240     }
241 
findFile(String parentId, String name)242     public @Nullable DocumentInfo findFile(String parentId, String name)
243             throws Exception {
244         List<DocumentInfo> children = listChildren(parentId);
245         for (DocumentInfo child : children) {
246             if (name.equals(child.displayName)) {
247                 return child;
248             }
249         }
250         return null;
251     }
252 
findDocument(String parentId, String name)253     public DocumentInfo findDocument(String parentId, String name) throws Exception {
254         List<DocumentInfo> children = listChildren(parentId);
255         for (DocumentInfo child : children) {
256             if (name.equals(child.displayName)) {
257                 return child;
258             }
259         }
260         return null;
261     }
262 
findDocument(Uri parentUri, String name)263     public DocumentInfo findDocument(Uri parentUri, String name) throws Exception {
264         List<DocumentInfo> children = listChildren(parentUri);
265         for (DocumentInfo child : children) {
266             if (name.equals(child.displayName)) {
267                 return child;
268             }
269         }
270         return null;
271     }
272 
listChildren(Uri parentUri)273     public List<DocumentInfo> listChildren(Uri parentUri) throws Exception {
274         String id = DocumentsContract.getDocumentId(parentUri);
275         return listChildren(id);
276     }
277 
listChildren(String documentId)278     public List<DocumentInfo> listChildren(String documentId) throws Exception {
279         return listChildren(documentId, 100);
280     }
281 
listChildren(Uri parentUri, int maxCount)282     public List<DocumentInfo> listChildren(Uri parentUri, int maxCount) throws Exception {
283         String id = DocumentsContract.getDocumentId(parentUri);
284         return listChildren(id, maxCount);
285     }
286 
listChildren(String documentId, int maxCount)287     public List<DocumentInfo> listChildren(String documentId, int maxCount) throws Exception {
288         Uri uri = buildChildDocumentsUri(mAuthority, documentId);
289         List<DocumentInfo> children = new ArrayList<>();
290         try (Cursor cursor = mClient.query(uri, null, null, null, null, null)) {
291             Cursor wrapper = new RootCursorWrapper(mAuthority, "totally-fake", cursor, maxCount);
292             while (wrapper.moveToNext()) {
293                 children.add(DocumentInfo.fromDirectoryCursor(wrapper));
294             }
295         }
296         return children;
297     }
298 
assertFileContents(Uri documentUri, byte[] expected)299     public void assertFileContents(Uri documentUri, byte[] expected) throws Exception {
300         MoreAsserts.assertEquals(
301                 "Copied file contents differ",
302                 expected, readDocument(documentUri));
303     }
304 
assertFileContents(String parentId, String fileName, byte[] expected)305     public void assertFileContents(String parentId, String fileName, byte[] expected)
306             throws Exception {
307         DocumentInfo file = findFile(parentId, fileName);
308         assertNotNull(file);
309         assertFileContents(file.derivedUri, expected);
310     }
311 
312     /**
313      * A helper method for StubProvider only. Won't work with other providers.
314      * @throws RemoteException
315      */
createVirtualFile( RootInfo root, String path, String mimeType, byte[] content, String... streamTypes)316     public Uri createVirtualFile(
317             RootInfo root, String path, String mimeType, byte[] content, String... streamTypes)
318                     throws RemoteException {
319 
320         Bundle args = new Bundle();
321         args.putString(StubProvider.EXTRA_ROOT, root.rootId);
322         args.putString(StubProvider.EXTRA_PATH, path);
323         args.putString(Document.COLUMN_MIME_TYPE, mimeType);
324         args.putStringArrayList(StubProvider.EXTRA_STREAM_TYPES, Lists.newArrayList(streamTypes));
325         args.putByteArray(StubProvider.EXTRA_CONTENT, content);
326 
327         Bundle result = mClient.call("createVirtualFile", null, args);
328         String documentId = result.getString(Document.COLUMN_DOCUMENT_ID);
329 
330         return DocumentsContract.buildDocumentUri(mAuthority, documentId);
331     }
332 
setLoadingDuration(long duration)333     public void setLoadingDuration(long duration) throws RemoteException {
334         final Bundle extra = new Bundle();
335         extra.putLong(DocumentsContract.EXTRA_LOADING, duration);
336         mClient.call("setLoadingDuration", null, extra);
337     }
338 
configure(String args, Bundle configuration)339     public void configure(String args, Bundle configuration) throws RemoteException {
340         mClient.call("configure", args, configuration);
341     }
342 
getRootList()343     public List<RootInfo> getRootList() throws RemoteException {
344         List<RootInfo> list = new ArrayList<>();
345         final Uri rootsUri = DocumentsContract.buildRootsUri(mAuthority);
346         Cursor cursor = null;
347         try {
348             cursor = mClient.query(rootsUri, null, null, null, null);
349             while (cursor.moveToNext()) {
350                 RootInfo rootInfo = RootInfo.fromRootsCursor(mAuthority, cursor);
351                 if (rootInfo != null) {
352                     list.add(rootInfo);
353                 }
354             }
355         } catch (Exception e) {
356             throw new RuntimeException("Can't load rootInfo list", e);
357         } finally {
358             FileUtils.closeQuietly(cursor);
359         }
360         return list;
361     }
362 }
363