Merge "Extends MtpDatabase so that it can handle child documents."
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 0534313..8568da0 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -84,8 +84,7 @@
             DocumentsContract.Document.COLUMN_DOCUMENT_ID + " = ?";
     private static final String SELECTION_ROOT_DOCUMENTS =
             COLUMN_DEVICE_ID + " = ? AND " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
-    private static final String SELECTION_ROOT_DOCUMENTS_WITH_STATE =
-            SELECTION_ROOT_DOCUMENTS + " AND " + COLUMN_ROW_STATE + " = ?";
+    private static final String SELECTION_CHILD_DOCUMENTS = COLUMN_PARENT_DOCUMENT_ID + " = ?";
 
     static class ParentNotFoundException extends Exception {}
 
@@ -136,7 +135,7 @@
     }
 
     @VisibleForTesting
-    Cursor queryChildDocuments(String[] columnNames) {
+    Cursor queryRootDocuments(String[] columnNames) {
         return database.query(
                 TABLE_DOCUMENTS,
                 columnNames,
@@ -159,84 +158,27 @@
                 null);
     }
 
-    /**
-     * Puts the roots into the database.
-     * If the database found another unmapped document that shares the same name with the root,
-     * the document may be merged into the unmapped document. In that case, the database marks the
-     * root as 'mapping' and wait for {@link #resolveRootDocuments(int)} is invoked.
-     * @param deviceId Device ID of roots.
-     * @param resources Resources used to get localized root name.
-     * @param roots Roots added to the database.
-     */
     @VisibleForTesting
     void putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) {
-        database.beginTransaction();
-        try {
-            // Add roots to database.
-            final ContentValues values = new ContentValues();
-            for (int i = 0; i < roots.length; i++) {
-                getRootDocumentValues(values, resources, roots[i]);
-                final String displayName =
-                        values.getAsString(DocumentsContract.Document.COLUMN_DISPLAY_NAME);
-                final long numUnmapped = DatabaseUtils.queryNumEntries(
-                        database,
-                        TABLE_DOCUMENTS,
-                        SELECTION_ROOT_DOCUMENTS_WITH_STATE + " AND " +
-                        DocumentsContract.Document.COLUMN_DISPLAY_NAME + " = ?",
-                        strings(deviceId, ROW_STATE_UNMAPPED, displayName));
-                if (numUnmapped != 0) {
-                    values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPING);
-                }
-                database.insert(TABLE_DOCUMENTS, null, values);
+        final ContentValues[] valuesList = new ContentValues[roots.length];
+        for (int i = 0; i < roots.length; i++) {
+            if (roots[i].mDeviceId != deviceId) {
+                throw new IllegalArgumentException();
             }
-            database.setTransactionSuccessful();
-        } finally {
-            database.endTransaction();
+            valuesList[i] = new ContentValues();
+            getRootDocumentValues(valuesList[i], resources, roots[i]);
         }
+        putDocuments(valuesList, SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId));
     }
 
     @VisibleForTesting
-    void putDocument(int deviceId, MtpObjectInfo info) throws Exception {
-        database.beginTransaction();
-        try {
-            final String mimeType = CursorHelper.formatTypeToMimeType(info.getFormat());
-
-            int flag = 0;
-            if (info.getProtectionStatus() == 0) {
-                flag |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
-                        DocumentsContract.Document.FLAG_SUPPORTS_WRITE;
-                if (mimeType == DocumentsContract.Document.MIME_TYPE_DIR) {
-                    flag |= DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE;
-                }
-            }
-            if (info.getThumbCompressedSize() > 0) {
-                flag |= DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL;
-            }
-
-            final ContentValues values = new ContentValues();
-            values.put(COLUMN_DEVICE_ID, deviceId);
-            values.put(COLUMN_STORAGE_ID, info.getStorageId());
-            values.put(COLUMN_OBJECT_HANDLE, info.getObjectHandle());
-            // TODO: Specify correct document ID.
-            values.putNull(COLUMN_PARENT_DOCUMENT_ID);
-            values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPED);
-            values.put(Document.COLUMN_MIME_TYPE, mimeType);
-            values.put(Document.COLUMN_DISPLAY_NAME, info.getName());
-            values.putNull(Document.COLUMN_SUMMARY);
-            values.put(
-                    Document.COLUMN_LAST_MODIFIED,
-                    info.getDateModified() != 0 ? info.getDateModified() : null);
-            values.putNull(Document.COLUMN_ICON);
-            values.put(Document.COLUMN_FLAGS, flag);
-            values.put(Document.COLUMN_SIZE, info.getCompressedSize());
-            if (database.insert(TABLE_DOCUMENTS, null, values) == -1) {
-                throw new Exception("Failed to add document.");
-            }
-
-            database.setTransactionSuccessful();
-        } finally {
-            database.endTransaction();
+    void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
+        final ContentValues[] valuesList = new ContentValues[documents.length];
+        for (int i = 0; i < documents.length; i++) {
+            valuesList[i] = new ContentValues();
+            getChildDocumentValues(valuesList[i], deviceId, parentId, documents[i]);
         }
+        putDocuments(valuesList, SELECTION_CHILD_DOCUMENTS, parentId);
     }
 
     /**
@@ -261,15 +203,59 @@
         }
     }
 
+    @VisibleForTesting
+    void resolveRootDocuments(int deviceId) {
+        resolveDocuments(SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId));
+    }
+
+    @VisibleForTesting
+    void resolveChildDocuments(String parentId) {
+        resolveDocuments(SELECTION_CHILD_DOCUMENTS, parentId);
+    }
+
+    /**
+     * Puts the documents into the database.
+     * If the database found another unmapped document that shares the same name and parent,
+     * the document may be merged into the unmapped document. In that case, the database marks the
+     * root as 'mapping' and wait for {@link #resolveRootDocuments(int)} is invoked.
+     * @param valuesList Values that are stored in the database.
+     * @param selection SQL where closure to select rows that shares the same parent.
+     * @param arg Argument for selection SQL.
+     */
+    private void putDocuments(ContentValues[] valuesList, String selection, String arg) {
+        database.beginTransaction();
+        try {
+            for (final ContentValues values : valuesList) {
+                final String displayName =
+                        values.getAsString(DocumentsContract.Document.COLUMN_DISPLAY_NAME);
+                final long numUnmapped = DatabaseUtils.queryNumEntries(
+                        database,
+                        TABLE_DOCUMENTS,
+                        selection + " AND " +
+                        COLUMN_ROW_STATE + " = ? AND " +
+                        DocumentsContract.Document.COLUMN_DISPLAY_NAME + " = ?",
+                        strings(arg, ROW_STATE_UNMAPPED, displayName));
+                if (numUnmapped != 0) {
+                    values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPING);
+                }
+                database.insert(TABLE_DOCUMENTS, null, values);
+            }
+
+            database.setTransactionSuccessful();
+        } finally {
+            database.endTransaction();
+        }
+    }
+
     /**
      * Maps 'unmapped' document and 'mapping' document that don't have document but shares the same
      * name.
      * If the database does not find corresponding 'mapping' document, it just removes 'unmapped'
      * document from the database.
-     * @param deviceId Device ID of roots which the method tries to resolve.
+     * @param selection Query to select rows for resolving.
+     * @param arg Argument for selection SQL.
      */
-    @VisibleForTesting
-    void resolveRootDocuments(int deviceId) {
+    private void resolveDocuments(String selection, String arg) {
         database.beginTransaction();
         try {
             // Get 1-to-1 mapping of unmapped document and mapping document.
@@ -290,8 +276,8 @@
                             "group_concat(" + unmappedIdQuery + ")",
                             "group_concat(" + mappingIdQuery + ")"
                     },
-                    SELECTION_ROOT_DOCUMENTS,
-                    strings(deviceId),
+                    selection,
+                    strings(arg),
                     DocumentsContract.Document.COLUMN_DISPLAY_NAME,
                     "count(" + unmappedIdQuery + ") = 1 AND count(" + mappingIdQuery + ") = 1",
                     null);
@@ -335,8 +321,8 @@
             // Delete all unmapped rows that cannot be mapped.
             database.delete(
                     TABLE_DOCUMENTS,
-                    SELECTION_ROOT_DOCUMENTS_WITH_STATE,
-                    strings(deviceId, ROW_STATE_UNMAPPED));
+                    COLUMN_ROW_STATE + " = ? AND " + selection,
+                    strings(ROW_STATE_UNMAPPED, arg));
 
             // The database cannot find old document ID for the mapping rows.
             // Turn the all mapping rows into mapped state, which means the rows become to be
@@ -346,8 +332,8 @@
             database.update(
                     TABLE_DOCUMENTS,
                     values,
-                    SELECTION_ROOT_DOCUMENTS_WITH_STATE,
-                    strings(deviceId, ROW_STATE_MAPPING));
+                    COLUMN_ROW_STATE + " = ? AND " + selection,
+                    strings(ROW_STATE_MAPPING, arg));
             database.setTransactionSuccessful();
         } finally {
             database.endTransaction();
@@ -379,6 +365,44 @@
     }
 
     /**
+     * Gets {@link ContentValues} for the given MTP object.
+     * @param values {@link ContentValues} that receives values.
+     * @param deviceId Device ID of the object.
+     * @param parentId Parent document ID of the object.
+     * @param info MTP object info.
+     */
+    private void getChildDocumentValues(
+            ContentValues values, int deviceId, String parentId, MtpObjectInfo info) {
+        values.clear();
+        final String mimeType = CursorHelper.formatTypeToMimeType(info.getFormat());
+        int flag = 0;
+        if (info.getProtectionStatus() == 0) {
+            flag |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
+                    DocumentsContract.Document.FLAG_SUPPORTS_WRITE;
+            if (mimeType == DocumentsContract.Document.MIME_TYPE_DIR) {
+                flag |= DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE;
+            }
+        }
+        if (info.getThumbCompressedSize() > 0) {
+            flag |= DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL;
+        }
+        values.put(COLUMN_DEVICE_ID, deviceId);
+        values.put(COLUMN_STORAGE_ID, info.getStorageId());
+        values.put(COLUMN_OBJECT_HANDLE, info.getObjectHandle());
+        values.put(COLUMN_PARENT_DOCUMENT_ID, parentId);
+        values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPED);
+        values.put(Document.COLUMN_MIME_TYPE, mimeType);
+        values.put(Document.COLUMN_DISPLAY_NAME, info.getName());
+        values.putNull(Document.COLUMN_SUMMARY);
+        values.put(
+                Document.COLUMN_LAST_MODIFIED,
+                info.getDateModified() != 0 ? info.getDateModified() : null);
+        values.putNull(Document.COLUMN_ICON);
+        values.put(Document.COLUMN_FLAGS, flag);
+        values.put(Document.COLUMN_SIZE, info.getCompressedSize());
+    }
+
+    /**
      * Converts values into string array.
      * @param args Values converted into string array.
      * @return String array.
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index e7d8481..7bc9972 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -46,7 +46,7 @@
         MtpDatabase.deleteDatabase(getContext());
     }
 
-    public void testPutRootDocument() throws Exception {
+    public void testPutRootDocuments() throws Exception {
         final MtpDatabase database = new MtpDatabase(getContext());
         database.putRootDocuments(0, resources, new MtpRoot[] {
                 new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, ""),
@@ -54,7 +54,7 @@
                 new MtpRoot(0, 3, "Device", "/@#%&<>Storage", 1000, 2000,"")
         });
 
-        final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES);
+        final Cursor cursor = database.queryRootDocuments(COLUMN_NAMES);
         assertEquals(3, cursor.getCount());
 
         cursor.moveToNext();
@@ -81,25 +81,34 @@
         cursor.close();
     }
 
-    public void testPutDocument() throws Exception {
-        final MtpDatabase database = new MtpDatabase(getContext());
+    private MtpObjectInfo createDocument(int objectHandle, String name, int format, int size) {
         final MtpObjectInfo.Builder builder = new MtpObjectInfo.Builder();
-        builder.setObjectHandle(100);
-        builder.setName("test.txt");
-        builder.setStorageId(5);
-        builder.setFormat(MtpConstants.FORMAT_TEXT);
-        builder.setCompressedSize(1000);
-        database.putDocument(0, builder.build());
+        builder.setObjectHandle(objectHandle);
+        builder.setName(name);
+        builder.setFormat(format);
+        builder.setCompressedSize(size);
+        return builder.build();
+    }
 
-        final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES);
-        assertEquals(1, cursor.getCount());
+    public void testPutChildDocuments() throws Exception {
+        final MtpDatabase database = new MtpDatabase(getContext());
+
+        database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+                createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+                createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
+                createDocument(102, "music.mp3", MtpConstants.FORMAT_MP3, 3 * 1024 * 1024)
+        });
+
+        final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES, "parentId");
+        assertEquals(3, cursor.getCount());
+
         cursor.moveToNext();
         assertEquals("documentId", 1, cursor.getInt(0));
         assertEquals("deviceId", 0, cursor.getInt(1));
-        assertEquals("storageId", 5, cursor.getInt(2));
+        assertEquals("storageId", 0, cursor.getInt(2));
         assertEquals("objectHandle", 100, cursor.getInt(3));
         assertEquals("mimeType", "text/plain", cursor.getString(4));
-        assertEquals("displayName", "test.txt", cursor.getString(5));
+        assertEquals("displayName", "note.txt", cursor.getString(5));
         assertTrue("summary", cursor.isNull(6));
         assertTrue("lastModified", cursor.isNull(7));
         assertTrue("icon", cursor.isNull(8));
@@ -108,7 +117,43 @@
                 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
                 DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
                 cursor.getInt(9));
-        assertEquals("size", 1000, cursor.getInt(10));
+        assertEquals("size", 1024, cursor.getInt(10));
+
+        cursor.moveToNext();
+        assertEquals("documentId", 2, cursor.getInt(0));
+        assertEquals("deviceId", 0, cursor.getInt(1));
+        assertEquals("storageId", 0, cursor.getInt(2));
+        assertEquals("objectHandle", 101, cursor.getInt(3));
+        assertEquals("mimeType", "image/jpeg", cursor.getString(4));
+        assertEquals("displayName", "image.jpg", cursor.getString(5));
+        assertTrue("summary", cursor.isNull(6));
+        assertTrue("lastModified", cursor.isNull(7));
+        assertTrue("icon", cursor.isNull(8));
+        assertEquals(
+                "flag",
+                DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
+                DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
+                cursor.getInt(9));
+        assertEquals("size", 2 * 1024 * 1024, cursor.getInt(10));
+
+        cursor.moveToNext();
+        assertEquals("documentId", 3, cursor.getInt(0));
+        assertEquals("deviceId", 0, cursor.getInt(1));
+        assertEquals("storageId", 0, cursor.getInt(2));
+        assertEquals("objectHandle", 102, cursor.getInt(3));
+        assertEquals("mimeType", "audio/mpeg", cursor.getString(4));
+        assertEquals("displayName", "music.mp3", cursor.getString(5));
+        assertTrue("summary", cursor.isNull(6));
+        assertTrue("lastModified", cursor.isNull(7));
+        assertTrue("icon", cursor.isNull(8));
+        assertEquals(
+                "flag",
+                DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
+                DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
+                cursor.getInt(9));
+        assertEquals("size", 3 * 1024 * 1024, cursor.getInt(10));
+
+        cursor.close();
     }
 
     public void testRestoreIdForRootDocuments() throws Exception {
@@ -124,7 +169,7 @@
         });
 
         {
-            final Cursor cursor = database.queryChildDocuments(columns);
+            final Cursor cursor = database.queryRootDocuments(columns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
             assertEquals("documentId", 1, cursor.getInt(0));
@@ -140,7 +185,7 @@
         database.clearMapping();
 
         {
-            final Cursor cursor = database.queryChildDocuments(columns);
+            final Cursor cursor = database.queryRootDocuments(columns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
             assertEquals("documentId", 1, cursor.getInt(0));
@@ -159,7 +204,7 @@
         });
 
         {
-            final Cursor cursor = database.queryChildDocuments(columns);
+            final Cursor cursor = database.queryRootDocuments(columns);
             assertEquals(3, cursor.getCount());
             cursor.moveToNext();
             assertEquals("documentId", 1, cursor.getInt(0));
@@ -179,7 +224,7 @@
         database.resolveRootDocuments(0);
 
         {
-            final Cursor cursor = database.queryChildDocuments(columns);
+            final Cursor cursor = database.queryRootDocuments(columns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
             assertEquals("documentId", 1, cursor.getInt(0));
@@ -193,6 +238,78 @@
         }
     }
 
+    public void testRestoreIdForChildDocuments() throws Exception {
+        final MtpDatabase database = new MtpDatabase(getContext());
+        final String[] columns = new String[] {
+                DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+                MtpDatabase.COLUMN_OBJECT_HANDLE,
+                DocumentsContract.Document.COLUMN_DISPLAY_NAME
+        };
+        database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+                createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+                createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
+                createDocument(102, "music.mp3", MtpConstants.FORMAT_MP3, 3 * 1024 * 1024)
+        });
+        database.clearMapping();
+
+        {
+            final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+            assertEquals(3, cursor.getCount());
+
+            cursor.moveToNext();
+            assertEquals("documentId", 1, cursor.getInt(0));
+            assertTrue("objectHandle", cursor.isNull(1));
+            assertEquals("name", "note.txt", cursor.getString(2));
+
+            cursor.moveToNext();
+            assertEquals("documentId", 2, cursor.getInt(0));
+            assertTrue("objectHandle", cursor.isNull(1));
+            assertEquals("name", "image.jpg", cursor.getString(2));
+
+            cursor.moveToNext();
+            assertEquals("documentId", 3, cursor.getInt(0));
+            assertTrue("objectHandle", cursor.isNull(1));
+            assertEquals("name", "music.mp3", cursor.getString(2));
+
+            cursor.close();
+        }
+
+        database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+                createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+                createDocument(203, "video.mp4", MtpConstants.FORMAT_MP4_CONTAINER, 1024),
+        });
+
+        {
+            final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+            assertEquals(4, cursor.getCount());
+
+            cursor.moveToPosition(3);
+            assertEquals("documentId", 5, cursor.getInt(0));
+            assertEquals("objectHandle", 203, cursor.getInt(1));
+            assertEquals("name", "video.mp4", cursor.getString(2));
+
+            cursor.close();
+        }
+
+        database.resolveChildDocuments("parentId");
+
+        {
+            final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+            assertEquals(2, cursor.getCount());
+
+            cursor.moveToNext();
+            assertEquals("documentId", 1, cursor.getInt(0));
+            assertEquals("objectHandle", 200, cursor.getInt(1));
+            assertEquals("name", "note.txt", cursor.getString(2));
+
+            cursor.moveToNext();
+            assertEquals("documentId", 5, cursor.getInt(0));
+            assertEquals("objectHandle", 203, cursor.getInt(1));
+            assertEquals("name", "video.mp4", cursor.getString(2));
+            cursor.close();
+        }
+    }
+
     public void testRestoreIdForDifferentDevices() throws Exception {
         final MtpDatabase database = new MtpDatabase(getContext());
         final String[] columns = new String[] {
@@ -208,7 +325,7 @@
         });
 
         {
-            final Cursor cursor = database.queryChildDocuments(columns);
+            final Cursor cursor = database.queryRootDocuments(columns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
             assertEquals("documentId", 1, cursor.getInt(0));
@@ -231,8 +348,9 @@
         });
         database.resolveRootDocuments(0);
         database.resolveRootDocuments(1);
+
         {
-            final Cursor cursor = database.queryChildDocuments(columns);
+            final Cursor cursor = database.queryRootDocuments(columns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
             assertEquals("documentId", 1, cursor.getInt(0));
@@ -246,6 +364,45 @@
         }
     }
 
+    public void testRestoreIdForDifferentParents() throws Exception {
+        final MtpDatabase database = new MtpDatabase(getContext());
+        final String[] columns = new String[] {
+                DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+                MtpDatabase.COLUMN_OBJECT_HANDLE
+        };
+        database.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
+                createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+        });
+        database.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
+                createDocument(101, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+        });
+        database.clearMapping();
+        database.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
+                createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+        });
+        database.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
+                createDocument(201, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+        });
+        database.resolveChildDocuments("parentId1");
+
+        {
+            final Cursor cursor = database.queryChildDocuments(columns, "parentId1");
+            assertEquals(1, cursor.getCount());
+            cursor.moveToNext();
+            assertEquals("documentId", 1, cursor.getInt(0));
+            assertEquals("objectHandle", 200, cursor.getInt(1));
+            cursor.close();
+        }
+        {
+            final Cursor cursor = database.queryChildDocuments(columns, "parentId2");
+            assertEquals(1, cursor.getCount());
+            cursor.moveToNext();
+            assertEquals("documentId", 2, cursor.getInt(0));
+            assertTrue("objectHandle", cursor.isNull(1));
+            cursor.close();
+        }
+    }
+
     public void testClearMtpIdentifierBeforeResolveRootDocuments() {
         final MtpDatabase database = new MtpDatabase(getContext());
         final String[] columns = new String[] {
@@ -266,7 +423,7 @@
         });
         database.resolveRootDocuments(0);
         {
-            final Cursor cursor = database.queryChildDocuments(columns);
+            final Cursor cursor = database.queryRootDocuments(columns);
             assertEquals(1, cursor.getCount());
             cursor.moveToNext();
             assertEquals("documentId", 1, cursor.getInt(0));
@@ -293,7 +450,7 @@
         });
         database.resolveRootDocuments(0);
         {
-            final Cursor cursor = database.queryChildDocuments(columns);
+            final Cursor cursor = database.queryRootDocuments(columns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
             assertEquals("documentId", 2, cursor.getInt(0));