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 
17 package com.android.mtp;
18 
19 import static android.provider.DocumentsContract.Document.*;
20 
21 import static com.android.mtp.MtpDatabase.strings;
22 import static com.android.mtp.MtpDatabaseConstants.*;
23 import static com.android.mtp.TestUtil.OPERATIONS_SUPPORTED;
24 
25 import android.database.Cursor;
26 import android.mtp.MtpConstants;
27 import android.mtp.MtpObjectInfo;
28 import android.net.Uri;
29 import android.provider.DocumentsContract;
30 import android.provider.DocumentsContract.Document;
31 import android.provider.DocumentsContract.Root;
32 import android.test.AndroidTestCase;
33 
34 import androidx.test.filters.SmallTest;
35 
36 import java.io.FileNotFoundException;
37 
38 @SmallTest
39 public class MtpDatabaseTest extends AndroidTestCase {
40     private static final String[] COLUMN_NAMES = new String[] {
41         DocumentsContract.Document.COLUMN_DOCUMENT_ID,
42         MtpDatabaseConstants.COLUMN_DEVICE_ID,
43         MtpDatabaseConstants.COLUMN_STORAGE_ID,
44         MtpDatabaseConstants.COLUMN_OBJECT_HANDLE,
45         DocumentsContract.Document.COLUMN_MIME_TYPE,
46         DocumentsContract.Document.COLUMN_DISPLAY_NAME,
47         DocumentsContract.Document.COLUMN_SUMMARY,
48         DocumentsContract.Document.COLUMN_LAST_MODIFIED,
49         DocumentsContract.Document.COLUMN_ICON,
50         DocumentsContract.Document.COLUMN_FLAGS,
51         DocumentsContract.Document.COLUMN_SIZE,
52         MtpDatabaseConstants.COLUMN_DOCUMENT_TYPE
53     };
54 
55     private final TestResources resources = new TestResources();
56     MtpDatabase mDatabase;
57 
58     @Override
setUp()59     public void setUp() {
60         mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
61     }
62 
63     @Override
tearDown()64     public void tearDown() {
65         mDatabase.close();
66         mDatabase = null;
67     }
68 
getInt(Cursor cursor, String columnName)69     private static int getInt(Cursor cursor, String columnName) {
70         return cursor.getInt(cursor.getColumnIndex(columnName));
71     }
72 
isNull(Cursor cursor, String columnName)73     private static boolean isNull(Cursor cursor, String columnName) {
74         return cursor.isNull(cursor.getColumnIndex(columnName));
75     }
76 
getString(Cursor cursor, String columnName)77     private static String getString(Cursor cursor, String columnName) {
78         return cursor.getString(cursor.getColumnIndex(columnName));
79     }
80 
testPutSingleStorageDocuments()81     public void testPutSingleStorageDocuments() throws Exception {
82         addTestDevice();
83 
84         mDatabase.getMapper().startAddingDocuments("1");
85         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
86                 new MtpRoot(0, 1, "Storage", 1000, 2000, "")
87         });
88         mDatabase.getMapper().stopAddingDocuments("1");
89 
90         {
91             final Cursor cursor = mDatabase.queryRootDocuments(COLUMN_NAMES);
92             assertEquals(1, cursor.getCount());
93 
94             cursor.moveToNext();
95             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
96             assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
97             assertEquals(1, getInt(cursor, COLUMN_STORAGE_ID));
98             assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
99             assertEquals(
100                     DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE));
101             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
102             assertTrue(isNull(cursor, COLUMN_SUMMARY));
103             assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
104             assertEquals(R.drawable.ic_root_mtp, getInt(cursor, COLUMN_ICON));
105             assertEquals(Document.FLAG_DIR_SUPPORTS_CREATE, getInt(cursor, COLUMN_FLAGS));
106             assertEquals(1000, getInt(cursor, COLUMN_SIZE));
107             assertEquals(
108                     MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE,
109                     getInt(cursor, COLUMN_DOCUMENT_TYPE));
110 
111             cursor.close();
112         }
113 
114         {
115             final Cursor cursor = mDatabase.queryRoots(resources, new String [] {
116                     Root.COLUMN_ROOT_ID,
117                     Root.COLUMN_FLAGS,
118                     Root.COLUMN_ICON,
119                     Root.COLUMN_TITLE,
120                     Root.COLUMN_SUMMARY,
121                     Root.COLUMN_DOCUMENT_ID,
122                     Root.COLUMN_AVAILABLE_BYTES,
123                     Root.COLUMN_CAPACITY_BYTES
124             });
125             assertEquals(1, cursor.getCount());
126 
127             cursor.moveToNext();
128             assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
129             assertEquals(
130                     Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY,
131                     getInt(cursor, Root.COLUMN_FLAGS));
132             assertEquals(R.drawable.ic_root_mtp, getInt(cursor, Root.COLUMN_ICON));
133             assertEquals("Device Storage", getString(cursor, Root.COLUMN_TITLE));
134             assertTrue(isNull(cursor, Root.COLUMN_SUMMARY));
135             assertEquals(1, getInt(cursor, Root.COLUMN_DOCUMENT_ID));
136             assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
137             assertEquals(2000, getInt(cursor, Root.COLUMN_CAPACITY_BYTES));
138 
139             cursor.close();
140         }
141     }
142 
testPutStorageDocuments()143     public void testPutStorageDocuments() throws Exception {
144         addTestDevice();
145 
146         mDatabase.getMapper().startAddingDocuments("1");
147         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
148                 new MtpRoot(0, 1, "Storage", 1000, 2000, ""),
149                 new MtpRoot(0, 2, "Storage", 2000, 4000, ""),
150                 new MtpRoot(0, 3, "/@#%&<>Storage", 3000, 6000,"")
151         });
152 
153         {
154             final Cursor cursor = mDatabase.queryRootDocuments(COLUMN_NAMES);
155             assertEquals(3, cursor.getCount());
156 
157             cursor.moveToNext();
158             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
159             assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
160             assertEquals(1, getInt(cursor, COLUMN_STORAGE_ID));
161             assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
162             assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE));
163             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
164             assertTrue(isNull(cursor, COLUMN_SUMMARY));
165             assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
166             assertEquals(R.drawable.ic_root_mtp, getInt(cursor, COLUMN_ICON));
167             assertEquals(Document.FLAG_DIR_SUPPORTS_CREATE, getInt(cursor, COLUMN_FLAGS));
168             assertEquals(1000, getInt(cursor, COLUMN_SIZE));
169             assertEquals(
170                     MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, getInt(cursor, COLUMN_DOCUMENT_TYPE));
171 
172             cursor.moveToNext();
173             assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
174             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
175 
176             cursor.moveToNext();
177             assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
178             assertEquals("/@#%&<>Storage", getString(cursor, COLUMN_DISPLAY_NAME));
179 
180             cursor.close();
181         }
182     }
183 
createDocument(int objectHandle, String name, int format, int size)184     private MtpObjectInfo createDocument(int objectHandle, String name, int format, int size) {
185         final MtpObjectInfo.Builder builder = new MtpObjectInfo.Builder();
186         builder.setObjectHandle(objectHandle);
187         builder.setName(name);
188         builder.setFormat(format);
189         builder.setCompressedSize(size);
190         return builder.build();
191     }
192 
testPutChildDocuments()193     public void testPutChildDocuments() throws Exception {
194         addTestDevice();
195         addTestStorage("1");
196 
197         mDatabase.getMapper().startAddingDocuments("2");
198         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
199                 createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
200                 createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
201                 createDocument(102, "music.mp3", MtpConstants.FORMAT_MP3, 3 * 1024 * 1024)
202         }, new long[] { 1024L, 2L * 1024L * 1024L, 3L * 1024L * 1024L});
203 
204         final Cursor cursor = mDatabase.queryChildDocuments(COLUMN_NAMES, "2");
205         assertEquals(3, cursor.getCount());
206 
207         cursor.moveToNext();
208         assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
209         assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
210         assertEquals(0, getInt(cursor, COLUMN_STORAGE_ID));
211         assertEquals(100, getInt(cursor, COLUMN_OBJECT_HANDLE));
212         assertEquals("text/plain", getString(cursor, COLUMN_MIME_TYPE));
213         assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
214         assertTrue(isNull(cursor, COLUMN_SUMMARY));
215         assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
216         assertTrue(isNull(cursor, COLUMN_ICON));
217         assertEquals(
218                 COLUMN_FLAGS,
219                 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
220                 DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
221                 cursor.getInt(9));
222         assertEquals(1024, getInt(cursor, COLUMN_SIZE));
223         assertEquals(
224                 MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, getInt(cursor, COLUMN_DOCUMENT_TYPE));
225 
226         cursor.moveToNext();
227         assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
228         assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
229         assertEquals(0, getInt(cursor, COLUMN_STORAGE_ID));
230         assertEquals(101, getInt(cursor, COLUMN_OBJECT_HANDLE));
231         assertEquals("image/jpeg", getString(cursor, COLUMN_MIME_TYPE));
232         assertEquals("image.jpg", getString(cursor, COLUMN_DISPLAY_NAME));
233         assertTrue(isNull(cursor, COLUMN_SUMMARY));
234         assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
235         assertTrue(isNull(cursor, COLUMN_ICON));
236         assertEquals(
237                 COLUMN_FLAGS,
238                 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
239                 DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
240                 DocumentsContract.Document.FLAG_SUPPORTS_METADATA,
241                 cursor.getInt(9));
242         assertEquals(2 * 1024 * 1024, getInt(cursor, COLUMN_SIZE));
243         assertEquals(
244                 MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, getInt(cursor, COLUMN_DOCUMENT_TYPE));
245 
246         cursor.moveToNext();
247         assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
248         assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
249         assertEquals(0, getInt(cursor, COLUMN_STORAGE_ID));
250         assertEquals(102, getInt(cursor, COLUMN_OBJECT_HANDLE));
251         assertEquals("audio/mpeg", getString(cursor, COLUMN_MIME_TYPE));
252         assertEquals("music.mp3", getString(cursor, COLUMN_DISPLAY_NAME));
253         assertTrue(isNull(cursor, COLUMN_SUMMARY));
254         assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
255         assertTrue(isNull(cursor, COLUMN_ICON));
256         assertEquals(
257                 COLUMN_FLAGS,
258                 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
259                 DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
260                 cursor.getInt(9));
261         assertEquals(3 * 1024 * 1024, getInt(cursor, COLUMN_SIZE));
262         assertEquals(
263                 MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, getInt(cursor, COLUMN_DOCUMENT_TYPE));
264 
265         cursor.close();
266     }
267 
testPutChildDocuments_operationsSupported()268     public void testPutChildDocuments_operationsSupported() throws Exception {
269         addTestDevice();
270         addTestStorage("1");
271 
272         // Put a document with empty supported operations.
273         mDatabase.getMapper().startAddingDocuments("2");
274         mDatabase.getMapper().putChildDocuments(0, "2", new int[0], new MtpObjectInfo[] {
275                 createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024)
276         }, new long[] { 1024L });
277         mDatabase.getMapper().stopAddingDocuments("2");
278 
279         try (final Cursor cursor =
280                 mDatabase.queryChildDocuments(strings(Document.COLUMN_FLAGS), "2")) {
281             assertEquals(1, cursor.getCount());
282             cursor.moveToNext();
283             assertEquals(0, cursor.getInt(0));
284         }
285 
286         // Put a document with writable operations.
287         mDatabase.getMapper().startAddingDocuments("2");
288         mDatabase.getMapper().putChildDocuments(0, "2", new int[] {
289                 MtpConstants.OPERATION_SEND_OBJECT,
290                 MtpConstants.OPERATION_SEND_OBJECT_INFO,
291         }, new MtpObjectInfo[] {
292                 createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024)
293         }, new long[] { 1024L });
294         mDatabase.getMapper().stopAddingDocuments("2");
295 
296         try (final Cursor cursor =
297                 mDatabase.queryChildDocuments(strings(Document.COLUMN_FLAGS), "2")) {
298             assertEquals(1, cursor.getCount());
299             cursor.moveToNext();
300             assertEquals(Document.FLAG_SUPPORTS_WRITE, cursor.getInt(0));
301         }
302 
303         // Put a document with deletable operations.
304         mDatabase.getMapper().startAddingDocuments("2");
305         mDatabase.getMapper().putChildDocuments(0, "2", new int[] {
306                 MtpConstants.OPERATION_DELETE_OBJECT
307         }, new MtpObjectInfo[] {
308                 createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024)
309         }, new long[] { 1024L });
310         mDatabase.getMapper().stopAddingDocuments("2");
311 
312         try (final Cursor cursor =
313                 mDatabase.queryChildDocuments(strings(Document.COLUMN_FLAGS), "2")) {
314             assertEquals(1, cursor.getCount());
315             cursor.moveToNext();
316             assertEquals(Document.FLAG_SUPPORTS_DELETE, cursor.getInt(0));
317         }
318     }
319 
testRestoreIdForRootDocuments()320     public void testRestoreIdForRootDocuments() throws Exception {
321         final String[] columns = new String[] {
322                 DocumentsContract.Document.COLUMN_DOCUMENT_ID,
323                 MtpDatabaseConstants.COLUMN_STORAGE_ID,
324                 DocumentsContract.Document.COLUMN_DISPLAY_NAME
325         };
326 
327         // Add device and two storages.
328         addTestDevice();
329         mDatabase.getMapper().startAddingDocuments("1");
330         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
331                 new MtpRoot(0, 100, "Storage A", 1000, 0, ""),
332                 new MtpRoot(0, 101, "Storage B", 1001, 0, "")
333         });
334 
335         {
336             final Cursor cursor = mDatabase.queryRootDocuments(columns);
337             assertEquals(2, cursor.getCount());
338             cursor.moveToNext();
339             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
340             assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
341             assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
342             cursor.moveToNext();
343             assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
344             assertEquals(101, getInt(cursor, COLUMN_STORAGE_ID));
345             assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
346             cursor.close();
347         }
348 
349         // Clear mapping and add a device.
350         mDatabase.getMapper().clearMapping();
351         addTestDevice();
352 
353         {
354             final Cursor cursor = mDatabase.queryRootDocuments(columns);
355             assertEquals(0, cursor.getCount());
356             cursor.close();
357         }
358 
359         // Add two storages, but one's name is different from previous one.
360         mDatabase.getMapper().startAddingDocuments("1");
361         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
362                 new MtpRoot(0, 200, "Storage A", 2000, 0, ""),
363                 new MtpRoot(0, 202, "Storage C", 2002, 0, "")
364         });
365         mDatabase.getMapper().stopAddingDocuments("1");
366 
367         {
368             // After compeleting mapping, Storage A can be obtained with new storage ID.
369             final Cursor cursor = mDatabase.queryRootDocuments(columns);
370             assertEquals(2, cursor.getCount());
371             cursor.moveToNext();
372             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
373             assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
374             assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
375             cursor.moveToNext();
376             assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
377             assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
378             assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
379             cursor.close();
380         }
381     }
382 
testRestoreIdForChildDocuments()383     public void testRestoreIdForChildDocuments() throws Exception {
384         final String[] columns = new String[] {
385                 DocumentsContract.Document.COLUMN_DOCUMENT_ID,
386                 MtpDatabaseConstants.COLUMN_OBJECT_HANDLE,
387                 DocumentsContract.Document.COLUMN_DISPLAY_NAME
388         };
389 
390         addTestDevice();
391         addTestStorage("1");
392 
393         mDatabase.getMapper().startAddingDocuments("2");
394         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
395                 createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
396                 createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
397                 createDocument(102, "music.mp3", MtpConstants.FORMAT_MP3, 3 * 1024 * 1024)
398         }, new long[] { 1024L, 2L * 1024L * 1024L, 3L * 1024L * 1024L});
399         mDatabase.getMapper().clearMapping();
400 
401         addTestDevice();
402         addTestStorage("1");
403 
404         {
405             // Don't return objects that lost MTP object handles.
406             final Cursor cursor = mDatabase.queryChildDocuments(columns, "2");
407             assertEquals(0, cursor.getCount());
408             cursor.close();
409         }
410 
411         mDatabase.getMapper().startAddingDocuments("2");
412         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
413                 createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
414                 createDocument(203, "video.mp4", MtpConstants.FORMAT_MP4_CONTAINER, 1024),
415         }, new long[] { 1024L, 1024L });
416         mDatabase.getMapper().stopAddingDocuments("2");
417 
418         {
419             final Cursor cursor = mDatabase.queryChildDocuments(columns, "2");
420             assertEquals(2, cursor.getCount());
421 
422             cursor.moveToNext();
423             assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
424             assertEquals(200, getInt(cursor, COLUMN_OBJECT_HANDLE));
425             assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
426 
427             cursor.moveToNext();
428             assertEquals(6, getInt(cursor, COLUMN_DOCUMENT_ID));
429             assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
430             assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
431 
432             cursor.close();
433         }
434     }
435 
testRestoreIdForDifferentDevices()436     public void testRestoreIdForDifferentDevices() throws Exception {
437         final String[] columns = new String[] {
438                 DocumentsContract.Document.COLUMN_DOCUMENT_ID,
439                 MtpDatabaseConstants.COLUMN_STORAGE_ID,
440                 DocumentsContract.Document.COLUMN_DISPLAY_NAME
441         };
442         final String[] rootColumns = new String[] {
443                 Root.COLUMN_ROOT_ID,
444                 Root.COLUMN_AVAILABLE_BYTES
445         };
446         mDatabase.getMapper().startAddingDocuments(null);
447         mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
448                 0, "Device A", "Device key A", true, new MtpRoot[0], null, null));
449         mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
450                 1, "Device B", "Device key B", true, new MtpRoot[0], null, null));
451         mDatabase.getMapper().stopAddingDocuments(null);
452 
453         mDatabase.getMapper().startAddingDocuments("1");
454         mDatabase.getMapper().startAddingDocuments("2");
455         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
456                 new MtpRoot(0, 100, "Storage", 0, 0, "")
457         });
458         mDatabase.getMapper().putStorageDocuments("2", OPERATIONS_SUPPORTED, new MtpRoot[] {
459                 new MtpRoot(1, 100, "Storage", 0, 0, "")
460         });
461 
462         {
463             final Cursor cursor = mDatabase.queryRootDocuments(columns);
464             assertEquals(2, cursor.getCount());
465             cursor.moveToNext();
466             assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
467             assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
468             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
469             cursor.moveToNext();
470             assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
471             assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
472             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
473             cursor.close();
474         }
475 
476         {
477             final Cursor cursor = mDatabase.queryRoots(resources, rootColumns);
478             assertEquals(2, cursor.getCount());
479             cursor.moveToNext();
480             assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
481             assertEquals(0, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
482             cursor.moveToNext();
483             assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
484             assertEquals(0, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
485             cursor.close();
486         }
487 
488         mDatabase.getMapper().clearMapping();
489 
490         mDatabase.getMapper().startAddingDocuments(null);
491         mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
492                 0, "Device A", "Device key A", true, new MtpRoot[0], null, null));
493         mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
494                 1, "Device B", "Device key B", true, new MtpRoot[0], null, null));
495         mDatabase.getMapper().stopAddingDocuments(null);
496 
497         mDatabase.getMapper().startAddingDocuments("1");
498         mDatabase.getMapper().startAddingDocuments("2");
499         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
500                 new MtpRoot(0, 200, "Storage", 2000, 0, "")
501         });
502         mDatabase.getMapper().putStorageDocuments("2", OPERATIONS_SUPPORTED, new MtpRoot[] {
503                 new MtpRoot(1, 300, "Storage", 3000, 0, "")
504         });
505         mDatabase.getMapper().stopAddingDocuments("1");
506         mDatabase.getMapper().stopAddingDocuments("2");
507 
508         {
509             final Cursor cursor = mDatabase.queryRootDocuments(columns);
510             assertEquals(2, cursor.getCount());
511             cursor.moveToNext();
512             assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
513             assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
514             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
515             cursor.moveToNext();
516             assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
517             assertEquals(300, getInt(cursor, COLUMN_STORAGE_ID));
518             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
519             cursor.close();
520         }
521 
522         {
523             final Cursor cursor = mDatabase.queryRoots(resources, rootColumns);
524             assertEquals(2, cursor.getCount());
525             cursor.moveToNext();
526             assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
527             assertEquals(2000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
528             cursor.moveToNext();
529             assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
530             assertEquals(3000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
531             cursor.close();
532         }
533     }
534 
testRestoreIdForDifferentParents()535     public void testRestoreIdForDifferentParents() throws Exception {
536         final String[] columns = new String[] {
537                 DocumentsContract.Document.COLUMN_DOCUMENT_ID,
538                 MtpDatabaseConstants.COLUMN_OBJECT_HANDLE
539         };
540 
541         // Add device, storage, and two directories.
542         addTestDevice();
543         addTestStorage("1");
544         mDatabase.getMapper().startAddingDocuments("2");
545         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
546                 createDocument(50, "A", MtpConstants.FORMAT_ASSOCIATION, 0),
547                 createDocument(51, "B", MtpConstants.FORMAT_ASSOCIATION, 0),
548         }, new long[] { 0L, 0L });
549         mDatabase.getMapper().stopAddingDocuments("2");
550 
551         // Put note.txt in each directory.
552         mDatabase.getMapper().startAddingDocuments("3");
553         mDatabase.getMapper().startAddingDocuments("4");
554         mDatabase.getMapper().putChildDocuments(0, "3", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
555                 createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
556         }, new long[] { 1024L });
557         mDatabase.getMapper().putChildDocuments(0, "4", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
558                 createDocument(101, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
559         }, new long[] { 1024L });
560 
561         // Clear mapping.
562         mDatabase.getMapper().clearMapping();
563 
564         // Add device, storage, and two directories again.
565         addTestDevice();
566         addTestStorage("1");
567         mDatabase.getMapper().startAddingDocuments("2");
568         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
569                 createDocument(50, "A", MtpConstants.FORMAT_ASSOCIATION, 0),
570                 createDocument(51, "B", MtpConstants.FORMAT_ASSOCIATION, 0),
571         }, new long[] { 0L, 0L });
572         mDatabase.getMapper().stopAddingDocuments("2");
573 
574         // Add note.txt in each directory again.
575         mDatabase.getMapper().startAddingDocuments("3");
576         mDatabase.getMapper().startAddingDocuments("4");
577         mDatabase.getMapper().putChildDocuments(0, "3", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
578                 createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
579         }, new long[] { 1024L });
580         mDatabase.getMapper().putChildDocuments(0, "4", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
581                 createDocument(201, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
582         }, new long[] { 1024L });
583         mDatabase.getMapper().stopAddingDocuments("3");
584         mDatabase.getMapper().stopAddingDocuments("4");
585 
586         // Check if the two note.txt are mapped correctly.
587         {
588             final Cursor cursor = mDatabase.queryChildDocuments(columns, "3");
589             assertEquals(1, cursor.getCount());
590             cursor.moveToNext();
591             assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
592             assertEquals(200, getInt(cursor, COLUMN_OBJECT_HANDLE));
593             cursor.close();
594         }
595         {
596             final Cursor cursor = mDatabase.queryChildDocuments(columns, "4");
597             assertEquals(1, cursor.getCount());
598             cursor.moveToNext();
599             assertEquals(6, getInt(cursor, COLUMN_DOCUMENT_ID));
600             assertEquals(201, getInt(cursor, COLUMN_OBJECT_HANDLE));
601             cursor.close();
602         }
603     }
604 
testClearMtpIdentifierBeforeResolveRootDocuments()605     public void testClearMtpIdentifierBeforeResolveRootDocuments() throws Exception {
606         final String[] columns = new String[] {
607                 DocumentsContract.Document.COLUMN_DOCUMENT_ID,
608                 MtpDatabaseConstants.COLUMN_STORAGE_ID,
609                 DocumentsContract.Document.COLUMN_DISPLAY_NAME
610         };
611         final String[] rootColumns = new String[] {
612                 Root.COLUMN_ROOT_ID,
613                 Root.COLUMN_AVAILABLE_BYTES
614         };
615 
616         addTestDevice();
617 
618         mDatabase.getMapper().startAddingDocuments("1");
619         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
620                 new MtpRoot(0, 100, "Storage", 0, 0, ""),
621         });
622         mDatabase.getMapper().clearMapping();
623 
624         addTestDevice();
625 
626         try (final Cursor cursor = mDatabase.queryRoots(resources, rootColumns)) {
627             assertEquals(1, cursor.getCount());
628             cursor.moveToNext();
629             assertEquals("1", getString(cursor, Root.COLUMN_ROOT_ID));
630         }
631 
632         mDatabase.getMapper().startAddingDocuments("1");
633         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
634                 new MtpRoot(0, 200, "Storage", 2000, 0, ""),
635         });
636         mDatabase.getMapper().clearMapping();
637 
638         addTestDevice();
639 
640         mDatabase.getMapper().startAddingDocuments("1");
641         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
642                 new MtpRoot(0, 300, "Storage", 3000, 0, ""),
643         });
644         mDatabase.getMapper().stopAddingDocuments("1");
645 
646         {
647             final Cursor cursor = mDatabase.queryRootDocuments(columns);
648             assertEquals(1, cursor.getCount());
649             cursor.moveToNext();
650             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
651             assertEquals(300, getInt(cursor, COLUMN_STORAGE_ID));
652             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
653             cursor.close();
654         }
655         {
656             final Cursor cursor = mDatabase.queryRoots(resources, rootColumns);
657             assertEquals(1, cursor.getCount());
658             cursor.moveToNext();
659             assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
660             assertEquals(3000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
661             cursor.close();
662         }
663     }
664 
testPutSameNameRootsAfterClearing()665     public void testPutSameNameRootsAfterClearing() throws Exception {
666         final String[] columns = new String[] {
667                 DocumentsContract.Document.COLUMN_DOCUMENT_ID,
668                 MtpDatabaseConstants.COLUMN_STORAGE_ID,
669                 DocumentsContract.Document.COLUMN_DISPLAY_NAME
670         };
671 
672         // Add a device and a storage.
673         addTestDevice();
674         addTestStorage("1");
675 
676         // Disconnect devices.
677         mDatabase.getMapper().clearMapping();
678 
679         // Add a device and two storages that has same name.
680         addTestDevice();
681         mDatabase.getMapper().startAddingDocuments("1");
682         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
683                 new MtpRoot(0, 200, "Storage", 2000, 0, ""),
684                 new MtpRoot(0, 201, "Storage", 2001, 0, ""),
685         });
686         mDatabase.getMapper().stopAddingDocuments("1");
687 
688         {
689             final Cursor cursor = mDatabase.queryRootDocuments(columns);
690             assertEquals(2, cursor.getCount());
691 
692             // First storage reuse document ID of previous storage.
693             cursor.moveToNext();
694             // One reuses exisitng document ID 1.
695             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
696             assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
697             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
698 
699             // Second one has new document ID.
700             cursor.moveToNext();
701             assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
702             assertEquals(201, getInt(cursor, COLUMN_STORAGE_ID));
703             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
704 
705             cursor.close();
706         }
707     }
708 
testReplaceExistingRoots()709     public void testReplaceExistingRoots() throws Exception {
710         addTestDevice();
711 
712         // The client code should be able to replace existing rows with new information.
713         // Add one.
714         mDatabase.getMapper().startAddingDocuments("1");
715         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
716                 new MtpRoot(0, 100, "Storage A", 0, 0, ""),
717         });
718         mDatabase.getMapper().stopAddingDocuments("1");
719         // Replace it.
720         mDatabase.getMapper().startAddingDocuments("1");
721         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
722                 new MtpRoot(0, 100, "Storage B", 1000, 1000, ""),
723         });
724         mDatabase.getMapper().stopAddingDocuments("1");
725         {
726             final String[] columns = new String[] {
727                     DocumentsContract.Document.COLUMN_DOCUMENT_ID,
728                     MtpDatabaseConstants.COLUMN_STORAGE_ID,
729                     DocumentsContract.Document.COLUMN_DISPLAY_NAME
730             };
731             final Cursor cursor = mDatabase.queryRootDocuments(columns);
732             assertEquals(1, cursor.getCount());
733             cursor.moveToNext();
734             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
735             assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
736             assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
737             cursor.close();
738         }
739         {
740             final String[] columns = new String[] {
741                     Root.COLUMN_ROOT_ID,
742                     Root.COLUMN_TITLE,
743                     Root.COLUMN_AVAILABLE_BYTES
744             };
745             final Cursor cursor = mDatabase.queryRoots(resources, columns);
746             assertEquals(1, cursor.getCount());
747             cursor.moveToNext();
748             assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
749             assertEquals("Device Storage B", getString(cursor, Root.COLUMN_TITLE));
750             assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
751             cursor.close();
752         }
753     }
754 
testFailToReplaceExisitingUnmappedRoots()755     public void testFailToReplaceExisitingUnmappedRoots() throws Exception {
756         // The client code should not be able to replace rows before resolving 'unmapped' rows.
757         // Add one.
758         addTestDevice();
759         mDatabase.getMapper().startAddingDocuments("1");
760         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
761                 new MtpRoot(0, 100, "Storage A", 0, 0, ""),
762         });
763         mDatabase.getMapper().clearMapping();
764 
765         addTestDevice();
766         try (final Cursor oldCursor =
767                 mDatabase.queryRoots(resources, strings(Root.COLUMN_ROOT_ID))) {
768             assertEquals(1, oldCursor.getCount());
769             oldCursor.moveToNext();
770             assertEquals("1", getString(oldCursor, Root.COLUMN_ROOT_ID));
771 
772             // Add one.
773             mDatabase.getMapper().startAddingDocuments("1");
774             mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
775                     new MtpRoot(0, 101, "Storage B", 1000, 1000, ""),
776             });
777             // Add one more before resolving unmapped documents.
778             mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
779                     new MtpRoot(0, 102, "Storage B", 1000, 1000, ""),
780             });
781             mDatabase.getMapper().stopAddingDocuments("1");
782 
783             // Because the roots shares the same name, the roots should have new IDs.
784             try (final Cursor newCursor = mDatabase.queryChildDocuments(
785                     strings(Document.COLUMN_DOCUMENT_ID), "1")) {
786                 assertEquals(2, newCursor.getCount());
787                 newCursor.moveToNext();
788                 assertFalse(oldCursor.getString(0).equals(newCursor.getString(0)));
789                 newCursor.moveToNext();
790                 assertFalse(oldCursor.getString(0).equals(newCursor.getString(0)));
791             }
792         }
793     }
794 
testQueryDocuments()795     public void testQueryDocuments() throws Exception {
796         addTestDevice();
797         addTestStorage("1");
798 
799         final Cursor cursor = mDatabase.queryDocument("2", strings(Document.COLUMN_DISPLAY_NAME));
800         assertEquals(1, cursor.getCount());
801         cursor.moveToNext();
802         assertEquals("Storage", getString(cursor, Document.COLUMN_DISPLAY_NAME));
803         cursor.close();
804     }
805 
testQueryRoots()806     public void testQueryRoots() throws Exception {
807         // Add device document.
808         addTestDevice();
809 
810         // It the device does not have storages, it shows a device root.
811         {
812             final Cursor cursor = mDatabase.queryRoots(resources, strings(Root.COLUMN_TITLE));
813             assertEquals(1, cursor.getCount());
814             cursor.moveToNext();
815             assertEquals("Device", cursor.getString(0));
816             cursor.close();
817         }
818 
819         mDatabase.getMapper().startAddingDocuments("1");
820         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
821                 new MtpRoot(0, 100, "Storage A", 0, 0, "")
822         });
823         mDatabase.getMapper().stopAddingDocuments("1");
824 
825         // It the device has single storage, it shows a storage root.
826         {
827             final Cursor cursor = mDatabase.queryRoots(resources, strings(Root.COLUMN_TITLE));
828             assertEquals(1, cursor.getCount());
829             cursor.moveToNext();
830             assertEquals("Device Storage A", cursor.getString(0));
831             cursor.close();
832         }
833 
834         mDatabase.getMapper().startAddingDocuments("1");
835         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
836                 new MtpRoot(0, 100, "Storage A", 0, 0, ""),
837                 new MtpRoot(0, 101, "Storage B", 0, 0, "")
838         });
839         mDatabase.getMapper().stopAddingDocuments("1");
840 
841         // It the device has multiple storages, it shows a device root.
842         {
843             final Cursor cursor = mDatabase.queryRoots(resources, strings(Root.COLUMN_TITLE));
844             assertEquals(1, cursor.getCount());
845             cursor.moveToNext();
846             assertEquals("Device", cursor.getString(0));
847             cursor.close();
848         }
849     }
850 
testGetParentId()851     public void testGetParentId() throws FileNotFoundException {
852         addTestDevice();
853 
854         mDatabase.getMapper().startAddingDocuments("1");
855         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
856                 new MtpRoot(0, 100, "Storage A", 0, 0, ""),
857         });
858         mDatabase.getMapper().stopAddingDocuments("1");
859 
860         mDatabase.getMapper().startAddingDocuments("2");
861         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
862                 createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
863         }, new long[] { 1024L });
864         mDatabase.getMapper().stopAddingDocuments("2");
865 
866         assertEquals("2", mDatabase.getParentIdentifier("3").mDocumentId);
867     }
868 
testDeleteDocument()869     public void testDeleteDocument() throws Exception {
870         addTestDevice();
871         addTestStorage("1");
872 
873         mDatabase.getMapper().startAddingDocuments("2");
874         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
875                 createDocument(200, "dir", MtpConstants.FORMAT_ASSOCIATION, 1024),
876         }, new long[] { 1024L });
877         mDatabase.getMapper().stopAddingDocuments("2");
878 
879         mDatabase.getMapper().startAddingDocuments("3");
880         mDatabase.getMapper().putChildDocuments(0, "3", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
881                 createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
882         }, new long[] { 1024L });
883         mDatabase.getMapper().stopAddingDocuments("3");
884 
885         mDatabase.deleteDocument("3");
886 
887         {
888             // Do not query deleted documents.
889             final Cursor cursor =
890                     mDatabase.queryChildDocuments(strings(Document.COLUMN_DOCUMENT_ID), "2");
891             assertEquals(0, cursor.getCount());
892             cursor.close();
893         }
894 
895         {
896             // Child document should be deleted also.
897             final Cursor cursor =
898                     mDatabase.queryDocument("4", strings(Document.COLUMN_DOCUMENT_ID));
899             assertEquals(0, cursor.getCount());
900             cursor.close();
901         }
902     }
903 
testPutNewDocument()904     public void testPutNewDocument() throws Exception {
905         addTestDevice();
906         addTestStorage("1");
907 
908         assertEquals(
909                 "3",
910                 mDatabase.putNewDocument(
911                         0, "2", OPERATIONS_SUPPORTED,
912                         createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
913                         1024L));
914 
915         {
916             final Cursor cursor =
917                     mDatabase.queryChildDocuments(strings(Document.COLUMN_DOCUMENT_ID), "2");
918             assertEquals(1, cursor.getCount());
919             cursor.moveToNext();
920             assertEquals("3", cursor.getString(0));
921             cursor.close();
922         }
923 
924         // The new document should not be mapped with existing invalidated document.
925         mDatabase.getMapper().clearMapping();
926         addTestDevice();
927         addTestStorage("1");
928 
929         mDatabase.getMapper().startAddingDocuments("2");
930         mDatabase.putNewDocument(
931                 0, "2", OPERATIONS_SUPPORTED,
932                 createDocument(201, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
933                 1024L);
934         mDatabase.getMapper().stopAddingDocuments("2");
935 
936         {
937             final Cursor cursor =
938                     mDatabase.queryChildDocuments(strings(Document.COLUMN_DOCUMENT_ID), "2");
939             assertEquals(1, cursor.getCount());
940             cursor.moveToNext();
941             assertEquals("4", cursor.getString(0));
942             cursor.close();
943         }
944     }
945 
testGetDocumentIdForDevice()946     public void testGetDocumentIdForDevice() throws Exception {
947         addTestDevice();
948         assertEquals("1", mDatabase.getDocumentIdForDevice(0));
949     }
950 
testGetClosedDevice()951     public void testGetClosedDevice() throws Exception {
952         mDatabase.getMapper().startAddingDocuments(null);
953         mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
954                 0, "Device", null /* deviceKey */, /* opened is */ false, new MtpRoot[0], null,
955                 null));
956         mDatabase.getMapper().stopAddingDocuments(null);
957 
958         final String[] columns = new String [] {
959                 DocumentsContract.Root.COLUMN_ROOT_ID,
960                 DocumentsContract.Root.COLUMN_TITLE,
961                 DocumentsContract.Root.COLUMN_AVAILABLE_BYTES
962         };
963         try (final Cursor cursor = mDatabase.queryRoots(resources, columns)) {
964             assertEquals(1, cursor.getCount());
965             assertTrue(cursor.moveToNext());
966             assertEquals(1, cursor.getLong(0));
967             assertEquals("Device", cursor.getString(1));
968             assertTrue(cursor.isNull(2));
969         }
970     }
971 
testMappingWithoutKey()972     public void testMappingWithoutKey() throws FileNotFoundException {
973         mDatabase.getMapper().startAddingDocuments(null);
974         mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
975                 0, "Device", null /* device key */, /* opened is */ true, new MtpRoot[0], null,
976                 null));
977         mDatabase.getMapper().stopAddingDocuments(null);
978 
979         mDatabase.getMapper().startAddingDocuments(null);
980         mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
981                 0, "Device", null /* device key */, /* opened is */ true, new MtpRoot[0], null,
982                 null));
983         mDatabase.getMapper().stopAddingDocuments(null);
984 
985         try (final Cursor cursor =
986                 mDatabase.queryRoots(resources, strings(DocumentsContract.Root.COLUMN_ROOT_ID))) {
987             assertEquals(1, cursor.getCount());
988             assertTrue(cursor.moveToNext());
989             assertEquals(1, cursor.getLong(0));
990         }
991     }
992 
testMappingFailsWithoutKey()993     public void testMappingFailsWithoutKey() throws FileNotFoundException {
994         mDatabase.getMapper().startAddingDocuments(null);
995         mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
996                 0, "Device", null /* device key */, /* opened is */ true, new MtpRoot[0], null,
997                 null));
998         mDatabase.getMapper().stopAddingDocuments(null);
999 
1000         // MTP identifier is cleared here. Mapping no longer works without device key.
1001         mDatabase.getMapper().startAddingDocuments(null);
1002         mDatabase.getMapper().stopAddingDocuments(null);
1003 
1004         mDatabase.getMapper().startAddingDocuments(null);
1005         mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
1006                 0, "Device", null /* device key */, /* opened is */ true, new MtpRoot[0], null,
1007                 null));
1008         mDatabase.getMapper().stopAddingDocuments(null);
1009 
1010         try (final Cursor cursor =
1011                 mDatabase.queryRoots(resources, strings(DocumentsContract.Root.COLUMN_ROOT_ID))) {
1012             assertEquals(1, cursor.getCount());
1013             assertTrue(cursor.moveToNext());
1014             assertEquals(2, cursor.getLong(0));
1015         }
1016     }
1017 
testUpdateDocumentWithoutChange()1018     public void testUpdateDocumentWithoutChange() throws FileNotFoundException {
1019         mDatabase.getMapper().startAddingDocuments(null);
1020         assertTrue(mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
1021                 0, "Device", "device_key", /* opened is */ true, new MtpRoot[0], null,
1022                 null)));
1023         assertFalse(mDatabase.getMapper().stopAddingDocuments(null));
1024 
1025         mDatabase.getMapper().startAddingDocuments(null);
1026         assertFalse(mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
1027                 0, "Device", "device_key", /* opened is */ true, new MtpRoot[0], null,
1028                 null)));
1029         assertFalse(mDatabase.getMapper().stopAddingDocuments(null));
1030     }
1031 
testSetBootCount()1032     public void testSetBootCount() {
1033         assertEquals(0, mDatabase.getLastBootCount());
1034         mDatabase.setLastBootCount(10);
1035         assertEquals(10, mDatabase.getLastBootCount());
1036         try {
1037             mDatabase.setLastBootCount(-1);
1038             fail();
1039         } catch (IllegalArgumentException e) {}
1040     }
1041 
testCleanDatabase()1042     public void testCleanDatabase() throws FileNotFoundException {
1043         // Add tree.
1044         addTestDevice();
1045         addTestStorage("1");
1046         mDatabase.getMapper().startAddingDocuments("2");
1047         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
1048                 createDocument(100, "apple.txt", MtpConstants.FORMAT_TEXT, 1024),
1049                 createDocument(101, "orange.txt", MtpConstants.FORMAT_TEXT, 1024),
1050         }, new long[] { 1024L, 1024L });
1051         mDatabase.getMapper().stopAddingDocuments("2");
1052 
1053         // Disconnect the device.
1054         mDatabase.getMapper().startAddingDocuments(null);
1055         mDatabase.getMapper().stopAddingDocuments(null);
1056 
1057         // Clean database with error column
1058         mDatabase.cleanDatabase(new Uri[] {
1059                 DocumentsContract.buildDocumentUri(MtpDocumentsProvider.AUTHORITY,
1060                         "wrong_id_test")
1061         });
1062 
1063         // Clean database with error uri
1064         mDatabase.cleanDatabase(new Uri[] {
1065                 Uri.parse("content://com.android.providers.downloads.documents/wrong_uri_test")
1066         });
1067 
1068         // Clean database.
1069         mDatabase.cleanDatabase(new Uri[] {
1070                 DocumentsContract.buildDocumentUri(MtpDocumentsProvider.AUTHORITY, "3")
1071         });
1072 
1073         // Add tree again.
1074         addTestDevice();
1075         addTestStorage("1");
1076         mDatabase.getMapper().startAddingDocuments("2");
1077         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
1078                 createDocument(100, "apple.txt", MtpConstants.FORMAT_TEXT, 1024),
1079                 createDocument(101, "orange.txt", MtpConstants.FORMAT_TEXT, 1024),
1080         }, new long[] { 1024L, 1024L });
1081         mDatabase.getMapper().stopAddingDocuments("2");
1082 
1083         try (final Cursor cursor = mDatabase.queryChildDocuments(
1084                 strings(COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME), "2")) {
1085             assertEquals(2, cursor.getCount());
1086 
1087             // Persistent uri uses the same ID.
1088             cursor.moveToNext();
1089             assertEquals("3", cursor.getString(0));
1090             assertEquals("apple.txt", cursor.getString(1));
1091 
1092             // Others does not.
1093             cursor.moveToNext();
1094             assertEquals("5", cursor.getString(0));
1095             assertEquals("orange.txt", cursor.getString(1));
1096         }
1097     }
1098 
testFormatCodeForMpeg()1099     public void testFormatCodeForMpeg() throws FileNotFoundException {
1100         addTestDevice();
1101         addTestStorage("1");
1102         mDatabase.getMapper().startAddingDocuments("2");
1103         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
1104             createDocument(100, "audio.m4a", MtpConstants.FORMAT_MPEG, 1000),
1105             createDocument(101, "video.m4v", MtpConstants.FORMAT_MPEG, 1000),
1106             createDocument(102, "unknown.mp4", MtpConstants.FORMAT_MPEG, 1000),
1107             createDocument(103, "inconsistent.txt", MtpConstants.FORMAT_MPEG, 1000),
1108             createDocument(104, "noext", MtpConstants.FORMAT_UNDEFINED, 1000),
1109         }, new long[] { 1000L, 1000L, 1000L, 1000L, 1000L });
1110         mDatabase.getMapper().stopAddingDocuments("2");
1111         try (final Cursor cursor = mDatabase.queryChildDocuments(
1112                 strings(COLUMN_DISPLAY_NAME,  COLUMN_MIME_TYPE),
1113                 "2")) {
1114             assertEquals(5, cursor.getCount());
1115             cursor.moveToNext();
1116             assertEquals("audio.m4a", cursor.getString(0));
1117             assertEquals("audio/mp4", cursor.getString(1));
1118             cursor.moveToNext();
1119             assertEquals("video.m4v", cursor.getString(0));
1120             assertEquals("video/mp4", cursor.getString(1));
1121             cursor.moveToNext();
1122             // Assume that the file is video as we don't have any hints to find out if the file is
1123             // video or audio.
1124             assertEquals("unknown.mp4", cursor.getString(0));
1125             assertEquals("video/mp4", cursor.getString(1));
1126             // Don't return mime type that is inconsistent with format code.
1127             cursor.moveToNext();
1128             assertEquals("inconsistent.txt", cursor.getString(0));
1129             assertEquals("video/mpeg", cursor.getString(1));
1130             cursor.moveToNext();
1131             assertEquals("noext", cursor.getString(0));
1132             assertEquals("application/octet-stream", cursor.getString(1));
1133         }
1134     }
1135 
addTestDevice()1136     private void addTestDevice() throws FileNotFoundException {
1137         TestUtil.addTestDevice(mDatabase);
1138     }
1139 
addTestStorage(String parentId)1140     private void addTestStorage(String parentId) throws FileNotFoundException {
1141         TestUtil.addTestStorage(mDatabase, parentId);
1142     }
1143 }
1144