| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.documentsui; |
| |
| import static android.content.ContentResolver.wrap; |
| import static android.provider.DocumentsContract.buildChildDocumentsUri; |
| import static android.provider.DocumentsContract.buildDocumentUri; |
| import static android.provider.DocumentsContract.buildRootsUri; |
| |
| import static androidx.core.util.Preconditions.checkArgument; |
| |
| import static com.android.documentsui.base.DocumentInfo.getCursorString; |
| |
| import static junit.framework.Assert.assertEquals; |
| import static junit.framework.Assert.assertNotNull; |
| import static junit.framework.Assert.fail; |
| |
| import android.content.ContentProviderClient; |
| import android.content.Context; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.FileUtils; |
| import android.os.ParcelFileDescriptor; |
| import android.os.ParcelFileDescriptor.AutoCloseInputStream; |
| import android.os.ParcelFileDescriptor.AutoCloseOutputStream; |
| import android.os.RemoteException; |
| import android.provider.DocumentsContract; |
| import android.provider.DocumentsContract.Document; |
| import android.provider.DocumentsContract.Root; |
| import android.test.MoreAsserts; |
| import android.text.TextUtils; |
| |
| import androidx.annotation.Nullable; |
| |
| import com.android.documentsui.base.DocumentInfo; |
| import com.android.documentsui.base.RootInfo; |
| import com.android.documentsui.base.UserId; |
| import com.android.documentsui.roots.RootCursorWrapper; |
| |
| import com.google.common.collect.Lists; |
| |
| import libcore.io.Streams; |
| |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Provides support for creation of documents in a test settings. |
| */ |
| public class DocumentsProviderHelper { |
| |
| private final UserId mUserId; |
| private final String mAuthority; |
| private final ContentProviderClient mClient; |
| |
| public DocumentsProviderHelper(UserId userId, String authority, Context context, String name) { |
| checkArgument(!TextUtils.isEmpty(authority)); |
| mUserId = userId; |
| mAuthority = authority; |
| mClient = userId.getContentResolver(context).acquireContentProviderClient(name); |
| } |
| |
| public RootInfo getRoot(String documentId) throws RemoteException { |
| final Uri rootsUri = buildRootsUri(mAuthority); |
| Cursor cursor = null; |
| try { |
| cursor = mClient.query(rootsUri, null, null, null, null); |
| while (cursor.moveToNext()) { |
| if (documentId.equals(getCursorString(cursor, Root.COLUMN_ROOT_ID))) { |
| return RootInfo.fromRootsCursor(mUserId, mAuthority, cursor); |
| } |
| } |
| throw new IllegalArgumentException("Can't find matching root for id=" + documentId); |
| } catch (Exception e) { |
| throw new RuntimeException("Can't load root for id=" + documentId , e); |
| } finally { |
| FileUtils.closeQuietly(cursor); |
| } |
| } |
| |
| public Uri createDocument(Uri parentUri, String mimeType, String name) { |
| if (name.contains("/")) { |
| throw new IllegalArgumentException("Name and mimetype probably interposed."); |
| } |
| try { |
| Uri uri = DocumentsContract.createDocument(wrap(mClient), parentUri, mimeType, name); |
| return uri; |
| } catch (FileNotFoundException e) { |
| throw new RuntimeException("Couldn't create document: " + name + " with mimetype " |
| + mimeType, e); |
| } |
| } |
| |
| public Uri createDocument(String parentId, String mimeType, String name) { |
| Uri parentUri = buildDocumentUri(mAuthority, parentId); |
| return createDocument(parentUri, mimeType, name); |
| } |
| |
| public Uri createDocument(RootInfo root, String mimeType, String name) { |
| return createDocument(root.documentId, mimeType, name); |
| } |
| |
| public Uri createDocumentWithFlags(String documentId, String mimeType, String name, int flags, |
| String... streamTypes) |
| throws RemoteException { |
| Bundle in = new Bundle(); |
| in.putInt(StubProvider.EXTRA_FLAGS, flags); |
| in.putString(StubProvider.EXTRA_PARENT_ID, documentId); |
| in.putString(Document.COLUMN_MIME_TYPE, mimeType); |
| in.putString(Document.COLUMN_DISPLAY_NAME, name); |
| in.putStringArrayList(StubProvider.EXTRA_STREAM_TYPES, Lists.newArrayList(streamTypes)); |
| |
| Bundle out = mClient.call("createDocumentWithFlags", null, in); |
| Uri uri = out.getParcelable(DocumentsContract.EXTRA_URI); |
| return uri; |
| } |
| |
| public Uri createFolder(Uri parentUri, String name) { |
| return createDocument(parentUri, Document.MIME_TYPE_DIR, name); |
| } |
| |
| public Uri createFolder(String parentId, String name) { |
| Uri parentUri = buildDocumentUri(mAuthority, parentId); |
| return createDocument(parentUri, Document.MIME_TYPE_DIR, name); |
| } |
| |
| public Uri createFolder(RootInfo root, String name) { |
| return createDocument(root, Document.MIME_TYPE_DIR, name); |
| } |
| |
| public void writeDocument(Uri documentUri, byte[] contents) |
| throws RemoteException, IOException { |
| ParcelFileDescriptor file = mClient.openFile(documentUri, "w", null); |
| try (AutoCloseOutputStream out = new AutoCloseOutputStream(file)) { |
| out.write(contents, 0, contents.length); |
| } |
| waitForWrite(); |
| } |
| |
| public void writeAppendDocument(Uri documentUri, byte[] contents, int length) |
| throws RemoteException, IOException { |
| ParcelFileDescriptor file = mClient.openFile(documentUri, "wa", null); |
| try (AutoCloseOutputStream out = new AutoCloseOutputStream(file)) { |
| out.write(contents, 0, length); |
| } |
| waitForWrite(); |
| } |
| |
| public void waitForWrite() throws RemoteException { |
| mClient.call("waitForWrite", null, null); |
| } |
| |
| public byte[] readDocument(Uri documentUri) throws RemoteException, IOException { |
| ParcelFileDescriptor file = mClient.openFile(documentUri, "r", null); |
| byte[] buf = null; |
| try (AutoCloseInputStream in = new AutoCloseInputStream(file)) { |
| buf = Streams.readFully(in); |
| } |
| return buf; |
| } |
| |
| public void assertChildCount(Uri parentUri, int expected) throws Exception { |
| List<DocumentInfo> children = listChildren(parentUri); |
| assertEquals("Incorrect file count after copy", expected, children.size()); |
| } |
| |
| public void assertChildCount(String parentId, int expected) throws Exception { |
| List<DocumentInfo> children = listChildren(parentId, -1); |
| assertEquals("Incorrect file count after copy", expected, children.size()); |
| } |
| |
| public void assertChildCount(RootInfo root, int expected) throws Exception { |
| assertChildCount(root.documentId, expected); |
| } |
| |
| public void assertHasFile(Uri parentUri, String name) throws Exception { |
| List<DocumentInfo> children = listChildren(parentUri); |
| for (DocumentInfo child : children) { |
| if (name.equals(child.displayName) && !child.isDirectory()) { |
| return; |
| } |
| } |
| fail("Could not find file named=" + name + " in children " + children); |
| } |
| |
| public void assertHasFile(String parentId, String name) throws Exception { |
| Uri parentUri = buildDocumentUri(mAuthority, parentId); |
| assertHasFile(parentUri, name); |
| } |
| |
| public void assertHasFile(RootInfo root, String name) throws Exception { |
| assertHasFile(root.documentId, name); |
| } |
| |
| public void assertHasDirectory(Uri parentUri, String name) throws Exception { |
| List<DocumentInfo> children = listChildren(parentUri); |
| for (DocumentInfo child : children) { |
| if (name.equals(child.displayName) && child.isDirectory()) { |
| return; |
| } |
| } |
| fail("Could not find name=" + name + " in children " + children); |
| } |
| |
| public void assertHasDirectory(String parentId, String name) throws Exception { |
| Uri parentUri = buildDocumentUri(mAuthority, parentId); |
| assertHasDirectory(parentUri, name); |
| } |
| |
| public void assertHasDirectory(RootInfo root, String name) throws Exception { |
| assertHasDirectory(root.documentId, name); |
| } |
| |
| public void assertDoesNotExist(Uri parentUri, String name) throws Exception { |
| List<DocumentInfo> children = listChildren(parentUri); |
| for (DocumentInfo child : children) { |
| if (name.equals(child.displayName)) { |
| fail("Found name=" + name + " in children " + children); |
| } |
| } |
| } |
| |
| public void assertDoesNotExist(String parentId, String name) throws Exception { |
| Uri parentUri = buildDocumentUri(mAuthority, parentId); |
| assertDoesNotExist(parentUri, name); |
| } |
| |
| public void assertDoesNotExist(RootInfo root, String name) throws Exception { |
| assertDoesNotExist(root.getUri(), name); |
| } |
| |
| public @Nullable DocumentInfo findFile(String parentId, String name) |
| throws Exception { |
| List<DocumentInfo> children = listChildren(parentId); |
| for (DocumentInfo child : children) { |
| if (name.equals(child.displayName)) { |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| public DocumentInfo findDocument(String parentId, String name) throws Exception { |
| List<DocumentInfo> children = listChildren(parentId); |
| for (DocumentInfo child : children) { |
| if (name.equals(child.displayName)) { |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| public DocumentInfo findDocument(Uri parentUri, String name) throws Exception { |
| List<DocumentInfo> children = listChildren(parentUri); |
| for (DocumentInfo child : children) { |
| if (name.equals(child.displayName)) { |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| public List<DocumentInfo> listChildren(Uri parentUri) throws Exception { |
| String id = DocumentsContract.getDocumentId(parentUri); |
| return listChildren(id); |
| } |
| |
| public List<DocumentInfo> listChildren(String documentId) throws Exception { |
| return listChildren(documentId, 100); |
| } |
| |
| public List<DocumentInfo> listChildren(Uri parentUri, int maxCount) throws Exception { |
| String id = DocumentsContract.getDocumentId(parentUri); |
| return listChildren(id, maxCount); |
| } |
| |
| public List<DocumentInfo> listChildren(String documentId, int maxCount) throws Exception { |
| Uri uri = buildChildDocumentsUri(mAuthority, documentId); |
| List<DocumentInfo> children = new ArrayList<>(); |
| try (Cursor cursor = mClient.query(uri, null, null, null, null, null)) { |
| Cursor wrapper = new RootCursorWrapper(mUserId, mAuthority, "totally-fake", cursor, |
| maxCount); |
| while (wrapper.moveToNext()) { |
| children.add(DocumentInfo.fromDirectoryCursor(wrapper)); |
| } |
| } |
| return children; |
| } |
| |
| public void assertFileContents(Uri documentUri, byte[] expected) throws Exception { |
| MoreAsserts.assertEquals( |
| "Copied file contents differ", |
| expected, readDocument(documentUri)); |
| } |
| |
| public void assertFileContents(String parentId, String fileName, byte[] expected) |
| throws Exception { |
| DocumentInfo file = findFile(parentId, fileName); |
| assertNotNull(file); |
| assertFileContents(file.derivedUri, expected); |
| } |
| |
| /** |
| * A helper method for StubProvider only. Won't work with other providers. |
| * @throws RemoteException |
| */ |
| public Uri createVirtualFile( |
| RootInfo root, String path, String mimeType, byte[] content, String... streamTypes) |
| throws RemoteException { |
| |
| Bundle args = new Bundle(); |
| args.putString(StubProvider.EXTRA_ROOT, root.rootId); |
| args.putString(StubProvider.EXTRA_PATH, path); |
| args.putString(Document.COLUMN_MIME_TYPE, mimeType); |
| args.putStringArrayList(StubProvider.EXTRA_STREAM_TYPES, Lists.newArrayList(streamTypes)); |
| args.putByteArray(StubProvider.EXTRA_CONTENT, content); |
| |
| Bundle result = mClient.call("createVirtualFile", null, args); |
| String documentId = result.getString(Document.COLUMN_DOCUMENT_ID); |
| |
| return DocumentsContract.buildDocumentUri(mAuthority, documentId); |
| } |
| |
| public void setLoadingDuration(long duration) throws RemoteException { |
| final Bundle extra = new Bundle(); |
| extra.putLong(DocumentsContract.EXTRA_LOADING, duration); |
| mClient.call("setLoadingDuration", null, extra); |
| } |
| |
| public void configure(String args, Bundle configuration) throws RemoteException { |
| mClient.call("configure", args, configuration); |
| } |
| |
| public void simulateReadErrorsForFile(String args, Bundle configuration) |
| throws RemoteException { |
| mClient.call("simulateReadErrorsForFile", args, configuration); |
| } |
| |
| public void clear(String args, Bundle configuration) throws RemoteException { |
| mClient.call("clear", args, configuration); |
| } |
| |
| public List<RootInfo> getRootList() throws RemoteException { |
| List<RootInfo> list = new ArrayList<>(); |
| final Uri rootsUri = DocumentsContract.buildRootsUri(mAuthority); |
| Cursor cursor = null; |
| try { |
| cursor = mClient.query(rootsUri, null, null, null, null); |
| while (cursor.moveToNext()) { |
| RootInfo rootInfo = RootInfo.fromRootsCursor(mUserId, mAuthority, cursor); |
| if (rootInfo != null) { |
| list.add(rootInfo); |
| } |
| } |
| } catch (Exception e) { |
| throw new RuntimeException("Can't load rootInfo list", e); |
| } finally { |
| FileUtils.closeQuietly(cursor); |
| } |
| return list; |
| } |
| |
| public void cleanUp() { |
| mClient.close(); |
| } |
| } |