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