MtpDocumentsProviderTest.java revision 259ce80132d55774fe599c60b53a9d9dfc1efa65
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            // TODO: Add storage icon for MTP devices.
142            assertTrue(cursor.isNull(2) /* icon */);
143            assertEquals("Device A Storage A", cursor.getString(3));
144            assertEquals("1", cursor.getString(4));
145            assertEquals(1024, cursor.getInt(5));
146        }
147
148        {
149            mProvider.openDevice(1);
150            mResolver.waitForNotification(ROOTS_URI, 2);
151            final Cursor cursor = mProvider.queryRoots(null);
152            assertEquals(2, cursor.getCount());
153            cursor.moveToNext();
154            cursor.moveToNext();
155            assertEquals("2", cursor.getString(0));
156            assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
157            // TODO: Add storage icon for MTP devices.
158            assertTrue(cursor.isNull(2) /* icon */);
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        mMtpManager.addValidDevice(0);
167        mMtpManager.addValidDevice(1);
168        // Not set roots for device 0 so that MtpManagerMock#getRoots throws IOException.
169        mMtpManager.setRoots(1, new MtpRoot[] {
170                new MtpRoot(
171                        1 /* deviceId */,
172                        1 /* storageId */,
173                        "Device B" /* device model name */,
174                        "Storage B" /* volume description */,
175                        2048 /* free space */,
176                        4096 /* total space */,
177                        "Identifier B" /* no volume identifier */)
178        });
179        {
180            mProvider.openDevice(0);
181            mProvider.openDevice(1);
182            mResolver.waitForNotification(ROOTS_URI, 1);
183
184            final Cursor cursor = mProvider.queryRoots(null);
185            assertEquals(1, cursor.getCount());
186            cursor.moveToNext();
187            assertEquals("1", cursor.getString(0));
188            assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
189            // TODO: Add storage icon for MTP devices.
190            assertTrue(cursor.isNull(2) /* icon */);
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        mMtpManager.addValidDevice(0);
199        mProvider.openDevice(0);
200
201        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
202        setupDocuments(
203                0,
204                0,
205                MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
206                "1",
207                new MtpObjectInfo[] {
208                        new MtpObjectInfo.Builder()
209                                .setObjectHandle(100)
210                                .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
211                                .setName("image.jpg")
212                                .setDateModified(1422716400000L)
213                                .setCompressedSize(1024 * 1024 * 5)
214                                .setThumbCompressedSize(50 * 1024)
215                                .build()
216                });
217
218        final Cursor cursor = mProvider.queryDocument("2", null);
219        assertEquals(1, cursor.getCount());
220
221        cursor.moveToNext();
222
223        assertEquals("2", cursor.getString(0));
224        assertEquals("image/jpeg", cursor.getString(1));
225        assertEquals("image.jpg", cursor.getString(2));
226        assertEquals(1422716400000L, cursor.getLong(3));
227        assertEquals(
228                DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
229                DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
230                DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL,
231                cursor.getInt(4));
232        assertEquals(1024 * 1024 * 5, cursor.getInt(5));
233    }
234
235    public void testQueryDocument_directory()
236            throws IOException, InterruptedException, TimeoutException {
237        mMtpManager.addValidDevice(0);
238        mProvider.openDevice(0);
239
240        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
241        setupDocuments(
242                0,
243                0,
244                MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
245                "1",
246                new MtpObjectInfo[] {
247                        new MtpObjectInfo.Builder()
248                                .setObjectHandle(2)
249                                .setStorageId(1)
250                                .setFormat(MtpConstants.FORMAT_ASSOCIATION)
251                                .setName("directory")
252                                .setDateModified(1422716400000L)
253                                .build()
254                });
255
256        final Cursor cursor = mProvider.queryDocument("2", null);
257        assertEquals(1, cursor.getCount());
258
259        cursor.moveToNext();
260        assertEquals("2", cursor.getString(0));
261        assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
262        assertEquals("directory", cursor.getString(2));
263        assertEquals(1422716400000L, cursor.getLong(3));
264        assertEquals(
265                DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
266                DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
267                DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE,
268                cursor.getInt(4));
269        assertEquals(0, cursor.getInt(5));
270    }
271
272    public void testQueryDocument_forRoot()
273            throws IOException, InterruptedException, TimeoutException {
274        mMtpManager.addValidDevice(0);
275        mProvider.openDevice(0);
276
277        setupRoots(0, new MtpRoot[] {
278                new MtpRoot(
279                        0 /* deviceId */,
280                        1 /* storageId */,
281                        "Device A" /* device model name */,
282                        "Storage A" /* volume description */,
283                        1024 /* free space */,
284                        4096 /* total space */,
285                        "" /* no volume identifier */)
286        });
287        final Cursor cursor = mProvider.queryDocument("1", null);
288        assertEquals(1, cursor.getCount());
289
290        cursor.moveToNext();
291        assertEquals("1", cursor.getString(0));
292        assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
293        assertEquals("Device A Storage A", cursor.getString(2));
294        assertTrue(cursor.isNull(3));
295        assertEquals(0, cursor.getInt(4));
296        assertEquals(3072, cursor.getInt(5));
297    }
298
299    public void testQueryChildDocuments() throws Exception {
300        mMtpManager.addValidDevice(0);
301        mProvider.openDevice(0);
302
303        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
304        setupDocuments(
305                0,
306                0,
307                MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
308                "1",
309                new MtpObjectInfo[] {
310                        new MtpObjectInfo.Builder()
311                                .setObjectHandle(100)
312                                .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
313                                .setName("image.jpg")
314                                .setCompressedSize(1024 * 1024 * 5)
315                                .setThumbCompressedSize(5 * 1024)
316                                .setProtectionStatus(MtpConstants.PROTECTION_STATUS_READ_ONLY)
317                                .build()
318                });
319
320        final Cursor cursor = mProvider.queryChildDocuments("1", null, null);
321        assertEquals(1, cursor.getCount());
322
323        assertTrue(cursor.moveToNext());
324        assertEquals("2", cursor.getString(0));
325        assertEquals("image/jpeg", cursor.getString(1));
326        assertEquals("image.jpg", cursor.getString(2));
327        assertEquals(0, cursor.getLong(3));
328        assertEquals(DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL, cursor.getInt(4));
329        assertEquals(1024 * 1024 * 5, cursor.getInt(5));
330
331        cursor.close();
332    }
333
334    public void testQueryChildDocuments_cursorError() throws Exception {
335        mMtpManager.addValidDevice(0);
336        mProvider.openDevice(0);
337        try {
338            mProvider.queryChildDocuments("1", null, null);
339            fail();
340        } catch (Throwable error) {
341            assertTrue(error instanceof FileNotFoundException);
342        }
343    }
344
345    public void testQueryChildDocuments_documentError() throws Exception {
346        mMtpManager.addValidDevice(0);
347        mProvider.openDevice(0);
348        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
349        mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
350        try {
351            mProvider.queryChildDocuments("1", null, null);
352            fail();
353        } catch (Throwable error) {
354            assertTrue(error instanceof FileNotFoundException);
355        }
356    }
357
358    public void testDeleteDocument() throws IOException, InterruptedException, TimeoutException {
359        mMtpManager.addValidDevice(0);
360        mProvider.openDevice(0);
361        setupRoots(0, new MtpRoot[] {
362                new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
363        });
364        setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
365                new MtpObjectInfo.Builder()
366                    .setName("test.txt")
367                    .setObjectHandle(1)
368                    .setParent(-1)
369                    .build()
370        });
371
372        mProvider.deleteDocument("2");
373        assertEquals(1, mResolver.getChangeCount(
374                DocumentsContract.buildChildDocumentsUri(
375                        MtpDocumentsProvider.AUTHORITY, "1")));
376    }
377
378    public void testDeleteDocument_error()
379            throws IOException, InterruptedException, TimeoutException {
380        mMtpManager.addValidDevice(0);
381        mProvider.openDevice(0);
382        setupRoots(0, new MtpRoot[] {
383                new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
384        });
385        setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
386                new MtpObjectInfo.Builder()
387                    .setName("test.txt")
388                    .setObjectHandle(1)
389                    .setParent(-1)
390                    .build()
391        });
392        try {
393            mProvider.deleteDocument("3");
394            fail();
395        } catch (Throwable e) {
396            assertTrue(e instanceof IOException);
397        }
398        assertEquals(0, mResolver.getChangeCount(
399                DocumentsContract.buildChildDocumentsUri(
400                        MtpDocumentsProvider.AUTHORITY, "1")));
401    }
402
403    private String[] getStrings(Cursor cursor) {
404        try {
405            final String[] results = new String[cursor.getCount()];
406            for (int i = 0; cursor.moveToNext(); i++) {
407                results[i] = cursor.getString(0);
408            }
409            return results;
410        } finally {
411            cursor.close();
412        }
413    }
414
415    private String[] setupRoots(int deviceId, MtpRoot[] roots)
416            throws FileNotFoundException, InterruptedException, TimeoutException {
417        final int changeCount = mResolver.getChangeCount(ROOTS_URI);
418        mMtpManager.setRoots(deviceId, roots);
419        mResolver.waitForNotification(ROOTS_URI, changeCount + 1);
420        return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID)));
421    }
422
423    private String[] setupDocuments(
424            int deviceId,
425            int storageId,
426            int parentHandle,
427            String parentDocumentId,
428            MtpObjectInfo[] objects) throws FileNotFoundException {
429        final int[] handles = new int[objects.length];
430        int i = 0;
431        for (final MtpObjectInfo info : objects) {
432            handles[i] = info.getObjectHandle();
433            mMtpManager.setObjectInfo(deviceId, info);
434        }
435        mMtpManager.setObjectHandles(deviceId, storageId, parentHandle, handles);
436        return getStrings(mProvider.queryChildDocuments(
437                parentDocumentId, strings(DocumentsContract.Document.COLUMN_DOCUMENT_ID), null));
438    }
439}
440