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 writeAppendDocument(Uri documentUri, byte[] contents)147 public void writeAppendDocument(Uri documentUri, byte[] contents) 148 throws RemoteException, IOException { 149 ParcelFileDescriptor file = mClient.openFile(documentUri, "wa", null); 150 try (AutoCloseOutputStream out = new AutoCloseOutputStream(file)) { 151 out.write(contents); 152 } 153 waitForWrite(); 154 } 155 waitForWrite()156 public void waitForWrite() throws RemoteException { 157 mClient.call("waitForWrite", null, null); 158 } 159 readDocument(Uri documentUri)160 public byte[] readDocument(Uri documentUri) throws RemoteException, IOException { 161 ParcelFileDescriptor file = mClient.openFile(documentUri, "r", null); 162 byte[] buf = null; 163 try (AutoCloseInputStream in = new AutoCloseInputStream(file)) { 164 buf = Streams.readFully(in); 165 } 166 return buf; 167 } 168 assertChildCount(Uri parentUri, int expected)169 public void assertChildCount(Uri parentUri, int expected) throws Exception { 170 List<DocumentInfo> children = listChildren(parentUri); 171 assertEquals("Incorrect file count after copy", expected, children.size()); 172 } 173 assertChildCount(String parentId, int expected)174 public void assertChildCount(String parentId, int expected) throws Exception { 175 List<DocumentInfo> children = listChildren(parentId, -1); 176 assertEquals("Incorrect file count after copy", expected, children.size()); 177 } 178 assertChildCount(RootInfo root, int expected)179 public void assertChildCount(RootInfo root, int expected) throws Exception { 180 assertChildCount(root.documentId, expected); 181 } 182 assertHasFile(Uri parentUri, String name)183 public void assertHasFile(Uri parentUri, String name) throws Exception { 184 List<DocumentInfo> children = listChildren(parentUri); 185 for (DocumentInfo child : children) { 186 if (name.equals(child.displayName) && !child.isDirectory()) { 187 return; 188 } 189 } 190 fail("Could not find file named=" + name + " in children " + children); 191 } 192 assertHasFile(String parentId, String name)193 public void assertHasFile(String parentId, String name) throws Exception { 194 Uri parentUri = buildDocumentUri(mAuthority, parentId); 195 assertHasFile(parentUri, name); 196 } 197 assertHasFile(RootInfo root, String name)198 public void assertHasFile(RootInfo root, String name) throws Exception { 199 assertHasFile(root.documentId, name); 200 } 201 assertHasDirectory(Uri parentUri, String name)202 public void assertHasDirectory(Uri parentUri, String name) throws Exception { 203 List<DocumentInfo> children = listChildren(parentUri); 204 for (DocumentInfo child : children) { 205 if (name.equals(child.displayName) && child.isDirectory()) { 206 return; 207 } 208 } 209 fail("Could not find name=" + name + " in children " + children); 210 } 211 assertHasDirectory(String parentId, String name)212 public void assertHasDirectory(String parentId, String name) throws Exception { 213 Uri parentUri = buildDocumentUri(mAuthority, parentId); 214 assertHasDirectory(parentUri, name); 215 } 216 assertHasDirectory(RootInfo root, String name)217 public void assertHasDirectory(RootInfo root, String name) throws Exception { 218 assertHasDirectory(root.documentId, name); 219 } 220 assertDoesNotExist(Uri parentUri, String name)221 public void assertDoesNotExist(Uri parentUri, String name) throws Exception { 222 List<DocumentInfo> children = listChildren(parentUri); 223 for (DocumentInfo child : children) { 224 if (name.equals(child.displayName)) { 225 fail("Found name=" + name + " in children " + children); 226 } 227 } 228 } 229 assertDoesNotExist(String parentId, String name)230 public void assertDoesNotExist(String parentId, String name) throws Exception { 231 Uri parentUri = buildDocumentUri(mAuthority, parentId); 232 assertDoesNotExist(parentUri, name); 233 } 234 assertDoesNotExist(RootInfo root, String name)235 public void assertDoesNotExist(RootInfo root, String name) throws Exception { 236 assertDoesNotExist(root.getUri(), name); 237 } 238 findFile(String parentId, String name)239 public @Nullable DocumentInfo findFile(String parentId, String name) 240 throws Exception { 241 List<DocumentInfo> children = listChildren(parentId); 242 for (DocumentInfo child : children) { 243 if (name.equals(child.displayName)) { 244 return child; 245 } 246 } 247 return null; 248 } 249 findDocument(String parentId, String name)250 public DocumentInfo findDocument(String parentId, String name) throws Exception { 251 List<DocumentInfo> children = listChildren(parentId); 252 for (DocumentInfo child : children) { 253 if (name.equals(child.displayName)) { 254 return child; 255 } 256 } 257 return null; 258 } 259 findDocument(Uri parentUri, String name)260 public DocumentInfo findDocument(Uri parentUri, String name) throws Exception { 261 List<DocumentInfo> children = listChildren(parentUri); 262 for (DocumentInfo child : children) { 263 if (name.equals(child.displayName)) { 264 return child; 265 } 266 } 267 return null; 268 } 269 listChildren(Uri parentUri)270 public List<DocumentInfo> listChildren(Uri parentUri) throws Exception { 271 String id = DocumentsContract.getDocumentId(parentUri); 272 return listChildren(id); 273 } 274 listChildren(String documentId)275 public List<DocumentInfo> listChildren(String documentId) throws Exception { 276 return listChildren(documentId, 100); 277 } 278 listChildren(Uri parentUri, int maxCount)279 public List<DocumentInfo> listChildren(Uri parentUri, int maxCount) throws Exception { 280 String id = DocumentsContract.getDocumentId(parentUri); 281 return listChildren(id, maxCount); 282 } 283 listChildren(String documentId, int maxCount)284 public List<DocumentInfo> listChildren(String documentId, int maxCount) throws Exception { 285 Uri uri = buildChildDocumentsUri(mAuthority, documentId); 286 List<DocumentInfo> children = new ArrayList<>(); 287 try (Cursor cursor = mClient.query(uri, null, null, null, null, null)) { 288 Cursor wrapper = new RootCursorWrapper(mAuthority, "totally-fake", cursor, maxCount); 289 while (wrapper.moveToNext()) { 290 children.add(DocumentInfo.fromDirectoryCursor(wrapper)); 291 } 292 } 293 return children; 294 } 295 assertFileContents(Uri documentUri, byte[] expected)296 public void assertFileContents(Uri documentUri, byte[] expected) throws Exception { 297 MoreAsserts.assertEquals( 298 "Copied file contents differ", 299 expected, readDocument(documentUri)); 300 } 301 assertFileContents(String parentId, String fileName, byte[] expected)302 public void assertFileContents(String parentId, String fileName, byte[] expected) 303 throws Exception { 304 DocumentInfo file = findFile(parentId, fileName); 305 assertNotNull(file); 306 assertFileContents(file.derivedUri, expected); 307 } 308 309 /** 310 * A helper method for StubProvider only. Won't work with other providers. 311 * @throws RemoteException 312 */ createVirtualFile( RootInfo root, String path, String mimeType, byte[] content, String... streamTypes)313 public Uri createVirtualFile( 314 RootInfo root, String path, String mimeType, byte[] content, String... streamTypes) 315 throws RemoteException { 316 317 Bundle args = new Bundle(); 318 args.putString(StubProvider.EXTRA_ROOT, root.rootId); 319 args.putString(StubProvider.EXTRA_PATH, path); 320 args.putString(Document.COLUMN_MIME_TYPE, mimeType); 321 args.putStringArrayList(StubProvider.EXTRA_STREAM_TYPES, Lists.newArrayList(streamTypes)); 322 args.putByteArray(StubProvider.EXTRA_CONTENT, content); 323 324 Bundle result = mClient.call("createVirtualFile", null, args); 325 String documentId = result.getString(Document.COLUMN_DOCUMENT_ID); 326 327 return DocumentsContract.buildDocumentUri(mAuthority, documentId); 328 } 329 setLoadingDuration(long duration)330 public void setLoadingDuration(long duration) throws RemoteException { 331 final Bundle extra = new Bundle(); 332 extra.putLong(DocumentsContract.EXTRA_LOADING, duration); 333 mClient.call("setLoadingDuration", null, extra); 334 } 335 configure(String args, Bundle configuration)336 public void configure(String args, Bundle configuration) throws RemoteException { 337 mClient.call("configure", args, configuration); 338 } 339 } 340