blob: f9b06f804bac064c9a40de83c015458a0bdc17b5 [file] [log] [blame]
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +09001/*
2 * Copyright (C) 2016 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
17package com.android.documentsui;
18
19import android.content.Context;
20import android.content.pm.ProviderInfo;
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +090021import android.content.res.AssetFileDescriptor;
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +090022import android.database.Cursor;
23import android.database.MatrixCursor.RowBuilder;
24import android.database.MatrixCursor;
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +090025import android.graphics.Point;
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +090026import android.os.CancellationSignal;
27import android.os.FileUtils;
28import android.os.ParcelFileDescriptor;
29import android.provider.DocumentsContract.Document;
30import android.provider.DocumentsContract.Root;
31import android.provider.DocumentsContract;
32import android.provider.DocumentsProvider;
33
34import java.io.File;
35import java.io.FileNotFoundException;
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +090036import java.io.IOException;
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +090037import java.util.ArrayList;
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +090038import java.util.HashMap;
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +090039import java.util.List;
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +090040import java.util.Map;
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +090041import java.util.Random;
42
43/**
44 * Provider with thousands of files for testing loading time of directories in DocumentsUI.
45 * It doesn't support any file operations.
46 */
47public class StressProvider extends DocumentsProvider {
48
49 public static final String DEFAULT_AUTHORITY = "com.android.documentsui.stressprovider";
50
51 // Empty root.
52 public static final String STRESS_ROOT_0_ID = "STRESS_ROOT_0";
53
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +090054 // Root with thousands of directories.
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +090055 public static final String STRESS_ROOT_1_ID = "STRESS_ROOT_1";
56
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +090057 // Root with hundreds of files.
58 public static final String STRESS_ROOT_2_ID = "STRESS_ROOT_2";
59
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +090060 private static final String STRESS_ROOT_0_DOC_ID = "STRESS_ROOT_0_DOC";
61 private static final String STRESS_ROOT_1_DOC_ID = "STRESS_ROOT_1_DOC";
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +090062 private static final String STRESS_ROOT_2_DOC_ID = "STRESS_ROOT_2_DOC";
63
64 private static final int STRESS_ROOT_1_ITEMS = 10000;
65 private static final int STRESS_ROOT_2_ITEMS = 300;
66
67 private static final String MIME_TYPE_IMAGE = "image/jpeg";
68 private static final long REFERENCE_TIMESTAMP = 1459159369359L;
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +090069
70 private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
71 Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
72 Root.COLUMN_AVAILABLE_BYTES
73 };
74 private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
75 Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
76 Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
77 };
78
79 private String mAuthority = DEFAULT_AUTHORITY;
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +090080
81 // Map from a root document id to children document ids.
82 private Map<String, ArrayList<StubDocument>> mChildDocuments = new HashMap<>();
83
84 private Map<String, StubDocument> mDocuments = new HashMap<>();
85 private Map<String, StubRoot> mRoots = new HashMap<>();
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +090086
87 @Override
88 public void attachInfo(Context context, ProviderInfo info) {
89 mAuthority = info.authority;
90 super.attachInfo(context, info);
91 }
92
93 @Override
94 public boolean onCreate() {
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +090095 StubDocument document;
96
97 ArrayList<StubDocument> children = new ArrayList<StubDocument>();
98 mChildDocuments.put(STRESS_ROOT_1_DOC_ID, children);
99 for (int i = 0; i < STRESS_ROOT_1_ITEMS; i++) {
100 document = StubDocument.createDirectory(i);
101 mDocuments.put(document.id, document);
102 children.add(document);
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900103 }
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900104
105 children = new ArrayList<StubDocument>();
106 mChildDocuments.put(STRESS_ROOT_2_DOC_ID, children);
107 for (int i = 0; i < STRESS_ROOT_2_ITEMS; i++) {
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +0900108 try {
109 document = StubDocument.createFile(
110 getContext(), MIME_TYPE_IMAGE,
111 com.android.documentsui.perftests.R.raw.earth_small,
112 STRESS_ROOT_1_ITEMS + i);
113 } catch (IOException e) {
114 return false;
115 }
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900116 mDocuments.put(document.id, document);
117 children.add(document);
118 }
119
120 mRoots.put(STRESS_ROOT_0_ID, new StubRoot(STRESS_ROOT_0_ID, STRESS_ROOT_0_DOC_ID));
121 mRoots.put(STRESS_ROOT_1_ID, new StubRoot(STRESS_ROOT_1_ID, STRESS_ROOT_1_DOC_ID));
122 mRoots.put(STRESS_ROOT_2_ID, new StubRoot(STRESS_ROOT_2_ID, STRESS_ROOT_2_DOC_ID));
123
124 mDocuments.put(STRESS_ROOT_0_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_0_DOC_ID));
125 mDocuments.put(STRESS_ROOT_1_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_1_DOC_ID));
126 mDocuments.put(STRESS_ROOT_2_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_2_DOC_ID));
127
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900128 return true;
129 }
130
131 @Override
132 public Cursor queryRoots(String[] projection) throws FileNotFoundException {
133 final MatrixCursor result = new MatrixCursor(DEFAULT_ROOT_PROJECTION);
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900134 for (StubRoot root : mRoots.values()) {
135 includeRoot(result, root);
136 }
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900137 return result;
138 }
139
140 @Override
141 public Cursor queryDocument(String documentId, String[] projection)
142 throws FileNotFoundException {
143 final MatrixCursor result = new MatrixCursor(DEFAULT_DOCUMENT_PROJECTION);
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900144 final StubDocument document = mDocuments.get(documentId);
145 includeDocument(result, document);
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900146 return result;
147 }
148
149 @Override
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900150 public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
151 String sortOrder)
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900152 throws FileNotFoundException {
153 final MatrixCursor result = new MatrixCursor(DEFAULT_DOCUMENT_PROJECTION);
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900154 final ArrayList<StubDocument> childDocuments = mChildDocuments.get(parentDocumentId);
155 if (childDocuments != null) {
156 for (StubDocument document : childDocuments) {
157 includeDocument(result, document);
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900158 }
159 }
160 return result;
161 }
162
163 @Override
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +0900164 public AssetFileDescriptor openDocumentThumbnail(String docId, Point sizeHint,
165 CancellationSignal signal)
166 throws FileNotFoundException {
167 final StubDocument document = mDocuments.get(docId);
168 return getContext().getResources().openRawResourceFd(document.thumbnail);
169 }
170
171 @Override
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900172 public ParcelFileDescriptor openDocument(String docId, String mode,
173 CancellationSignal signal)
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900174 throws FileNotFoundException {
175 throw new UnsupportedOperationException();
176 }
177
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900178 private void includeRoot(MatrixCursor result, StubRoot root) {
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900179 final RowBuilder row = result.newRow();
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900180 row.add(Root.COLUMN_ROOT_ID, root.id);
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900181 row.add(Root.COLUMN_FLAGS, 0);
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900182 row.add(Root.COLUMN_TITLE, root.id);
183 row.add(Root.COLUMN_DOCUMENT_ID, root.documentId);
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900184 }
185
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900186 private void includeDocument(MatrixCursor result, StubDocument document) {
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900187 final RowBuilder row = result.newRow();
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900188 row.add(Document.COLUMN_DOCUMENT_ID, document.id);
189 row.add(Document.COLUMN_DISPLAY_NAME, document.id);
190 row.add(Document.COLUMN_SIZE, document.size);
191 row.add(Document.COLUMN_MIME_TYPE, document.mimeType);
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +0900192 row.add(Document.COLUMN_FLAGS,
193 document.thumbnail != -1 ? Document.FLAG_SUPPORTS_THUMBNAIL : 0);
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900194 row.add(Document.COLUMN_LAST_MODIFIED, document.lastModified);
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900195 }
196
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900197 private static String getStubDocumentIdForFile(File file) {
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900198 return file.getAbsolutePath();
199 }
200
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900201 private static class StubDocument {
202 final String mimeType;
203 final String id;
204 final int size;
205 final long lastModified;
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +0900206 final int thumbnail;
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900207
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +0900208 private StubDocument(String mimeType, String id, int size, long lastModified,
209 int thumbnail) {
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900210 this.mimeType = mimeType;
211 this.id = id;
212 this.size = size;
213 this.lastModified = lastModified;
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +0900214 this.thumbnail = thumbnail;
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900215 }
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900216
217 public static StubDocument createDirectory(int index) {
218 return new StubDocument(
219 DocumentsContract.Document.MIME_TYPE_DIR, createRandomId(index), 0,
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +0900220 createRandomTime(index), -1);
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900221 }
222
223 public static StubDocument createDirectory(String id) {
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +0900224 return new StubDocument(DocumentsContract.Document.MIME_TYPE_DIR, id, 0, 0, -1);
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900225 }
226
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +0900227 public static StubDocument createFile(Context context, String mimeType, int thumbnail,
228 int index) throws IOException {
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900229 return new StubDocument(
Tomasz Mikolajewskif3121f42016-03-29 17:46:53 +0900230 mimeType, createRandomId(index), createRandomSize(index),
231 createRandomTime(index), thumbnail);
Tomasz Mikolajewski811777e2016-03-28 12:13:43 +0900232 }
233
234 private static String createRandomId(int index) {
235 final Random random = new Random(index);
236 final StringBuilder builder = new StringBuilder();
237 for (int i = 0; i < 20; i++) {
238 builder.append((char) (random.nextInt(96) + 32));
239 }
240 builder.append(index); // Append a number to guarantee uniqueness.
241 return builder.toString();
242 }
243
244 private static int createRandomSize(int index) {
245 final Random random = new Random(index);
246 return random.nextInt(1024 * 1024 * 100); // Up to 100 MB.
247 }
248
249 private static long createRandomTime(int index) {
250 final Random random = new Random(index);
251 // Up to 30 days backwards from REFERENCE_TIMESTAMP.
252 return REFERENCE_TIMESTAMP - random.nextLong() % 1000L * 60 * 60 * 24 * 30;
253 }
254 }
255
256 private static class StubRoot {
257 final String id;
258 final String documentId;
259
260 public StubRoot(String id, String documentId) {
261 this.id = id;
262 this.documentId = documentId;
263 }
Tomasz Mikolajewskie29e3412016-02-24 12:53:44 +0900264 }
265}