Home | History | Annotate | Download | only in mtp
      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 android.database.Cursor;
     20 import android.media.MediaFile;
     21 import android.media.MediaFile.MediaFileType;
     22 import android.mtp.MtpConstants;
     23 import android.mtp.MtpObjectInfo;
     24 import android.net.Uri;
     25 import android.provider.DocumentsContract;
     26 import android.provider.DocumentsContract.Document;
     27 import android.provider.DocumentsContract.Root;
     28 import android.test.AndroidTestCase;
     29 import android.test.suitebuilder.annotation.SmallTest;
     30 
     31 import java.io.FileNotFoundException;
     32 import java.util.Arrays;
     33 
     34 import static android.provider.DocumentsContract.Document.*;
     35 import static com.android.mtp.MtpDatabase.strings;
     36 import static com.android.mtp.MtpDatabaseConstants.*;
     37 import static com.android.mtp.TestUtil.OPERATIONS_SUPPORTED;
     38 
     39 @SmallTest
     40 public class MtpDatabaseTest extends AndroidTestCase {
     41     private static final String[] COLUMN_NAMES = new String[] {
     42         DocumentsContract.Document.COLUMN_DOCUMENT_ID,
     43         MtpDatabaseConstants.COLUMN_DEVICE_ID,
     44         MtpDatabaseConstants.COLUMN_STORAGE_ID,
     45         MtpDatabaseConstants.COLUMN_OBJECT_HANDLE,
     46         DocumentsContract.Document.COLUMN_MIME_TYPE,
     47         DocumentsContract.Document.COLUMN_DISPLAY_NAME,
     48         DocumentsContract.Document.COLUMN_SUMMARY,
     49         DocumentsContract.Document.COLUMN_LAST_MODIFIED,
     50         DocumentsContract.Document.COLUMN_ICON,
     51         DocumentsContract.Document.COLUMN_FLAGS,
     52         DocumentsContract.Document.COLUMN_SIZE,
     53         MtpDatabaseConstants.COLUMN_DOCUMENT_TYPE
     54     };
     55 
     56     private final TestResources resources = new TestResources();
     57     MtpDatabase mDatabase;
     58 
     59     @Override
     60     public void setUp() {
     61         mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
     62     }
     63 
     64     @Override
     65     public void tearDown() {
     66         mDatabase.close();
     67         mDatabase = null;
     68     }
     69 
     70     private static int getInt(Cursor cursor, String columnName) {
     71         return cursor.getInt(cursor.getColumnIndex(columnName));
     72     }
     73 
     74     private static boolean isNull(Cursor cursor, String columnName) {
     75         return cursor.isNull(cursor.getColumnIndex(columnName));
     76     }
     77 
     78     private static String getString(Cursor cursor, String columnName) {
     79         return cursor.getString(cursor.getColumnIndex(columnName));
     80     }
     81 
     82     public void testPutSingleStorageDocuments() throws Exception {
     83         addTestDevice();
     84 
     85         mDatabase.getMapper().startAddingDocuments("1");
     86         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
     87                 new MtpRoot(0, 1, "Storage", 1000, 2000, "")
     88         });
     89         mDatabase.getMapper().stopAddingDocuments("1");
     90 
     91         {
     92             final Cursor cursor = mDatabase.queryRootDocuments(COLUMN_NAMES);
     93             assertEquals(1, cursor.getCount());
     94 
     95             cursor.moveToNext();
     96             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
     97             assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
     98             assertEquals(1, getInt(cursor, COLUMN_STORAGE_ID));
     99             assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
    100             assertEquals(
    101                     DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE));
    102             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
    103             assertTrue(isNull(cursor, COLUMN_SUMMARY));
    104             assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
    105             assertEquals(R.drawable.ic_root_mtp, getInt(cursor, COLUMN_ICON));
    106             assertEquals(Document.FLAG_DIR_SUPPORTS_CREATE, getInt(cursor, COLUMN_FLAGS));
    107             assertEquals(1000, getInt(cursor, COLUMN_SIZE));
    108             assertEquals(
    109                     MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE,
    110                     getInt(cursor, COLUMN_DOCUMENT_TYPE));
    111 
    112             cursor.close();
    113         }
    114 
    115         {
    116             final Cursor cursor = mDatabase.queryRoots(resources, new String [] {
    117                     Root.COLUMN_ROOT_ID,
    118                     Root.COLUMN_FLAGS,
    119                     Root.COLUMN_ICON,
    120                     Root.COLUMN_TITLE,
    121                     Root.COLUMN_SUMMARY,
    122                     Root.COLUMN_DOCUMENT_ID,
    123                     Root.COLUMN_AVAILABLE_BYTES,
    124                     Root.COLUMN_CAPACITY_BYTES
    125             });
    126             assertEquals(1, cursor.getCount());
    127 
    128             cursor.moveToNext();
    129             assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
    130             assertEquals(
    131                     Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY,
    132                     getInt(cursor, Root.COLUMN_FLAGS));
    133             assertEquals(R.drawable.ic_root_mtp, getInt(cursor, Root.COLUMN_ICON));
    134             assertEquals("Device Storage", getString(cursor, Root.COLUMN_TITLE));
    135             assertTrue(isNull(cursor, Root.COLUMN_SUMMARY));
    136             assertEquals(1, getInt(cursor, Root.COLUMN_DOCUMENT_ID));
    137             assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
    138             assertEquals(2000, getInt(cursor, Root.COLUMN_CAPACITY_BYTES));
    139 
    140             cursor.close();
    141         }
    142     }
    143 
    144     public void testPutStorageDocuments() throws Exception {
    145         addTestDevice();
    146 
    147         mDatabase.getMapper().startAddingDocuments("1");
    148         mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
    149                 new MtpRoot(0, 1, "Storage", 1000, 2000, ""),
    150                 new MtpRoot(0, 2, "Storage", 2000, 4000, ""),
    151                 new MtpRoot(0, 3, "/@#%&<>Storage", 3000, 6000,"")
    152         });
    153 
    154         {
    155             final Cursor cursor = mDatabase.queryRootDocuments(COLUMN_NAMES);
    156             assertEquals(3, cursor.getCount());
    157 
    158             cursor.moveToNext();
    159             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
    160             assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
    161             assertEquals(1, getInt(cursor, COLUMN_STORAGE_ID));
    162             assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
    163             assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE));
    164             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
    165             assertTrue(isNull(cursor, COLUMN_SUMMARY));
    166             assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
    167             assertEquals(R.drawable.ic_root_mtp, getInt(cursor, COLUMN_ICON));
    168             assertEquals(Document.FLAG_DIR_SUPPORTS_CREATE, getInt(cursor, COLUMN_FLAGS));
    169             assertEquals(1000, getInt(cursor, COLUMN_SIZE));
    170             assertEquals(
    171                     MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, getInt(cursor, COLUMN_DOCUMENT_TYPE));
    172 
    173             cursor.moveToNext();
    174             assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
    175             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
    176 
    177             cursor.moveToNext();
    178             assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
    179             assertEquals("/@#%&<>Storage", getString(cursor, COLUMN_DISPLAY_NAME));
    180 
    181             cursor.close();
    182         }
    183     }
    184 
    185     private MtpObjectInfo createDocument(int objectHandle, String name, int format, int size) {
    186         final MtpObjectInfo.Builder builder = new MtpObjectInfo.Builder();
    187         builder.setObjectHandle(objectHandle);
    188         builder.setName(name);
    189         builder.setFormat(format);
    190         builder.setCompressedSize(size);
    191         return builder.build();
    192     }
    193 
    194     public void testPutChildDocuments() throws Exception {
    195         addTestDevice();
    196         addTestStorage("1");
    197 
    198         mDatabase.getMapper().startAddingDocuments("2");
    199         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
    200                 createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
    201                 createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
    202                 createDocument(102, "music.mp3", MtpConstants.FORMAT_MP3, 3 * 1024 * 1024)
    203         }, new long[] { 1024L, 2L * 1024L * 1024L, 3L * 1024L * 1024L});
    204 
    205         final Cursor cursor = mDatabase.queryChildDocuments(COLUMN_NAMES, "2");
    206         assertEquals(3, cursor.getCount());
    207 
    208         cursor.moveToNext();
    209         assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
    210         assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
    211         assertEquals(0, getInt(cursor, COLUMN_STORAGE_ID));
    212         assertEquals(100, getInt(cursor, COLUMN_OBJECT_HANDLE));
    213         assertEquals("text/plain", getString(cursor, COLUMN_MIME_TYPE));
    214         assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
    215         assertTrue(isNull(cursor, COLUMN_SUMMARY));
    216         assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
    217         assertTrue(isNull(cursor, COLUMN_ICON));
    218         assertEquals(
    219                 COLUMN_FLAGS,
    220                 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
    221                 DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
    222                 cursor.getInt(9));
    223         assertEquals(1024, getInt(cursor, COLUMN_SIZE));
    224         assertEquals(
    225                 MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, getInt(cursor, COLUMN_DOCUMENT_TYPE));
    226 
    227         cursor.moveToNext();
    228         assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
    229         assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
    230         assertEquals(0, getInt(cursor, COLUMN_STORAGE_ID));
    231         assertEquals(101, getInt(cursor, COLUMN_OBJECT_HANDLE));
    232         assertEquals("image/jpeg", getString(cursor, COLUMN_MIME_TYPE));
    233         assertEquals("image.jpg", getString(cursor, COLUMN_DISPLAY_NAME));
    234         assertTrue(isNull(cursor, COLUMN_SUMMARY));
    235         assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
    236         assertTrue(isNull(cursor, COLUMN_ICON));
    237         assertEquals(
    238                 COLUMN_FLAGS,
    239                 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
    240                 DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    946     public void testGetDocumentIdForDevice() throws Exception {
    947         addTestDevice();
    948         assertEquals("1", mDatabase.getDocumentIdForDevice(0));
    949     }
    950 
    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 
    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 
    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 
   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 
   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 
   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.
   1058         mDatabase.cleanDatabase(new Uri[] {
   1059                 DocumentsContract.buildDocumentUri(MtpDocumentsProvider.AUTHORITY, "3")
   1060         });
   1061 
   1062         // Add tree again.
   1063         addTestDevice();
   1064         addTestStorage("1");
   1065         mDatabase.getMapper().startAddingDocuments("2");
   1066         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
   1067                 createDocument(100, "apple.txt", MtpConstants.FORMAT_TEXT, 1024),
   1068                 createDocument(101, "orange.txt", MtpConstants.FORMAT_TEXT, 1024),
   1069         }, new long[] { 1024L, 1024L });
   1070         mDatabase.getMapper().stopAddingDocuments("2");
   1071 
   1072         try (final Cursor cursor = mDatabase.queryChildDocuments(
   1073                 strings(COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME), "2")) {
   1074             assertEquals(2, cursor.getCount());
   1075 
   1076             // Persistent uri uses the same ID.
   1077             cursor.moveToNext();
   1078             assertEquals("3", cursor.getString(0));
   1079             assertEquals("apple.txt", cursor.getString(1));
   1080 
   1081             // Others does not.
   1082             cursor.moveToNext();
   1083             assertEquals("5", cursor.getString(0));
   1084             assertEquals("orange.txt", cursor.getString(1));
   1085         }
   1086     }
   1087 
   1088     public void testFormatCodeForMpeg() throws FileNotFoundException {
   1089         addTestDevice();
   1090         addTestStorage("1");
   1091         mDatabase.getMapper().startAddingDocuments("2");
   1092         mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
   1093             createDocument(100, "audio.m4a", MtpConstants.FORMAT_MPEG, 1000),
   1094             createDocument(101, "video.m4v", MtpConstants.FORMAT_MPEG, 1000),
   1095             createDocument(102, "unknown.mp4", MtpConstants.FORMAT_MPEG, 1000),
   1096             createDocument(103, "inconsistent.txt", MtpConstants.FORMAT_MPEG, 1000),
   1097             createDocument(104, "noext", MtpConstants.FORMAT_UNDEFINED, 1000),
   1098         }, new long[] { 1000L, 1000L, 1000L, 1000L, 1000L });
   1099         mDatabase.getMapper().stopAddingDocuments("2");
   1100         try (final Cursor cursor = mDatabase.queryChildDocuments(
   1101                 strings(COLUMN_DISPLAY_NAME,  COLUMN_MIME_TYPE),
   1102                 "2")) {
   1103             assertEquals(5, cursor.getCount());
   1104             cursor.moveToNext();
   1105             assertEquals("audio.m4a", cursor.getString(0));
   1106             assertEquals("audio/mp4", cursor.getString(1));
   1107             cursor.moveToNext();
   1108             assertEquals("video.m4v", cursor.getString(0));
   1109             assertEquals("video/mp4", cursor.getString(1));
   1110             cursor.moveToNext();
   1111             // Assume that the file is video as we don't have any hints to find out if the file is
   1112             // video or audio.
   1113             assertEquals("unknown.mp4", cursor.getString(0));
   1114             assertEquals("video/mp4", cursor.getString(1));
   1115             // Don't return mime type that is inconsistent with format code.
   1116             cursor.moveToNext();
   1117             assertEquals("inconsistent.txt", cursor.getString(0));
   1118             assertEquals("video/mp4", cursor.getString(1));
   1119             cursor.moveToNext();
   1120             assertEquals("noext", cursor.getString(0));
   1121             assertEquals("application/octet-stream", cursor.getString(1));
   1122         }
   1123     }
   1124 
   1125     private void addTestDevice() throws FileNotFoundException {
   1126         TestUtil.addTestDevice(mDatabase);
   1127     }
   1128 
   1129     private void addTestStorage(String parentId) throws FileNotFoundException {
   1130         TestUtil.addTestStorage(mDatabase, parentId);
   1131     }
   1132 }
   1133