1954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey/* 2954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * Copyright (C) 2013 The Android Open Source Project 3954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * 4954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 5954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * you may not use this file except in compliance with the License. 6954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * You may obtain a copy of the License at 7954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * 8954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 9954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * 10954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 11954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 12954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * See the License for the specific language governing permissions and 14954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * limitations under the License. 15954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey */ 16954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 17954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeypackage com.android.externalstorage; 18954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 19954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.content.ContentResolver; 20a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkeyimport android.content.Context; 21a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkeyimport android.content.pm.ProviderInfo; 22de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.content.res.AssetFileDescriptor; 23954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.database.Cursor; 24954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.database.MatrixCursor; 25954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.database.MatrixCursor.RowBuilder; 26de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Bitmap; 27de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Bitmap.CompressFormat; 28de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Canvas; 29de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Color; 30de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Paint; 31de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Point; 32954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.net.Uri; 33de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.os.AsyncTask; 34954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.os.Bundle; 35954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.os.CancellationSignal; 36b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkeyimport android.os.CancellationSignal.OnCancelListener; 37954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.os.ParcelFileDescriptor; 38954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.os.SystemClock; 39954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsContract; 40954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsContract.Document; 41954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsContract.Root; 42954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsProvider; 43954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.util.Log; 44954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 45de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport libcore.io.IoUtils; 46de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport libcore.io.Streams; 47de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey 48de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.ByteArrayInputStream; 49de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.ByteArrayOutputStream; 50954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport java.io.FileNotFoundException; 51de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.FileOutputStream; 52de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.IOException; 53954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport java.lang.ref.WeakReference; 54954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 55954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeypublic class TestDocumentsProvider extends DocumentsProvider { 56954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String TAG = "TestDocuments"; 57954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 58b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey private static final boolean LAG = false; 59b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey 605d321d472d9983db52610393e6506e2b2d2da4bfJeff Sharkey private static final boolean ROOT_LAME_PROJECTION = false; 615d321d472d9983db52610393e6506e2b2d2da4bfJeff Sharkey private static final boolean DOCUMENT_LAME_PROJECTION = false; 625d321d472d9983db52610393e6506e2b2d2da4bfJeff Sharkey 637aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean ROOTS_WEDGE = false; 647aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean ROOTS_CRASH = false; 657aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean ROOTS_REFRESH = false; 66a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey 677aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean DOCUMENT_CRASH = false; 687aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 697aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean RECENT_WEDGE = false; 707aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 717aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean CHILD_WEDGE = false; 727aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean CHILD_CRASH = false; 737aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 74d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey private static final boolean THUMB_HUNDREDS = false; 757aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean THUMB_WEDGE = false; 767aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean THUMB_CRASH = false; 77954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 78954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String MY_ROOT_ID = "myRoot"; 79954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String MY_DOC_ID = "myDoc"; 80954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String MY_DOC_NULL = "myNull"; 81954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 82954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { 836efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, 84954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID, 85954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Root.COLUMN_AVAILABLE_BYTES, 86954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey }; 87954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 88954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] { 89954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME, 90954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE, 91954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey }; 92954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 93954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static String[] resolveRootProjection(String[] projection) { 945d321d472d9983db52610393e6506e2b2d2da4bfJeff Sharkey if (ROOT_LAME_PROJECTION) return new String[0]; 95954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return projection != null ? projection : DEFAULT_ROOT_PROJECTION; 96954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 97954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 98954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static String[] resolveDocumentProjection(String[] projection) { 995d321d472d9983db52610393e6506e2b2d2da4bfJeff Sharkey if (DOCUMENT_LAME_PROJECTION) return new String[0]; 100954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION; 101954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 102954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 103a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey private String mAuthority; 104a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey 105a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey @Override 106a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey public void attachInfo(Context context, ProviderInfo info) { 107a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey mAuthority = info.authority; 108a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey super.attachInfo(context, info); 109a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey } 110a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey 111954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 112954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Cursor queryRoots(String[] projection) throws FileNotFoundException { 113a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey Log.d(TAG, "Someone asked for our roots!"); 114a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey 115b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey if (LAG) lagUntilCanceled(null); 116b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey if (ROOTS_WEDGE) wedgeUntilCanceled(null); 1177aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (ROOTS_CRASH) System.exit(12); 118954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 1197aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (ROOTS_REFRESH) { 120a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey new AsyncTask<Void, Void, Void>() { 121a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey @Override 122a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey protected Void doInBackground(Void... params) { 123a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey SystemClock.sleep(3000); 124a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey Log.d(TAG, "Notifying that something changed!!"); 125a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey final Uri uri = DocumentsContract.buildRootsUri(mAuthority); 126a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey getContext().getContentResolver().notifyChange(uri, null, false); 127a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey return null; 128a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey } 129a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey }.execute(); 130a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey } 131a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey 132954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection)); 133954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final RowBuilder row = result.newRow(); 134b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Root.COLUMN_ROOT_ID, MY_ROOT_ID); 13504d45a0bdd47c243ad7ccb179aa52a83e7bf45dbJeff Sharkey row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS | Root.FLAG_SUPPORTS_CREATE); 136b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Root.COLUMN_TITLE, "_Test title which is really long"); 137a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey row.add(Root.COLUMN_SUMMARY, 138a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey SystemClock.elapsedRealtime() + " summary which is also super long text"); 139b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Root.COLUMN_DOCUMENT_ID, MY_DOC_ID); 140b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Root.COLUMN_AVAILABLE_BYTES, 1024); 141954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return result; 142954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 143954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 144954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 145954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Cursor queryDocument(String documentId, String[] projection) 146954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey throws FileNotFoundException { 147b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey if (LAG) lagUntilCanceled(null); 1487aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (DOCUMENT_CRASH) System.exit(12); 149954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 150954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); 151de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, documentId, 0); 152954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return result; 153954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 154954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 15504d45a0bdd47c243ad7ccb179aa52a83e7bf45dbJeff Sharkey @Override 15604d45a0bdd47c243ad7ccb179aa52a83e7bf45dbJeff Sharkey public String createDocument(String parentDocumentId, String mimeType, String displayName) 15704d45a0bdd47c243ad7ccb179aa52a83e7bf45dbJeff Sharkey throws FileNotFoundException { 15804d45a0bdd47c243ad7ccb179aa52a83e7bf45dbJeff Sharkey if (LAG) lagUntilCanceled(null); 15904d45a0bdd47c243ad7ccb179aa52a83e7bf45dbJeff Sharkey 16004d45a0bdd47c243ad7ccb179aa52a83e7bf45dbJeff Sharkey return super.createDocument(parentDocumentId, mimeType, displayName); 16104d45a0bdd47c243ad7ccb179aa52a83e7bf45dbJeff Sharkey } 16204d45a0bdd47c243ad7ccb179aa52a83e7bf45dbJeff Sharkey 163954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey /** 164954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * Holds any outstanding or finished "network" fetching. 165954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey */ 166954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private WeakReference<CloudTask> mTask; 167954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 168954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static class CloudTask implements Runnable { 169954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 170954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private final ContentResolver mResolver; 171954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private final Uri mNotifyUri; 172954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 173954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private volatile boolean mFinished; 174954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 175954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public CloudTask(ContentResolver resolver, Uri notifyUri) { 176954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey mResolver = resolver; 177954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey mNotifyUri = notifyUri; 178954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 179954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 180954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 181954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public void run() { 182954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Pretend to do some network 183954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Log.d(TAG, hashCode() + ": pretending to do some network!"); 184954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey SystemClock.sleep(2000); 185954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Log.d(TAG, hashCode() + ": network done!"); 186954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 187954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey mFinished = true; 188954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 189954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Tell anyone remotely they should requery 190954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey mResolver.notifyChange(mNotifyUri, null, false); 191954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 192954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 193954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public boolean includeIfFinished(MatrixCursor result) { 194954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Log.d(TAG, hashCode() + ": includeIfFinished() found " + mFinished); 195954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey if (mFinished) { 196de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile1", 0); 197de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile2", 0); 198de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile3", 0); 199de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile4", 0); 200de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile5", 0); 201de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile6", 0); 202954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return true; 203954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } else { 204954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return false; 205954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 206954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 207954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 208954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 209954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static class CloudCursor extends MatrixCursor { 210954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Object keepAlive; 211954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public final Bundle extras = new Bundle(); 212954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 213954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public CloudCursor(String[] columnNames) { 214954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey super(columnNames); 215954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 216954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 217954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 218954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Bundle getExtras() { 219954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return extras; 220954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 221954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 222954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 223954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 224954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Cursor queryChildDocuments( 225954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey String parentDocumentId, String[] projection, String sortOrder) 226954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey throws FileNotFoundException { 227954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 228b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey if (LAG) lagUntilCanceled(null); 2297aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (CHILD_WEDGE) SystemClock.sleep(Integer.MAX_VALUE); 2307aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (CHILD_CRASH) System.exit(12); 2317aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 232954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final ContentResolver resolver = getContext().getContentResolver(); 233954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final Uri notifyUri = DocumentsContract.buildDocumentUri( 234954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey "com.example.documents", parentDocumentId); 235954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 236954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey CloudCursor result = new CloudCursor(resolveDocumentProjection(projection)); 237954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey result.setNotificationUri(resolver, notifyUri); 238954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 239954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Always include local results 240de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, MY_DOC_NULL, 0); 241de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "localfile1", 0); 242de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "localfile2", Document.FLAG_SUPPORTS_THUMBNAIL); 243de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "localfile3", 0); 244de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "localfile4", 0); 245954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 246d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey if (THUMB_HUNDREDS) { 247d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey for (int i = 0; i < 256; i++) { 248b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey includeFile(result, "i maded u an picshure" + i, Document.FLAG_SUPPORTS_THUMBNAIL); 249d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 250d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 251d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey 252954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey synchronized (this) { 253954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Try picking up an existing network fetch 254954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey CloudTask task = mTask != null ? mTask.get() : null; 255954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey if (task == null) { 256954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Log.d(TAG, "No network task found; starting!"); 257954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey task = new CloudTask(resolver, notifyUri); 258954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey mTask = new WeakReference<CloudTask>(task); 259954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey new Thread(task).start(); 260954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 261954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Aggressively try freeing weak reference above 262954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey new Thread() { 263954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 264954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public void run() { 265954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey while (mTask.get() != null) { 266954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey SystemClock.sleep(200); 267954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey System.gc(); 268954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey System.runFinalization(); 269954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 270954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Log.d(TAG, "AHA! THE CLOUD TASK WAS GC'ED!"); 271954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 272954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey }.start(); 273954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 274954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 275954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Blend in cloud results if ready 276954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey if (task.includeIfFinished(result)) { 277954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey result.extras.putString(DocumentsContract.EXTRA_INFO, 278954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey "Everything Went Better Than Expected and this message is quite " 279954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey + "long and verbose and maybe even too long"); 280954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey result.extras.putString(DocumentsContract.EXTRA_ERROR, 281954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey "But then again, maybe our server ran into an error, which means " 282954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey + "we're going to have a bad time"); 283954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } else { 284954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey result.extras.putBoolean(DocumentsContract.EXTRA_LOADING, true); 285954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 286954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 287954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Tie the network fetch to the cursor GC lifetime 288954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey result.keepAlive = task; 289954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 290954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return result; 291954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 292954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 293954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 294954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 295954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Cursor queryRecentDocuments(String rootId, String[] projection) 296954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey throws FileNotFoundException { 2977aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 298b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey if (LAG) lagUntilCanceled(null); 299b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey if (RECENT_WEDGE) wedgeUntilCanceled(null); 3007aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 301954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Pretend to take a super long time to respond 302954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey SystemClock.sleep(3000); 303954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 304954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); 305de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile( 306de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey result, "It was /worth/ the_wait for?the file:with the&incredibly long name", 0); 307954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return result; 308954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 309954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 310954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 311954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal) 312954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey throws FileNotFoundException { 313b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey if (LAG) lagUntilCanceled(null); 314954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey throw new FileNotFoundException(); 315954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 316954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 317954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 318de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey public AssetFileDescriptor openDocumentThumbnail( 319de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException { 3207aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 321b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey if (LAG) lagUntilCanceled(signal); 322d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey if (THUMB_WEDGE) wedgeUntilCanceled(signal); 3237aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (THUMB_CRASH) System.exit(12); 3247aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 325de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888); 326de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final Canvas canvas = new Canvas(bitmap); 327de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final Paint paint = new Paint(); 328de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey paint.setColor(Color.BLUE); 329de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey canvas.drawColor(Color.RED); 330de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey canvas.drawLine(0, 0, 32, 32, paint); 331de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey 332de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 333de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey bitmap.compress(CompressFormat.JPEG, 50, bos); 334de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey 335de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); 336de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey try { 337de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createReliablePipe(); 338de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey new AsyncTask<Object, Object, Object>() { 339de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey @Override 340de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey protected Object doInBackground(Object... params) { 341de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final FileOutputStream fos = new FileOutputStream(fds[1].getFileDescriptor()); 342de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey try { 343de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey Streams.copy(bis, fos); 344de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } catch (IOException e) { 345de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey throw new RuntimeException(e); 346de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } 347de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey IoUtils.closeQuietly(fds[1]); 348de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey return null; 349de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } 350de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 351de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey return new AssetFileDescriptor(fds[0], 0, AssetFileDescriptor.UNKNOWN_LENGTH); 352de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } catch (IOException e) { 353de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey throw new FileNotFoundException(e.getMessage()); 354de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } 355de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } 356de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey 357de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey @Override 358954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public boolean onCreate() { 359954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return true; 360954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 361954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 362b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey private static void lagUntilCanceled(CancellationSignal signal) { 363b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey waitForCancelOrTimeout(signal, 1500); 364b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey } 365b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey 366d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey private static void wedgeUntilCanceled(CancellationSignal signal) { 367b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey waitForCancelOrTimeout(signal, Integer.MAX_VALUE); 368b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey } 369b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey 370b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey private static void waitForCancelOrTimeout( 371b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey final CancellationSignal signal, long timeoutMillis) { 372d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey if (signal != null) { 373b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey final Thread blocked = Thread.currentThread(); 374b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey signal.setOnCancelListener(new OnCancelListener() { 375b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey @Override 376b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey public void onCancel() { 377b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey blocked.interrupt(); 378b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey } 379b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey }); 380b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey signal.throwIfCanceled(); 381b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey } 382b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey 383b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey try { 384b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey Thread.sleep(timeoutMillis); 385b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey } catch (InterruptedException e) { 386b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey } 387b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey 388b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey if (signal != null) { 389b3d42635aafacd80b1e1d257338ea6abb93d22c3Jeff Sharkey signal.throwIfCanceled(); 390d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 391d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 392d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey 393de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey private static void includeFile(MatrixCursor result, String docId, int flags) { 394954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final RowBuilder row = result.newRow(); 395b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Document.COLUMN_DOCUMENT_ID, docId); 396b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Document.COLUMN_DISPLAY_NAME, docId); 397b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Document.COLUMN_LAST_MODIFIED, System.currentTimeMillis()); 398de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey row.add(Document.COLUMN_FLAGS, flags); 399954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 400954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey if (MY_DOC_ID.equals(docId)) { 401b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR); 40204d45a0bdd47c243ad7ccb179aa52a83e7bf45dbJeff Sharkey row.add(Document.COLUMN_FLAGS, Document.FLAG_DIR_SUPPORTS_CREATE); 403954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } else if (MY_DOC_NULL.equals(docId)) { 404954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // No MIME type 405954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } else { 406b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Document.COLUMN_MIME_TYPE, "application/octet-stream"); 407954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 408954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 409954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey} 410