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