blob: 14f215cb7d0fab179158139f424384ac30aed8da [file] [log] [blame]
/*
* Copyright (C) 2014 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.cts.documentprovider;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyDocumentsProvider extends DocumentsProvider {
private static final String TAG = "TestDocumentsProvider";
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES,
};
private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
};
private static String[] resolveRootProjection(String[] projection) {
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
}
private static String[] resolveDocumentProjection(String[] projection) {
return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
}
@Override
public boolean onCreate() {
resetRoots();
return true;
}
@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
RowBuilder row = result.newRow();
row.add(Root.COLUMN_ROOT_ID, "local");
row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY);
row.add(Root.COLUMN_TITLE, "CtsLocal");
row.add(Root.COLUMN_SUMMARY, "CtsLocalSummary");
row.add(Root.COLUMN_DOCUMENT_ID, "doc:local");
row = result.newRow();
row.add(Root.COLUMN_ROOT_ID, "create");
row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD);
row.add(Root.COLUMN_TITLE, "CtsCreate");
row.add(Root.COLUMN_DOCUMENT_ID, "doc:create");
return result;
}
private Map<String, Doc> mDocs = new HashMap<>();
private Doc mLocalRoot;
private Doc mCreateRoot;
private Doc buildDoc(String docId, String displayName, String mimeType) {
final Doc doc = new Doc();
doc.docId = docId;
doc.displayName = displayName;
doc.mimeType = mimeType;
mDocs.put(doc.docId, doc);
return doc;
}
public void resetRoots() {
Log.d(TAG, "resetRoots()");
mDocs.clear();
mLocalRoot = buildDoc("doc:local", null, Document.MIME_TYPE_DIR);
mCreateRoot = buildDoc("doc:create", null, Document.MIME_TYPE_DIR);
mCreateRoot.flags = Document.FLAG_DIR_SUPPORTS_CREATE;
{
Doc file1 = buildDoc("doc:file1", "FILE1", "mime1/file1");
file1.contents = "fileone".getBytes();
file1.flags = Document.FLAG_SUPPORTS_WRITE;
mLocalRoot.children.add(file1);
mCreateRoot.children.add(file1);
}
{
Doc file2 = buildDoc("doc:file2", "FILE2", "mime2/file2");
file2.contents = "filetwo".getBytes();
file2.flags = Document.FLAG_SUPPORTS_WRITE;
mLocalRoot.children.add(file2);
mCreateRoot.children.add(file2);
}
Doc dir1 = buildDoc("doc:dir1", "DIR1", Document.MIME_TYPE_DIR);
mLocalRoot.children.add(dir1);
{
Doc file3 = buildDoc("doc:file3", "FILE3", "mime3/file3");
file3.contents = "filethree".getBytes();
file3.flags = Document.FLAG_SUPPORTS_WRITE;
dir1.children.add(file3);
}
Doc dir2 = buildDoc("doc:dir2", "DIR2", Document.MIME_TYPE_DIR);
mCreateRoot.children.add(dir2);
{
Doc file4 = buildDoc("doc:file4", "FILE4", "mime4/file4");
file4.contents = "filefour".getBytes();
file4.flags = Document.FLAG_SUPPORTS_WRITE;
dir2.children.add(file4);
}
}
private static class Doc {
public String docId;
public int flags;
public String displayName;
public long size;
public String mimeType;
public long lastModified;
public byte[] contents;
public List<Doc> children = new ArrayList<>();
public void include(MatrixCursor result) {
final RowBuilder row = result.newRow();
row.add(Document.COLUMN_DOCUMENT_ID, docId);
row.add(Document.COLUMN_DISPLAY_NAME, displayName);
row.add(Document.COLUMN_SIZE, size);
row.add(Document.COLUMN_MIME_TYPE, mimeType);
row.add(Document.COLUMN_FLAGS, flags);
row.add(Document.COLUMN_LAST_MODIFIED, lastModified);
}
}
@Override
public boolean isChildDocument(String parentDocumentId, String documentId) {
for (Doc doc : mDocs.get(parentDocumentId).children) {
if (doc.docId.equals(documentId)) {
return true;
}
if (Document.MIME_TYPE_DIR.equals(doc.mimeType)) {
return isChildDocument(doc.docId, documentId);
}
}
return false;
}
@Override
public String createDocument(String parentDocumentId, String mimeType, String displayName)
throws FileNotFoundException {
final String docId = "doc:" + System.currentTimeMillis();
final Doc doc = buildDoc(docId, displayName, mimeType);
doc.flags = Document.FLAG_SUPPORTS_WRITE | Document.FLAG_SUPPORTS_RENAME;
mDocs.get(parentDocumentId).children.add(doc);
return docId;
}
@Override
public String renameDocument(String documentId, String displayName)
throws FileNotFoundException {
mDocs.get(documentId).displayName = displayName;
return null;
}
@Override
public void deleteDocument(String documentId) throws FileNotFoundException {
mDocs.remove(documentId);
for (Doc doc : mDocs.values()) {
doc.children.remove(documentId);
}
}
@Override
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
mDocs.get(documentId).include(result);
return result;
}
@Override
public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
String sortOrder) throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
for (Doc doc : mDocs.get(parentDocumentId).children) {
doc.include(result);
}
return result;
}
@Override
public ParcelFileDescriptor openDocument(String documentId, String mode,
CancellationSignal signal) throws FileNotFoundException {
final Doc doc = mDocs.get(documentId);
if (doc == null) {
throw new FileNotFoundException();
}
final ParcelFileDescriptor[] pipe;
try {
pipe = ParcelFileDescriptor.createPipe();
} catch (IOException e) {
throw new IllegalStateException(e);
}
if (mode.contains("w")) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
synchronized (doc) {
try {
final InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
pipe[0]);
doc.contents = readFullyNoClose(is);
is.close();
doc.notifyAll();
} catch (IOException e) {
Log.w(TAG, "Failed to stream", e);
}
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return pipe[1];
} else {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
synchronized (doc) {
try {
final OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(
pipe[1]);
while (doc.contents == null) {
doc.wait();
}
os.write(doc.contents);
os.close();
} catch (IOException e) {
Log.w(TAG, "Failed to stream", e);
} catch (InterruptedException e) {
Log.w(TAG, "Interuppted", e);
}
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return pipe[0];
}
}
private static byte[] readFullyNoClose(InputStream in) throws IOException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count;
while ((count = in.read(buffer)) != -1) {
bytes.write(buffer, 0, count);
}
return bytes.toByteArray();
}
}