blob: f9b06f804bac064c9a40de83c015458a0bdc17b5 [file] [log] [blame]
/*
* Copyright (C) 2016 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 android.content.Context;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.MatrixCursor.RowBuilder;
import android.database.MatrixCursor;
import android.graphics.Point;
import android.os.CancellationSignal;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
/**
* Provider with thousands of files for testing loading time of directories in DocumentsUI.
* It doesn't support any file operations.
*/
public class StressProvider extends DocumentsProvider {
public static final String DEFAULT_AUTHORITY = "com.android.documentsui.stressprovider";
// Empty root.
public static final String STRESS_ROOT_0_ID = "STRESS_ROOT_0";
// Root with thousands of directories.
public static final String STRESS_ROOT_1_ID = "STRESS_ROOT_1";
// Root with hundreds of files.
public static final String STRESS_ROOT_2_ID = "STRESS_ROOT_2";
private static final String STRESS_ROOT_0_DOC_ID = "STRESS_ROOT_0_DOC";
private static final String STRESS_ROOT_1_DOC_ID = "STRESS_ROOT_1_DOC";
private static final String STRESS_ROOT_2_DOC_ID = "STRESS_ROOT_2_DOC";
private static final int STRESS_ROOT_1_ITEMS = 10000;
private static final int STRESS_ROOT_2_ITEMS = 300;
private static final String MIME_TYPE_IMAGE = "image/jpeg";
private static final long REFERENCE_TIMESTAMP = 1459159369359L;
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, 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 String mAuthority = DEFAULT_AUTHORITY;
// Map from a root document id to children document ids.
private Map<String, ArrayList<StubDocument>> mChildDocuments = new HashMap<>();
private Map<String, StubDocument> mDocuments = new HashMap<>();
private Map<String, StubRoot> mRoots = new HashMap<>();
@Override
public void attachInfo(Context context, ProviderInfo info) {
mAuthority = info.authority;
super.attachInfo(context, info);
}
@Override
public boolean onCreate() {
StubDocument document;
ArrayList<StubDocument> children = new ArrayList<StubDocument>();
mChildDocuments.put(STRESS_ROOT_1_DOC_ID, children);
for (int i = 0; i < STRESS_ROOT_1_ITEMS; i++) {
document = StubDocument.createDirectory(i);
mDocuments.put(document.id, document);
children.add(document);
}
children = new ArrayList<StubDocument>();
mChildDocuments.put(STRESS_ROOT_2_DOC_ID, children);
for (int i = 0; i < STRESS_ROOT_2_ITEMS; i++) {
try {
document = StubDocument.createFile(
getContext(), MIME_TYPE_IMAGE,
com.android.documentsui.perftests.R.raw.earth_small,
STRESS_ROOT_1_ITEMS + i);
} catch (IOException e) {
return false;
}
mDocuments.put(document.id, document);
children.add(document);
}
mRoots.put(STRESS_ROOT_0_ID, new StubRoot(STRESS_ROOT_0_ID, STRESS_ROOT_0_DOC_ID));
mRoots.put(STRESS_ROOT_1_ID, new StubRoot(STRESS_ROOT_1_ID, STRESS_ROOT_1_DOC_ID));
mRoots.put(STRESS_ROOT_2_ID, new StubRoot(STRESS_ROOT_2_ID, STRESS_ROOT_2_DOC_ID));
mDocuments.put(STRESS_ROOT_0_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_0_DOC_ID));
mDocuments.put(STRESS_ROOT_1_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_1_DOC_ID));
mDocuments.put(STRESS_ROOT_2_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_2_DOC_ID));
return true;
}
@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(DEFAULT_ROOT_PROJECTION);
for (StubRoot root : mRoots.values()) {
includeRoot(result, root);
}
return result;
}
@Override
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(DEFAULT_DOCUMENT_PROJECTION);
final StubDocument document = mDocuments.get(documentId);
includeDocument(result, document);
return result;
}
@Override
public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
String sortOrder)
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(DEFAULT_DOCUMENT_PROJECTION);
final ArrayList<StubDocument> childDocuments = mChildDocuments.get(parentDocumentId);
if (childDocuments != null) {
for (StubDocument document : childDocuments) {
includeDocument(result, document);
}
}
return result;
}
@Override
public AssetFileDescriptor openDocumentThumbnail(String docId, Point sizeHint,
CancellationSignal signal)
throws FileNotFoundException {
final StubDocument document = mDocuments.get(docId);
return getContext().getResources().openRawResourceFd(document.thumbnail);
}
@Override
public ParcelFileDescriptor openDocument(String docId, String mode,
CancellationSignal signal)
throws FileNotFoundException {
throw new UnsupportedOperationException();
}
private void includeRoot(MatrixCursor result, StubRoot root) {
final RowBuilder row = result.newRow();
row.add(Root.COLUMN_ROOT_ID, root.id);
row.add(Root.COLUMN_FLAGS, 0);
row.add(Root.COLUMN_TITLE, root.id);
row.add(Root.COLUMN_DOCUMENT_ID, root.documentId);
}
private void includeDocument(MatrixCursor result, StubDocument document) {
final RowBuilder row = result.newRow();
row.add(Document.COLUMN_DOCUMENT_ID, document.id);
row.add(Document.COLUMN_DISPLAY_NAME, document.id);
row.add(Document.COLUMN_SIZE, document.size);
row.add(Document.COLUMN_MIME_TYPE, document.mimeType);
row.add(Document.COLUMN_FLAGS,
document.thumbnail != -1 ? Document.FLAG_SUPPORTS_THUMBNAIL : 0);
row.add(Document.COLUMN_LAST_MODIFIED, document.lastModified);
}
private static String getStubDocumentIdForFile(File file) {
return file.getAbsolutePath();
}
private static class StubDocument {
final String mimeType;
final String id;
final int size;
final long lastModified;
final int thumbnail;
private StubDocument(String mimeType, String id, int size, long lastModified,
int thumbnail) {
this.mimeType = mimeType;
this.id = id;
this.size = size;
this.lastModified = lastModified;
this.thumbnail = thumbnail;
}
public static StubDocument createDirectory(int index) {
return new StubDocument(
DocumentsContract.Document.MIME_TYPE_DIR, createRandomId(index), 0,
createRandomTime(index), -1);
}
public static StubDocument createDirectory(String id) {
return new StubDocument(DocumentsContract.Document.MIME_TYPE_DIR, id, 0, 0, -1);
}
public static StubDocument createFile(Context context, String mimeType, int thumbnail,
int index) throws IOException {
return new StubDocument(
mimeType, createRandomId(index), createRandomSize(index),
createRandomTime(index), thumbnail);
}
private static String createRandomId(int index) {
final Random random = new Random(index);
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < 20; i++) {
builder.append((char) (random.nextInt(96) + 32));
}
builder.append(index); // Append a number to guarantee uniqueness.
return builder.toString();
}
private static int createRandomSize(int index) {
final Random random = new Random(index);
return random.nextInt(1024 * 1024 * 100); // Up to 100 MB.
}
private static long createRandomTime(int index) {
final Random random = new Random(index);
// Up to 30 days backwards from REFERENCE_TIMESTAMP.
return REFERENCE_TIMESTAMP - random.nextLong() % 1000L * 60 * 60 * 24 * 30;
}
}
private static class StubRoot {
final String id;
final String documentId;
public StubRoot(String id, String documentId) {
this.id = id;
this.documentId = documentId;
}
}
}