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