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