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;
36954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.os.ParcelFileDescriptor;
37954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.os.SystemClock;
38954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsContract;
39954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsContract.Document;
40954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsContract.Root;
41954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsProvider;
42954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.util.Log;
43954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
44de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport libcore.io.IoUtils;
45de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport libcore.io.Streams;
46de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey
47de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.ByteArrayInputStream;
48de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.ByteArrayOutputStream;
49954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport java.io.FileNotFoundException;
50de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.FileOutputStream;
51de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.IOException;
52954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport java.lang.ref.WeakReference;
53954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
54954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeypublic class TestDocumentsProvider extends DocumentsProvider {
55954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    private static final String TAG = "TestDocuments";
56954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
577aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey    private static final boolean ROOTS_WEDGE = false;
587aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey    private static final boolean ROOTS_LAG = false;
597aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey    private static final boolean ROOTS_CRASH = false;
607aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey    private static final boolean ROOTS_REFRESH = false;
61a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey
627aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey    private static final boolean DOCUMENT_CRASH = false;
637aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey
647aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey    private static final boolean RECENT_WEDGE = false;
657aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey
667aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey    private static final boolean CHILD_WEDGE = false;
677aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey    private static final boolean CHILD_CRASH = false;
687aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey
69d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey    private static final boolean THUMB_HUNDREDS = false;
707aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey    private static final boolean THUMB_WEDGE = false;
717aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey    private static final boolean THUMB_CRASH = false;
72954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
73954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    private static final String MY_ROOT_ID = "myRoot";
74954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    private static final String MY_DOC_ID = "myDoc";
75954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    private static final String MY_DOC_NULL = "myNull";
76954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
77954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
786efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
79954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
80954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            Root.COLUMN_AVAILABLE_BYTES,
81954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    };
82954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
83954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
84954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
85954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
86954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    };
87954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
88954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    private static String[] resolveRootProjection(String[] projection) {
89954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
90954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    }
91954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
92954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    private static String[] resolveDocumentProjection(String[] projection) {
93954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
94954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    }
95954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
96a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey    private String mAuthority;
97a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey
98a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey    @Override
99a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey    public void attachInfo(Context context, ProviderInfo info) {
100a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey        mAuthority = info.authority;
101a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey        super.attachInfo(context, info);
102a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey    }
103a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey
104954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    @Override
105954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
106a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey        Log.d(TAG, "Someone asked for our roots!");
107a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey
1087aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        if (ROOTS_WEDGE) SystemClock.sleep(Integer.MAX_VALUE);
1097aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        if (ROOTS_LAG) SystemClock.sleep(3000);
1107aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        if (ROOTS_CRASH) System.exit(12);
111954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
1127aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        if (ROOTS_REFRESH) {
113a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey            new AsyncTask<Void, Void, Void>() {
114a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey                @Override
115a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey                protected Void doInBackground(Void... params) {
116a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey                    SystemClock.sleep(3000);
117a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey                    Log.d(TAG, "Notifying that something changed!!");
118a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey                    final Uri uri = DocumentsContract.buildRootsUri(mAuthority);
119a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey                    getContext().getContentResolver().notifyChange(uri, null, false);
120a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey                    return null;
121a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey                }
122a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey            }.execute();
123a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey        }
124a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey
125954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
126954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        final RowBuilder row = result.newRow();
127b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey        row.add(Root.COLUMN_ROOT_ID, MY_ROOT_ID);
128b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey        row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS);
129b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey        row.add(Root.COLUMN_TITLE, "_Test title which is really long");
130a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey        row.add(Root.COLUMN_SUMMARY,
131a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey                SystemClock.elapsedRealtime() + " summary which is also super long text");
132b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey        row.add(Root.COLUMN_DOCUMENT_ID, MY_DOC_ID);
133b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey        row.add(Root.COLUMN_AVAILABLE_BYTES, 1024);
134954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        return result;
135954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    }
136954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
137954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    @Override
138954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    public Cursor queryDocument(String documentId, String[] projection)
139954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            throws FileNotFoundException {
1407aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        if (DOCUMENT_CRASH) System.exit(12);
141954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
142954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
143de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        includeFile(result, documentId, 0);
144954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        return result;
145954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    }
146954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
147954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    /**
148954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey     * Holds any outstanding or finished "network" fetching.
149954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey     */
150954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    private WeakReference<CloudTask> mTask;
151954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
152954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    private static class CloudTask implements Runnable {
153954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
154954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        private final ContentResolver mResolver;
155954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        private final Uri mNotifyUri;
156954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
157954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        private volatile boolean mFinished;
158954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
159954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        public CloudTask(ContentResolver resolver, Uri notifyUri) {
160954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            mResolver = resolver;
161954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            mNotifyUri = notifyUri;
162954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        }
163954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
164954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        @Override
165954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        public void run() {
166954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            // Pretend to do some network
167954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            Log.d(TAG, hashCode() + ": pretending to do some network!");
168954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            SystemClock.sleep(2000);
169954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            Log.d(TAG, hashCode() + ": network done!");
170954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
171954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            mFinished = true;
172954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
173954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            // Tell anyone remotely they should requery
174954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            mResolver.notifyChange(mNotifyUri, null, false);
175954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        }
176954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
177954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        public boolean includeIfFinished(MatrixCursor result) {
178954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            Log.d(TAG, hashCode() + ": includeIfFinished() found " + mFinished);
179954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            if (mFinished) {
180de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                includeFile(result, "_networkfile1", 0);
181de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                includeFile(result, "_networkfile2", 0);
182de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                includeFile(result, "_networkfile3", 0);
183de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                includeFile(result, "_networkfile4", 0);
184de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                includeFile(result, "_networkfile5", 0);
185de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                includeFile(result, "_networkfile6", 0);
186954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                return true;
187954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            } else {
188954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                return false;
189954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            }
190954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        }
191954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    }
192954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
193954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    private static class CloudCursor extends MatrixCursor {
194954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        public Object keepAlive;
195954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        public final Bundle extras = new Bundle();
196954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
197954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        public CloudCursor(String[] columnNames) {
198954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            super(columnNames);
199954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        }
200954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
201954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        @Override
202954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        public Bundle getExtras() {
203954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            return extras;
204954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        }
205954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    }
206954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
207954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    @Override
208954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    public Cursor queryChildDocuments(
209954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            String parentDocumentId, String[] projection, String sortOrder)
210954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            throws FileNotFoundException {
211954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
2127aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        if (CHILD_WEDGE) SystemClock.sleep(Integer.MAX_VALUE);
2137aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        if (CHILD_CRASH) System.exit(12);
2147aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey
215954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        final ContentResolver resolver = getContext().getContentResolver();
216954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        final Uri notifyUri = DocumentsContract.buildDocumentUri(
217954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                "com.example.documents", parentDocumentId);
218954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
219954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        CloudCursor result = new CloudCursor(resolveDocumentProjection(projection));
220954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        result.setNotificationUri(resolver, notifyUri);
221954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
222954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        // Always include local results
223de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        includeFile(result, MY_DOC_NULL, 0);
224de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        includeFile(result, "localfile1", 0);
225de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        includeFile(result, "localfile2", Document.FLAG_SUPPORTS_THUMBNAIL);
226de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        includeFile(result, "localfile3", 0);
227de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        includeFile(result, "localfile4", 0);
228954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
229d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey        if (THUMB_HUNDREDS) {
230d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey            for (int i = 0; i < 256; i++) {
231d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey                includeFile(result, "i maded u an picshure", Document.FLAG_SUPPORTS_THUMBNAIL);
232d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey            }
233d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey        }
234d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey
235954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        synchronized (this) {
236954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            // Try picking up an existing network fetch
237954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            CloudTask task = mTask != null ? mTask.get() : null;
238954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            if (task == null) {
239954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                Log.d(TAG, "No network task found; starting!");
240954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                task = new CloudTask(resolver, notifyUri);
241954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                mTask = new WeakReference<CloudTask>(task);
242954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                new Thread(task).start();
243954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
244954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                // Aggressively try freeing weak reference above
245954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                new Thread() {
246954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                    @Override
247954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                    public void run() {
248954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                        while (mTask.get() != null) {
249954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                            SystemClock.sleep(200);
250954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                            System.gc();
251954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                            System.runFinalization();
252954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                        }
253954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                        Log.d(TAG, "AHA! THE CLOUD TASK WAS GC'ED!");
254954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                    }
255954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                }.start();
256954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            }
257954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
258954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            // Blend in cloud results if ready
259954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            if (task.includeIfFinished(result)) {
260954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                result.extras.putString(DocumentsContract.EXTRA_INFO,
261954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                        "Everything Went Better Than Expected and this message is quite "
262954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                                + "long and verbose and maybe even too long");
263954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                result.extras.putString(DocumentsContract.EXTRA_ERROR,
264954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                        "But then again, maybe our server ran into an error, which means "
265954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                                + "we're going to have a bad time");
266954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            } else {
267954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey                result.extras.putBoolean(DocumentsContract.EXTRA_LOADING, true);
268954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            }
269954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
270954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            // Tie the network fetch to the cursor GC lifetime
271954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            result.keepAlive = task;
272954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
273954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            return result;
274954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        }
275954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    }
276954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
277954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    @Override
278954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    public Cursor queryRecentDocuments(String rootId, String[] projection)
279954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            throws FileNotFoundException {
2807aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey
2817aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        if (RECENT_WEDGE) SystemClock.sleep(Integer.MAX_VALUE);
2827aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey
283954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        // Pretend to take a super long time to respond
284954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        SystemClock.sleep(3000);
285954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
286954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
287de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        includeFile(
288de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                result, "It was /worth/ the_wait for?the file:with the&incredibly long name", 0);
289954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        return result;
290954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    }
291954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
292954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    @Override
293954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
294954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            throws FileNotFoundException {
295954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        throw new FileNotFoundException();
296954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    }
297954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
298954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    @Override
299de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    public AssetFileDescriptor openDocumentThumbnail(
300de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
3017aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey
302d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey        if (THUMB_WEDGE) wedgeUntilCanceled(signal);
3037aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        if (THUMB_CRASH) System.exit(12);
3047aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey
305de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        final Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
306de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        final Canvas canvas = new Canvas(bitmap);
307de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        final Paint paint = new Paint();
308de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        paint.setColor(Color.BLUE);
309de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        canvas.drawColor(Color.RED);
310de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        canvas.drawLine(0, 0, 32, 32, paint);
311de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey
312de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
313de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        bitmap.compress(CompressFormat.JPEG, 50, bos);
314de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey
315de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        final ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
316de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        try {
317de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createReliablePipe();
318de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            new AsyncTask<Object, Object, Object>() {
319de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                @Override
320de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                protected Object doInBackground(Object... params) {
321de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                    final FileOutputStream fos = new FileOutputStream(fds[1].getFileDescriptor());
322de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                    try {
323de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                        Streams.copy(bis, fos);
324de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                    } catch (IOException e) {
325de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                        throw new RuntimeException(e);
326de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                    }
327de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                    IoUtils.closeQuietly(fds[1]);
328de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                    return null;
329de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                }
330de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
331de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            return new AssetFileDescriptor(fds[0], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
332de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        } catch (IOException e) {
333de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            throw new FileNotFoundException(e.getMessage());
334de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        }
335de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    }
336de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey
337de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    @Override
338954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    public boolean onCreate() {
339954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        return true;
340954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    }
341954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
342d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey    private static void wedgeUntilCanceled(CancellationSignal signal) {
343d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey        if (signal != null) {
344d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey            while (true) {
345d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey                signal.throwIfCanceled();
346d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey                SystemClock.sleep(500);
347d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey            }
348d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey        } else {
349d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey            Log.w(TAG, "WEDGING WITHOUT A CANCELLATIONSIGNAL");
350d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey            SystemClock.sleep(Integer.MAX_VALUE);
351d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey        }
352d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey    }
353d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey
354de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    private static void includeFile(MatrixCursor result, String docId, int flags) {
355954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        final RowBuilder row = result.newRow();
356b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey        row.add(Document.COLUMN_DOCUMENT_ID, docId);
357b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey        row.add(Document.COLUMN_DISPLAY_NAME, docId);
358b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey        row.add(Document.COLUMN_LAST_MODIFIED, System.currentTimeMillis());
359de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        row.add(Document.COLUMN_FLAGS, flags);
360954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey
361954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        if (MY_DOC_ID.equals(docId)) {
362b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey            row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
363954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        } else if (MY_DOC_NULL.equals(docId)) {
364954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey            // No MIME type
365954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        } else {
366b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey            row.add(Document.COLUMN_MIME_TYPE, "application/octet-stream");
367954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey        }
368954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey    }
369954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey}
370