Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +106 −82 Original line number Original line Diff line number Diff line Loading @@ -84,8 +84,7 @@ class MtpDatabase { DocumentsContract.Document.COLUMN_DOCUMENT_ID + " = ?"; DocumentsContract.Document.COLUMN_DOCUMENT_ID + " = ?"; private static final String SELECTION_ROOT_DOCUMENTS = private static final String SELECTION_ROOT_DOCUMENTS = COLUMN_DEVICE_ID + " = ? AND " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; COLUMN_DEVICE_ID + " = ? AND " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; private static final String SELECTION_ROOT_DOCUMENTS_WITH_STATE = private static final String SELECTION_CHILD_DOCUMENTS = COLUMN_PARENT_DOCUMENT_ID + " = ?"; SELECTION_ROOT_DOCUMENTS + " AND " + COLUMN_ROW_STATE + " = ?"; static class ParentNotFoundException extends Exception {} static class ParentNotFoundException extends Exception {} Loading Loading @@ -136,7 +135,7 @@ class MtpDatabase { } } @VisibleForTesting @VisibleForTesting Cursor queryChildDocuments(String[] columnNames) { Cursor queryRootDocuments(String[] columnNames) { return database.query( return database.query( TABLE_DOCUMENTS, TABLE_DOCUMENTS, columnNames, columnNames, Loading @@ -159,84 +158,27 @@ class MtpDatabase { null); 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 @VisibleForTesting void putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) { void putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) { database.beginTransaction(); final ContentValues[] valuesList = new ContentValues[roots.length]; try { // Add roots to database. final ContentValues values = new ContentValues(); for (int i = 0; i < roots.length; i++) { for (int i = 0; i < roots.length; i++) { getRootDocumentValues(values, resources, roots[i]); if (roots[i].mDeviceId != deviceId) { final String displayName = throw new IllegalArgumentException(); 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); valuesList[i] = new ContentValues(); } getRootDocumentValues(valuesList[i], resources, roots[i]); database.setTransactionSuccessful(); } finally { database.endTransaction(); } } putDocuments(valuesList, SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId)); } } @VisibleForTesting @VisibleForTesting void putDocument(int deviceId, MtpObjectInfo info) throws Exception { void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) { database.beginTransaction(); final ContentValues[] valuesList = new ContentValues[documents.length]; try { for (int i = 0; i < documents.length; i++) { final String mimeType = CursorHelper.formatTypeToMimeType(info.getFormat()); valuesList[i] = new ContentValues(); getChildDocumentValues(valuesList[i], deviceId, parentId, documents[i]); 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(); } } putDocuments(valuesList, SELECTION_CHILD_DOCUMENTS, parentId); } } /** /** Loading @@ -261,15 +203,59 @@ class MtpDatabase { } } } } @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 * Maps 'unmapped' document and 'mapping' document that don't have document but shares the same * name. * name. * If the database does not find corresponding 'mapping' document, it just removes 'unmapped' * If the database does not find corresponding 'mapping' document, it just removes 'unmapped' * document from the database. * 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 private void resolveDocuments(String selection, String arg) { void resolveRootDocuments(int deviceId) { database.beginTransaction(); database.beginTransaction(); try { try { // Get 1-to-1 mapping of unmapped document and mapping document. // Get 1-to-1 mapping of unmapped document and mapping document. Loading @@ -290,8 +276,8 @@ class MtpDatabase { "group_concat(" + unmappedIdQuery + ")", "group_concat(" + unmappedIdQuery + ")", "group_concat(" + mappingIdQuery + ")" "group_concat(" + mappingIdQuery + ")" }, }, SELECTION_ROOT_DOCUMENTS, selection, strings(deviceId), strings(arg), DocumentsContract.Document.COLUMN_DISPLAY_NAME, DocumentsContract.Document.COLUMN_DISPLAY_NAME, "count(" + unmappedIdQuery + ") = 1 AND count(" + mappingIdQuery + ") = 1", "count(" + unmappedIdQuery + ") = 1 AND count(" + mappingIdQuery + ") = 1", null); null); Loading Loading @@ -335,8 +321,8 @@ class MtpDatabase { // Delete all unmapped rows that cannot be mapped. // Delete all unmapped rows that cannot be mapped. database.delete( database.delete( TABLE_DOCUMENTS, TABLE_DOCUMENTS, SELECTION_ROOT_DOCUMENTS_WITH_STATE, COLUMN_ROW_STATE + " = ? AND " + selection, strings(deviceId, ROW_STATE_UNMAPPED)); strings(ROW_STATE_UNMAPPED, arg)); // The database cannot find old document ID for the mapping rows. // 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 // Turn the all mapping rows into mapped state, which means the rows become to be Loading @@ -346,8 +332,8 @@ class MtpDatabase { database.update( database.update( TABLE_DOCUMENTS, TABLE_DOCUMENTS, values, values, SELECTION_ROOT_DOCUMENTS_WITH_STATE, COLUMN_ROW_STATE + " = ? AND " + selection, strings(deviceId, ROW_STATE_MAPPING)); strings(ROW_STATE_MAPPING, arg)); database.setTransactionSuccessful(); database.setTransactionSuccessful(); } finally { } finally { database.endTransaction(); database.endTransaction(); Loading Loading @@ -378,6 +364,44 @@ class MtpDatabase { (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE)); (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE)); } } /** * 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. * Converts values into string array. * @param args Values converted into string array. * @param args Values converted into string array. Loading packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java +181 −24 Original line number Original line Diff line number Diff line Loading @@ -46,7 +46,7 @@ public class MtpDatabaseTest extends AndroidTestCase { MtpDatabase.deleteDatabase(getContext()); MtpDatabase.deleteDatabase(getContext()); } } public void testPutRootDocument() throws Exception { public void testPutRootDocuments() throws Exception { final MtpDatabase database = new MtpDatabase(getContext()); final MtpDatabase database = new MtpDatabase(getContext()); database.putRootDocuments(0, resources, new MtpRoot[] { database.putRootDocuments(0, resources, new MtpRoot[] { new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, ""), new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, ""), Loading @@ -54,7 +54,7 @@ public class MtpDatabaseTest extends AndroidTestCase { new MtpRoot(0, 3, "Device", "/@#%&<>Storage", 1000, 2000,"") 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()); assertEquals(3, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); Loading @@ -81,25 +81,34 @@ public class MtpDatabaseTest extends AndroidTestCase { cursor.close(); cursor.close(); } } public void testPutDocument() throws Exception { private MtpObjectInfo createDocument(int objectHandle, String name, int format, int size) { final MtpDatabase database = new MtpDatabase(getContext()); final MtpObjectInfo.Builder builder = new MtpObjectInfo.Builder(); final MtpObjectInfo.Builder builder = new MtpObjectInfo.Builder(); builder.setObjectHandle(100); builder.setObjectHandle(objectHandle); builder.setName("test.txt"); builder.setName(name); builder.setStorageId(5); builder.setFormat(format); builder.setFormat(MtpConstants.FORMAT_TEXT); builder.setCompressedSize(size); builder.setCompressedSize(1000); return builder.build(); database.putDocument(0, builder.build()); } final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES); public void testPutChildDocuments() throws Exception { assertEquals(1, cursor.getCount()); 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(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("deviceId", 0, cursor.getInt(1)); 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("objectHandle", 100, cursor.getInt(3)); assertEquals("mimeType", "text/plain", cursor.getString(4)); 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("summary", cursor.isNull(6)); assertTrue("lastModified", cursor.isNull(7)); assertTrue("lastModified", cursor.isNull(7)); assertTrue("icon", cursor.isNull(8)); assertTrue("icon", cursor.isNull(8)); Loading @@ -108,7 +117,43 @@ public class MtpDatabaseTest extends AndroidTestCase { DocumentsContract.Document.FLAG_SUPPORTS_DELETE | DocumentsContract.Document.FLAG_SUPPORTS_DELETE | DocumentsContract.Document.FLAG_SUPPORTS_WRITE, DocumentsContract.Document.FLAG_SUPPORTS_WRITE, cursor.getInt(9)); 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 { public void testRestoreIdForRootDocuments() throws Exception { Loading @@ -124,7 +169,7 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -140,7 +185,7 @@ public class MtpDatabaseTest extends AndroidTestCase { database.clearMapping(); database.clearMapping(); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -159,7 +204,7 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(3, cursor.getCount()); assertEquals(3, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -179,7 +224,7 @@ public class MtpDatabaseTest extends AndroidTestCase { database.resolveRootDocuments(0); database.resolveRootDocuments(0); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -193,6 +238,78 @@ public class MtpDatabaseTest extends AndroidTestCase { } } } } 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 { public void testRestoreIdForDifferentDevices() throws Exception { final MtpDatabase database = new MtpDatabase(getContext()); final MtpDatabase database = new MtpDatabase(getContext()); final String[] columns = new String[] { final String[] columns = new String[] { Loading @@ -208,7 +325,7 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -231,8 +348,9 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); database.resolveRootDocuments(0); database.resolveRootDocuments(0); database.resolveRootDocuments(1); database.resolveRootDocuments(1); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -246,6 +364,45 @@ public class MtpDatabaseTest extends AndroidTestCase { } } } } 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() { public void testClearMtpIdentifierBeforeResolveRootDocuments() { final MtpDatabase database = new MtpDatabase(getContext()); final MtpDatabase database = new MtpDatabase(getContext()); final String[] columns = new String[] { final String[] columns = new String[] { Loading @@ -266,7 +423,7 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); database.resolveRootDocuments(0); database.resolveRootDocuments(0); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(1, cursor.getCount()); assertEquals(1, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -293,7 +450,7 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); database.resolveRootDocuments(0); database.resolveRootDocuments(0); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 2, cursor.getInt(0)); assertEquals("documentId", 2, cursor.getInt(0)); Loading Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +106 −82 Original line number Original line Diff line number Diff line Loading @@ -84,8 +84,7 @@ class MtpDatabase { DocumentsContract.Document.COLUMN_DOCUMENT_ID + " = ?"; DocumentsContract.Document.COLUMN_DOCUMENT_ID + " = ?"; private static final String SELECTION_ROOT_DOCUMENTS = private static final String SELECTION_ROOT_DOCUMENTS = COLUMN_DEVICE_ID + " = ? AND " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; COLUMN_DEVICE_ID + " = ? AND " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; private static final String SELECTION_ROOT_DOCUMENTS_WITH_STATE = private static final String SELECTION_CHILD_DOCUMENTS = COLUMN_PARENT_DOCUMENT_ID + " = ?"; SELECTION_ROOT_DOCUMENTS + " AND " + COLUMN_ROW_STATE + " = ?"; static class ParentNotFoundException extends Exception {} static class ParentNotFoundException extends Exception {} Loading Loading @@ -136,7 +135,7 @@ class MtpDatabase { } } @VisibleForTesting @VisibleForTesting Cursor queryChildDocuments(String[] columnNames) { Cursor queryRootDocuments(String[] columnNames) { return database.query( return database.query( TABLE_DOCUMENTS, TABLE_DOCUMENTS, columnNames, columnNames, Loading @@ -159,84 +158,27 @@ class MtpDatabase { null); 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 @VisibleForTesting void putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) { void putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) { database.beginTransaction(); final ContentValues[] valuesList = new ContentValues[roots.length]; try { // Add roots to database. final ContentValues values = new ContentValues(); for (int i = 0; i < roots.length; i++) { for (int i = 0; i < roots.length; i++) { getRootDocumentValues(values, resources, roots[i]); if (roots[i].mDeviceId != deviceId) { final String displayName = throw new IllegalArgumentException(); 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); valuesList[i] = new ContentValues(); } getRootDocumentValues(valuesList[i], resources, roots[i]); database.setTransactionSuccessful(); } finally { database.endTransaction(); } } putDocuments(valuesList, SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId)); } } @VisibleForTesting @VisibleForTesting void putDocument(int deviceId, MtpObjectInfo info) throws Exception { void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) { database.beginTransaction(); final ContentValues[] valuesList = new ContentValues[documents.length]; try { for (int i = 0; i < documents.length; i++) { final String mimeType = CursorHelper.formatTypeToMimeType(info.getFormat()); valuesList[i] = new ContentValues(); getChildDocumentValues(valuesList[i], deviceId, parentId, documents[i]); 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(); } } putDocuments(valuesList, SELECTION_CHILD_DOCUMENTS, parentId); } } /** /** Loading @@ -261,15 +203,59 @@ class MtpDatabase { } } } } @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 * Maps 'unmapped' document and 'mapping' document that don't have document but shares the same * name. * name. * If the database does not find corresponding 'mapping' document, it just removes 'unmapped' * If the database does not find corresponding 'mapping' document, it just removes 'unmapped' * document from the database. * 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 private void resolveDocuments(String selection, String arg) { void resolveRootDocuments(int deviceId) { database.beginTransaction(); database.beginTransaction(); try { try { // Get 1-to-1 mapping of unmapped document and mapping document. // Get 1-to-1 mapping of unmapped document and mapping document. Loading @@ -290,8 +276,8 @@ class MtpDatabase { "group_concat(" + unmappedIdQuery + ")", "group_concat(" + unmappedIdQuery + ")", "group_concat(" + mappingIdQuery + ")" "group_concat(" + mappingIdQuery + ")" }, }, SELECTION_ROOT_DOCUMENTS, selection, strings(deviceId), strings(arg), DocumentsContract.Document.COLUMN_DISPLAY_NAME, DocumentsContract.Document.COLUMN_DISPLAY_NAME, "count(" + unmappedIdQuery + ") = 1 AND count(" + mappingIdQuery + ") = 1", "count(" + unmappedIdQuery + ") = 1 AND count(" + mappingIdQuery + ") = 1", null); null); Loading Loading @@ -335,8 +321,8 @@ class MtpDatabase { // Delete all unmapped rows that cannot be mapped. // Delete all unmapped rows that cannot be mapped. database.delete( database.delete( TABLE_DOCUMENTS, TABLE_DOCUMENTS, SELECTION_ROOT_DOCUMENTS_WITH_STATE, COLUMN_ROW_STATE + " = ? AND " + selection, strings(deviceId, ROW_STATE_UNMAPPED)); strings(ROW_STATE_UNMAPPED, arg)); // The database cannot find old document ID for the mapping rows. // 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 // Turn the all mapping rows into mapped state, which means the rows become to be Loading @@ -346,8 +332,8 @@ class MtpDatabase { database.update( database.update( TABLE_DOCUMENTS, TABLE_DOCUMENTS, values, values, SELECTION_ROOT_DOCUMENTS_WITH_STATE, COLUMN_ROW_STATE + " = ? AND " + selection, strings(deviceId, ROW_STATE_MAPPING)); strings(ROW_STATE_MAPPING, arg)); database.setTransactionSuccessful(); database.setTransactionSuccessful(); } finally { } finally { database.endTransaction(); database.endTransaction(); Loading Loading @@ -378,6 +364,44 @@ class MtpDatabase { (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE)); (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE)); } } /** * 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. * Converts values into string array. * @param args Values converted into string array. * @param args Values converted into string array. Loading
packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java +181 −24 Original line number Original line Diff line number Diff line Loading @@ -46,7 +46,7 @@ public class MtpDatabaseTest extends AndroidTestCase { MtpDatabase.deleteDatabase(getContext()); MtpDatabase.deleteDatabase(getContext()); } } public void testPutRootDocument() throws Exception { public void testPutRootDocuments() throws Exception { final MtpDatabase database = new MtpDatabase(getContext()); final MtpDatabase database = new MtpDatabase(getContext()); database.putRootDocuments(0, resources, new MtpRoot[] { database.putRootDocuments(0, resources, new MtpRoot[] { new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, ""), new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, ""), Loading @@ -54,7 +54,7 @@ public class MtpDatabaseTest extends AndroidTestCase { new MtpRoot(0, 3, "Device", "/@#%&<>Storage", 1000, 2000,"") 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()); assertEquals(3, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); Loading @@ -81,25 +81,34 @@ public class MtpDatabaseTest extends AndroidTestCase { cursor.close(); cursor.close(); } } public void testPutDocument() throws Exception { private MtpObjectInfo createDocument(int objectHandle, String name, int format, int size) { final MtpDatabase database = new MtpDatabase(getContext()); final MtpObjectInfo.Builder builder = new MtpObjectInfo.Builder(); final MtpObjectInfo.Builder builder = new MtpObjectInfo.Builder(); builder.setObjectHandle(100); builder.setObjectHandle(objectHandle); builder.setName("test.txt"); builder.setName(name); builder.setStorageId(5); builder.setFormat(format); builder.setFormat(MtpConstants.FORMAT_TEXT); builder.setCompressedSize(size); builder.setCompressedSize(1000); return builder.build(); database.putDocument(0, builder.build()); } final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES); public void testPutChildDocuments() throws Exception { assertEquals(1, cursor.getCount()); 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(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("deviceId", 0, cursor.getInt(1)); 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("objectHandle", 100, cursor.getInt(3)); assertEquals("mimeType", "text/plain", cursor.getString(4)); 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("summary", cursor.isNull(6)); assertTrue("lastModified", cursor.isNull(7)); assertTrue("lastModified", cursor.isNull(7)); assertTrue("icon", cursor.isNull(8)); assertTrue("icon", cursor.isNull(8)); Loading @@ -108,7 +117,43 @@ public class MtpDatabaseTest extends AndroidTestCase { DocumentsContract.Document.FLAG_SUPPORTS_DELETE | DocumentsContract.Document.FLAG_SUPPORTS_DELETE | DocumentsContract.Document.FLAG_SUPPORTS_WRITE, DocumentsContract.Document.FLAG_SUPPORTS_WRITE, cursor.getInt(9)); 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 { public void testRestoreIdForRootDocuments() throws Exception { Loading @@ -124,7 +169,7 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -140,7 +185,7 @@ public class MtpDatabaseTest extends AndroidTestCase { database.clearMapping(); database.clearMapping(); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -159,7 +204,7 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(3, cursor.getCount()); assertEquals(3, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -179,7 +224,7 @@ public class MtpDatabaseTest extends AndroidTestCase { database.resolveRootDocuments(0); database.resolveRootDocuments(0); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -193,6 +238,78 @@ public class MtpDatabaseTest extends AndroidTestCase { } } } } 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 { public void testRestoreIdForDifferentDevices() throws Exception { final MtpDatabase database = new MtpDatabase(getContext()); final MtpDatabase database = new MtpDatabase(getContext()); final String[] columns = new String[] { final String[] columns = new String[] { Loading @@ -208,7 +325,7 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -231,8 +348,9 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); database.resolveRootDocuments(0); database.resolveRootDocuments(0); database.resolveRootDocuments(1); database.resolveRootDocuments(1); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -246,6 +364,45 @@ public class MtpDatabaseTest extends AndroidTestCase { } } } } 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() { public void testClearMtpIdentifierBeforeResolveRootDocuments() { final MtpDatabase database = new MtpDatabase(getContext()); final MtpDatabase database = new MtpDatabase(getContext()); final String[] columns = new String[] { final String[] columns = new String[] { Loading @@ -266,7 +423,7 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); database.resolveRootDocuments(0); database.resolveRootDocuments(0); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(1, cursor.getCount()); assertEquals(1, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 1, cursor.getInt(0)); assertEquals("documentId", 1, cursor.getInt(0)); Loading @@ -293,7 +450,7 @@ public class MtpDatabaseTest extends AndroidTestCase { }); }); database.resolveRootDocuments(0); database.resolveRootDocuments(0); { { final Cursor cursor = database.queryChildDocuments(columns); final Cursor cursor = database.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); cursor.moveToNext(); cursor.moveToNext(); assertEquals("documentId", 2, cursor.getInt(0)); assertEquals("documentId", 2, cursor.getInt(0)); Loading