MtpDocumentsProviderTest.java revision 39795da4c948536c4a9d9ceadece5b03fd395d89
1/*
2 * Copyright (C) 2015 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.mtp;
18
19import android.database.Cursor;
20import android.mtp.MtpConstants;
21import android.mtp.MtpObjectInfo;
22import android.net.Uri;
23import android.provider.DocumentsContract.Root;
24import android.provider.DocumentsContract;
25import android.test.AndroidTestCase;
26import android.test.suitebuilder.annotation.SmallTest;
27
28import java.io.FileNotFoundException;
29import java.io.IOException;
30import java.util.concurrent.TimeoutException;
31
32import static com.android.mtp.MtpDatabase.strings;
33
34@SmallTest
35public class MtpDocumentsProviderTest extends AndroidTestCase {
36    private final static Uri ROOTS_URI =
37            DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY);
38    private TestContentResolver mResolver;
39    private MtpDocumentsProvider mProvider;
40    private TestMtpManager mMtpManager;
41    private final TestResources mResources = new TestResources();
42    private MtpDatabase mDatabase;
43
44    @Override
45    public void setUp() throws IOException {
46        mResolver = new TestContentResolver();
47        mMtpManager = new TestMtpManager(getContext());
48        mProvider = new MtpDocumentsProvider();
49        mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
50        mProvider.onCreateForTesting(mResources, mMtpManager, mResolver, mDatabase);
51    }
52
53    @Override
54    public void tearDown() {
55        mProvider.shutdown();
56    }
57
58    public void testOpenAndCloseDevice() throws Exception {
59        mMtpManager.addValidDevice(0);
60        mMtpManager.setRoots(0, new MtpRoot[] {
61                new MtpRoot(
62                        0 /* deviceId */,
63                        1 /* storageId */,
64                        "Device A" /* device model name */,
65                        "Storage A" /* volume description */,
66                        1024 /* free space */,
67                        2048 /* total space */,
68                        "" /* no volume identifier */)
69        });
70
71        mProvider.openDevice(0);
72        mResolver.waitForNotification(ROOTS_URI, 1);
73
74        mProvider.closeDevice(0);
75        mResolver.waitForNotification(ROOTS_URI, 2);
76    }
77
78    public void testOpenAndCloseErrorDevice() throws Exception {
79        try {
80            mProvider.openDevice(1);
81            fail();
82        } catch (Throwable error) {
83            assertTrue(error instanceof IOException);
84        }
85
86        try {
87            mProvider.closeDevice(1);
88            fail();
89        } catch (Throwable error) {
90            assertTrue(error instanceof IOException);
91        }
92
93        // Check if the following notification is the first one or not.
94        mMtpManager.addValidDevice(0);
95        mMtpManager.setRoots(0, new MtpRoot[] {
96                new MtpRoot(
97                        0 /* deviceId */,
98                        1 /* storageId */,
99                        "Device A" /* device model name */,
100                        "Storage A" /* volume description */,
101                        1024 /* free space */,
102                        2048 /* total space */,
103                        "" /* no volume identifier */)
104        });
105        mProvider.openDevice(0);
106        mResolver.waitForNotification(ROOTS_URI, 1);
107    }
108
109    public void testQueryRoots() throws Exception {
110        mMtpManager.addValidDevice(0);
111        mMtpManager.addValidDevice(1);
112        mMtpManager.setRoots(0, new MtpRoot[] {
113                new MtpRoot(
114                        0 /* deviceId */,
115                        1 /* storageId */,
116                        "Device A" /* device model name */,
117                        "Storage A" /* volume description */,
118                        1024 /* free space */,
119                        2048 /* total space */,
120                        "" /* no volume identifier */)
121        });
122        mMtpManager.setRoots(1, new MtpRoot[] {
123                new MtpRoot(
124                        1 /* deviceId */,
125                        1 /* storageId */,
126                        "Device B" /* device model name */,
127                        "Storage B" /* volume description */,
128                        2048 /* free space */,
129                        4096 /* total space */,
130                        "Identifier B" /* no volume identifier */)
131        });
132
133        {
134            mProvider.openDevice(0);
135            mResolver.waitForNotification(ROOTS_URI, 1);
136            final Cursor cursor = mProvider.queryRoots(null);
137            assertEquals(1, cursor.getCount());
138            cursor.moveToNext();
139            assertEquals("1", cursor.getString(0));
140            assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
141            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
142            assertEquals("Device A Storage A", cursor.getString(3));
143            assertEquals("1", cursor.getString(4));
144            assertEquals(1024, cursor.getInt(5));
145        }
146
147        {
148            mProvider.openDevice(1);
149            mResolver.waitForNotification(ROOTS_URI, 2);
150            final Cursor cursor = mProvider.queryRoots(null);
151            assertEquals(2, cursor.getCount());
152            cursor.moveToNext();
153            cursor.moveToNext();
154            assertEquals("2", cursor.getString(0));
155            assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
156            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
157            assertEquals("Device B Storage B", cursor.getString(3));
158            assertEquals("2", cursor.getString(4));
159            assertEquals(2048, cursor.getInt(5));
160        }
161    }
162
163    public void testQueryRoots_error() throws Exception {
164        mMtpManager.addValidDevice(0);
165        mMtpManager.addValidDevice(1);
166        // Not set roots for device 0 so that MtpManagerMock#getRoots throws IOException.
167        mMtpManager.setRoots(1, new MtpRoot[] {
168                new MtpRoot(
169                        1 /* deviceId */,
170                        1 /* storageId */,
171                        "Device B" /* device model name */,
172                        "Storage B" /* volume description */,
173                        2048 /* free space */,
174                        4096 /* total space */,
175                        "Identifier B" /* no volume identifier */)
176        });
177        {
178            mProvider.openDevice(0);
179            mProvider.openDevice(1);
180            mResolver.waitForNotification(ROOTS_URI, 1);
181
182            final Cursor cursor = mProvider.queryRoots(null);
183            assertEquals(1, cursor.getCount());
184            cursor.moveToNext();
185            assertEquals("1", cursor.getString(0));
186            assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
187            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
188            assertEquals("Device B Storage B", cursor.getString(3));
189            assertEquals("1", cursor.getString(4));
190            assertEquals(2048, cursor.getInt(5));
191        }
192    }
193
194    public void testQueryDocument() throws IOException, InterruptedException, TimeoutException {
195        mMtpManager.addValidDevice(0);
196        mProvider.openDevice(0);
197
198        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
199        setupDocuments(
200                0,
201                0,
202                MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
203                "1",
204                new MtpObjectInfo[] {
205                        new MtpObjectInfo.Builder()
206                                .setObjectHandle(100)
207                                .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
208                                .setName("image.jpg")
209                                .setDateModified(1422716400000L)
210                                .setCompressedSize(1024 * 1024 * 5)
211                                .setThumbCompressedSize(50 * 1024)
212                                .build()
213                });
214
215        final Cursor cursor = mProvider.queryDocument("2", null);
216        assertEquals(1, cursor.getCount());
217
218        cursor.moveToNext();
219
220        assertEquals("2", cursor.getString(0));
221        assertEquals("image/jpeg", cursor.getString(1));
222        assertEquals("image.jpg", cursor.getString(2));
223        assertEquals(1422716400000L, cursor.getLong(3));
224        assertEquals(
225                DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
226                DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
227                DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL,
228                cursor.getInt(4));
229        assertEquals(1024 * 1024 * 5, cursor.getInt(5));
230    }
231
232    public void testQueryDocument_directory()
233            throws IOException, InterruptedException, TimeoutException {
234        mMtpManager.addValidDevice(0);
235        mProvider.openDevice(0);
236
237        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
238        setupDocuments(
239                0,
240                0,
241                MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
242                "1",
243                new MtpObjectInfo[] {
244                        new MtpObjectInfo.Builder()
245                                .setObjectHandle(2)
246                                .setStorageId(1)
247                                .setFormat(MtpConstants.FORMAT_ASSOCIATION)
248                                .setName("directory")
249                                .setDateModified(1422716400000L)
250                                .build()
251                });
252
253        final Cursor cursor = mProvider.queryDocument("2", null);
254        assertEquals(1, cursor.getCount());
255
256        cursor.moveToNext();
257        assertEquals("2", cursor.getString(0));
258        assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
259        assertEquals("directory", cursor.getString(2));
260        assertEquals(1422716400000L, cursor.getLong(3));
261        assertEquals(
262                DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
263                DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
264                DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE,
265                cursor.getInt(4));
266        assertEquals(0, cursor.getInt(5));
267    }
268
269    public void testQueryDocument_forRoot()
270            throws IOException, InterruptedException, TimeoutException {
271        mMtpManager.addValidDevice(0);
272        mProvider.openDevice(0);
273
274        setupRoots(0, new MtpRoot[] {
275                new MtpRoot(
276                        0 /* deviceId */,
277                        1 /* storageId */,
278                        "Device A" /* device model name */,
279                        "Storage A" /* volume description */,
280                        1024 /* free space */,
281                        4096 /* total space */,
282                        "" /* no volume identifier */)
283        });
284        final Cursor cursor = mProvider.queryDocument("1", null);
285        assertEquals(1, cursor.getCount());
286
287        cursor.moveToNext();
288        assertEquals("1", cursor.getString(0));
289        assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
290        assertEquals("Device A Storage A", cursor.getString(2));
291        assertTrue(cursor.isNull(3));
292        assertEquals(0, cursor.getInt(4));
293        assertEquals(3072, cursor.getInt(5));
294    }
295
296    public void testQueryChildDocuments() throws Exception {
297        mMtpManager.addValidDevice(0);
298        mProvider.openDevice(0);
299
300        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
301        setupDocuments(
302                0,
303                0,
304                MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
305                "1",
306                new MtpObjectInfo[] {
307                        new MtpObjectInfo.Builder()
308                                .setObjectHandle(100)
309                                .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
310                                .setName("image.jpg")
311                                .setCompressedSize(1024 * 1024 * 5)
312                                .setThumbCompressedSize(5 * 1024)
313                                .setProtectionStatus(MtpConstants.PROTECTION_STATUS_READ_ONLY)
314                                .build()
315                });
316
317        final Cursor cursor = mProvider.queryChildDocuments("1", null, null);
318        assertEquals(1, cursor.getCount());
319
320        assertTrue(cursor.moveToNext());
321        assertEquals("2", cursor.getString(0));
322        assertEquals("image/jpeg", cursor.getString(1));
323        assertEquals("image.jpg", cursor.getString(2));
324        assertEquals(0, cursor.getLong(3));
325        assertEquals(DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL, cursor.getInt(4));
326        assertEquals(1024 * 1024 * 5, cursor.getInt(5));
327
328        cursor.close();
329    }
330
331    public void testQueryChildDocuments_cursorError() throws Exception {
332        mMtpManager.addValidDevice(0);
333        mProvider.openDevice(0);
334        try {
335            mProvider.queryChildDocuments("1", null, null);
336            fail();
337        } catch (Throwable error) {
338            assertTrue(error instanceof FileNotFoundException);
339        }
340    }
341
342    public void testQueryChildDocuments_documentError() throws Exception {
343        mMtpManager.addValidDevice(0);
344        mProvider.openDevice(0);
345        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
346        mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
347        try {
348            mProvider.queryChildDocuments("1", null, null);
349            fail();
350        } catch (Throwable error) {
351            assertTrue(error instanceof FileNotFoundException);
352        }
353    }
354
355    public void testDeleteDocument() throws IOException, InterruptedException, TimeoutException {
356        mMtpManager.addValidDevice(0);
357        mProvider.openDevice(0);
358        setupRoots(0, new MtpRoot[] {
359                new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
360        });
361        setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
362                new MtpObjectInfo.Builder()
363                    .setName("test.txt")
364                    .setObjectHandle(1)
365                    .setParent(-1)
366                    .build()
367        });
368
369        mProvider.deleteDocument("2");
370        assertEquals(1, mResolver.getChangeCount(
371                DocumentsContract.buildChildDocumentsUri(
372                        MtpDocumentsProvider.AUTHORITY, "1")));
373    }
374
375    public void testDeleteDocument_error()
376            throws IOException, InterruptedException, TimeoutException {
377        mMtpManager.addValidDevice(0);
378        mProvider.openDevice(0);
379        setupRoots(0, new MtpRoot[] {
380                new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
381        });
382        setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
383                new MtpObjectInfo.Builder()
384                    .setName("test.txt")
385                    .setObjectHandle(1)
386                    .setParent(-1)
387                    .build()
388        });
389        try {
390            mProvider.deleteDocument("3");
391            fail();
392        } catch (Throwable e) {
393            assertTrue(e instanceof IOException);
394        }
395        assertEquals(0, mResolver.getChangeCount(
396                DocumentsContract.buildChildDocumentsUri(
397                        MtpDocumentsProvider.AUTHORITY, "1")));
398    }
399
400    private String[] getStrings(Cursor cursor) {
401        try {
402            final String[] results = new String[cursor.getCount()];
403            for (int i = 0; cursor.moveToNext(); i++) {
404                results[i] = cursor.getString(0);
405            }
406            return results;
407        } finally {
408            cursor.close();
409        }
410    }
411
412    private String[] setupRoots(int deviceId, MtpRoot[] roots)
413            throws FileNotFoundException, InterruptedException, TimeoutException {
414        final int changeCount = mResolver.getChangeCount(ROOTS_URI);
415        mMtpManager.setRoots(deviceId, roots);
416        mResolver.waitForNotification(ROOTS_URI, changeCount + 1);
417        return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID)));
418    }
419
420    private String[] setupDocuments(
421            int deviceId,
422            int storageId,
423            int parentHandle,
424            String parentDocumentId,
425            MtpObjectInfo[] objects) throws FileNotFoundException {
426        final int[] handles = new int[objects.length];
427        int i = 0;
428        for (final MtpObjectInfo info : objects) {
429            handles[i] = info.getObjectHandle();
430            mMtpManager.setObjectInfo(deviceId, info);
431        }
432        mMtpManager.setObjectHandles(deviceId, storageId, parentHandle, handles);
433        return getStrings(mProvider.queryChildDocuments(
434                parentDocumentId, strings(DocumentsContract.Document.COLUMN_DOCUMENT_ID), null));
435    }
436}
437