Home | History | Annotate | Download | only in documentsui
      1 /*
      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 
     17 package com.android.documentsui;
     18 
     19 import android.content.Context;
     20 import android.content.pm.ProviderInfo;
     21 import android.content.res.AssetFileDescriptor;
     22 import android.database.Cursor;
     23 import android.database.MatrixCursor.RowBuilder;
     24 import android.database.MatrixCursor;
     25 import android.graphics.Point;
     26 import android.os.CancellationSignal;
     27 import android.os.FileUtils;
     28 import android.os.ParcelFileDescriptor;
     29 import android.provider.DocumentsContract.Document;
     30 import android.provider.DocumentsContract.Root;
     31 import android.provider.DocumentsContract;
     32 import android.provider.DocumentsProvider;
     33 
     34 import java.io.File;
     35 import java.io.FileNotFoundException;
     36 import java.io.IOException;
     37 import java.util.ArrayList;
     38 import java.util.HashMap;
     39 import java.util.List;
     40 import java.util.Map;
     41 import 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  */
     47 public 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 
     54     // Root with thousands of directories.
     55     public static final String STRESS_ROOT_1_ID = "STRESS_ROOT_1";
     56 
     57     // Root with hundreds of files.
     58     public static final String STRESS_ROOT_2_ID = "STRESS_ROOT_2";
     59 
     60     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";
     62     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;
     69 
     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;
     80 
     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<>();
     86 
     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() {
     95         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);
    103         }
    104 
    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++) {
    108             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             }
    116             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 
    128         return true;
    129     }
    130 
    131     @Override
    132     public Cursor queryRoots(String[] projection) throws FileNotFoundException {
    133         final MatrixCursor result = new MatrixCursor(DEFAULT_ROOT_PROJECTION);
    134         for (StubRoot root : mRoots.values()) {
    135             includeRoot(result, root);
    136         }
    137         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);
    144         final StubDocument document = mDocuments.get(documentId);
    145         includeDocument(result, document);
    146         return result;
    147     }
    148 
    149     @Override
    150     public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
    151             String sortOrder)
    152             throws FileNotFoundException {
    153         final MatrixCursor result = new MatrixCursor(DEFAULT_DOCUMENT_PROJECTION);
    154         final ArrayList<StubDocument> childDocuments = mChildDocuments.get(parentDocumentId);
    155         if (childDocuments != null) {
    156             for (StubDocument document : childDocuments) {
    157                 includeDocument(result, document);
    158             }
    159         }
    160         return result;
    161     }
    162 
    163     @Override
    164     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
    172     public ParcelFileDescriptor openDocument(String docId, String mode,
    173             CancellationSignal signal)
    174             throws FileNotFoundException {
    175         throw new UnsupportedOperationException();
    176     }
    177 
    178     private void includeRoot(MatrixCursor result, StubRoot root) {
    179         final RowBuilder row = result.newRow();
    180         row.add(Root.COLUMN_ROOT_ID, root.id);
    181         row.add(Root.COLUMN_FLAGS, 0);
    182         row.add(Root.COLUMN_TITLE, root.id);
    183         row.add(Root.COLUMN_DOCUMENT_ID, root.documentId);
    184     }
    185 
    186     private void includeDocument(MatrixCursor result, StubDocument document) {
    187         final RowBuilder row = result.newRow();
    188         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);
    192         row.add(Document.COLUMN_FLAGS,
    193                 document.thumbnail != -1 ? Document.FLAG_SUPPORTS_THUMBNAIL : 0);
    194         row.add(Document.COLUMN_LAST_MODIFIED, document.lastModified);
    195     }
    196 
    197     private static String getStubDocumentIdForFile(File file) {
    198         return file.getAbsolutePath();
    199     }
    200 
    201     private static class StubDocument {
    202         final String mimeType;
    203         final String id;
    204         final int size;
    205         final long lastModified;
    206         final int thumbnail;
    207 
    208         private StubDocument(String mimeType, String id, int size, long lastModified,
    209                 int thumbnail) {
    210             this.mimeType = mimeType;
    211             this.id = id;
    212             this.size = size;
    213             this.lastModified = lastModified;
    214             this.thumbnail = thumbnail;
    215         }
    216 
    217         public static StubDocument createDirectory(int index) {
    218             return new StubDocument(
    219                     DocumentsContract.Document.MIME_TYPE_DIR, createRandomId(index), 0,
    220                     createRandomTime(index), -1);
    221         }
    222 
    223         public static StubDocument createDirectory(String id) {
    224             return new StubDocument(DocumentsContract.Document.MIME_TYPE_DIR, id, 0, 0, -1);
    225         }
    226 
    227         public static StubDocument createFile(Context context, String mimeType, int thumbnail,
    228                 int index) throws IOException {
    229             return new StubDocument(
    230                     mimeType, createRandomId(index), createRandomSize(index),
    231                     createRandomTime(index), thumbnail);
    232         }
    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         }
    264     }
    265 }
    266