Loading packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java +46 −21 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package com.android.mtp; import static com.android.mtp.MtpDatabaseConstants.*; import android.annotation.Nullable; import android.content.ContentValues; import android.database.Cursor; Loading @@ -26,6 +24,7 @@ import android.database.sqlite.SQLiteDatabase; import android.mtp.MtpObjectInfo; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.util.Log; import com.android.internal.util.Preconditions; Loading @@ -33,6 +32,7 @@ import java.io.FileNotFoundException; import java.util.HashMap; import java.util.Map; import static com.android.mtp.MtpDatabaseConstants.*; import static com.android.mtp.MtpDatabase.strings; /** Loading Loading @@ -75,7 +75,7 @@ class Mapper { extraValuesList, COLUMN_PARENT_DOCUMENT_ID + " IS NULL", EMPTY_ARGS, COLUMN_DEVICE_ID); Document.COLUMN_DISPLAY_NAME); database.setTransactionSuccessful(); return changed; } finally { Loading Loading @@ -172,13 +172,16 @@ class Mapper { final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); database.beginTransaction(); try { final ContentValues values = new ContentValues(); values.putNull(COLUMN_OBJECT_HANDLE); values.putNull(COLUMN_STORAGE_ID); values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED); database.update(TABLE_DOCUMENTS, values, null, null); database.setTransactionSuccessful(); mMappingMode.clear(); // Disconnect all device rows. try { startAddingDocuments(null); stopAddingDocuments(null); } catch (FileNotFoundException exception) { Log.e(MtpDocumentsProvider.TAG, "Unexpected FileNotFoundException.", exception); throw new RuntimeException(exception); } database.setTransactionSuccessful(); } finally { database.endTransaction(); } Loading Loading @@ -210,16 +213,20 @@ class Mapper { getParentOrHaltMapping(parentDocumentId); Preconditions.checkState(!mMappingMode.containsKey(parentDocumentId)); // Set all documents as invalidated. // Set all valid documents as invalidated. final ContentValues values = new ContentValues(); values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED); database.update(TABLE_DOCUMENTS, values, selection, args); database.update( TABLE_DOCUMENTS, values, selection + " AND " + COLUMN_ROW_STATE + " = ?", DatabaseUtils.appendSelectionArgs(args, strings(ROW_STATE_VALID))); // If we have rows that does not have MTP identifier, do heuristic mapping by name. final boolean useNameForResolving = DatabaseUtils.queryNumEntries( database, TABLE_DOCUMENTS, selection + " AND " + COLUMN_STORAGE_ID + " IS NULL", selection + " AND " + COLUMN_DEVICE_ID + " IS NULL", args) > 0; database.setTransactionSuccessful(); mMappingMode.put( Loading Loading @@ -270,11 +277,13 @@ class Mapper { TABLE_DOCUMENTS, strings(Document.COLUMN_DOCUMENT_ID), selection + " AND " + COLUMN_ROW_STATE + "=? AND " + COLUMN_ROW_STATE + " IN (?, ?) AND " + mappingKey + "=?", DatabaseUtils.appendSelectionArgs( args, strings(ROW_STATE_INVALIDATED, values.getAsString(mappingKey))), strings(ROW_STATE_INVALIDATED, ROW_STATE_DISCONNECTED, values.getAsString(mappingKey))), null, null, null, Loading Loading @@ -335,17 +344,28 @@ class Mapper { final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); database.beginTransaction(); try { getParentOrHaltMapping(parentId); final Identifier parentIdentifier = getParentOrHaltMapping(parentId); Preconditions.checkState(mMappingMode.containsKey(parentId)); mMappingMode.remove(parentId); boolean changed = false; // Delete all invalidated rows that cannot be mapped. // Delete/disconnect all invalidated rows that cannot be mapped. final boolean keepUnmatchedDocument = parentIdentifier == null || parentIdentifier.mDocumentType == DOCUMENT_TYPE_DEVICE; if (keepUnmatchedDocument) { if (mDatabase.disconnectDocumentsRecursively( COLUMN_ROW_STATE + " = ? AND " + selection, DatabaseUtils.appendSelectionArgs(strings(ROW_STATE_INVALIDATED), args))) { changed = true; } } else { if (mDatabase.deleteDocumentsAndRootsRecursively( COLUMN_ROW_STATE + " = ? AND " + selection, DatabaseUtils.appendSelectionArgs(strings(ROW_STATE_INVALIDATED), args))) { changed = true; } } database.setTransactionSuccessful(); return changed; Loading @@ -368,7 +388,12 @@ class Mapper { return null; } try { return mDatabase.createIdentifier(parentId); final Identifier identifier = mDatabase.createIdentifier(parentId); if (mDatabase.getRowState(parentId) == ROW_STATE_DISCONNECTED) { throw new FileNotFoundException( "document: " + parentId + " is in disconnected device."); } return identifier; } catch (FileNotFoundException error) { mMappingMode.remove(parentId); throw error; Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +79 −13 Original line number Diff line number Diff line Loading @@ -294,15 +294,6 @@ class MtpDatabase { "1"); } /** * Remove all rows belong to a device. * @param deviceId Device ID. */ void removeDeviceRows(int deviceId) { // Call non-recursive version because it anyway deletes all rows in the devices. deleteDocumentsAndRoots(COLUMN_DEVICE_ID + "=?", strings(deviceId)); } @Nullable String getDocumentIdForDevice(int deviceId) { final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, Loading Loading @@ -456,7 +447,43 @@ class MtpDatabase { } } private boolean deleteDocumentsAndRoots(String selection, String[] args) { /** * Marks the documents and their child as disconnected documents. * @param selection * @param args * @return True if at least one row is updated. */ boolean disconnectDocumentsRecursively(String selection, String[] args) { mDatabase.beginTransaction(); try { boolean changed = false; try (final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(Document.COLUMN_DOCUMENT_ID), selection, args, null, null, null)) { while (cursor.moveToNext()) { if (disconnectDocumentsRecursively( COLUMN_PARENT_DOCUMENT_ID + " = ?", strings(cursor.getString(0)))) { changed = true; } } } if (disconnectDocuments(selection, args)) { changed = true; } mDatabase.setTransactionSuccessful(); return changed; } finally { mDatabase.endTransaction(); } } boolean deleteDocumentsAndRoots(String selection, String[] args) { mDatabase.beginTransaction(); try { int deleted = 0; Loading @@ -481,6 +508,39 @@ class MtpDatabase { } } boolean disconnectDocuments(String selection, String[] args) { mDatabase.beginTransaction(); try { final ContentValues values = new ContentValues(); values.put(COLUMN_ROW_STATE, ROW_STATE_DISCONNECTED); values.putNull(COLUMN_DEVICE_ID); values.putNull(COLUMN_STORAGE_ID); values.putNull(COLUMN_OBJECT_HANDLE); final boolean updated = mDatabase.update(TABLE_DOCUMENTS, values, selection, args) != 0; mDatabase.setTransactionSuccessful(); return updated; } finally { mDatabase.endTransaction(); } } int getRowState(String documentId) throws FileNotFoundException { try (final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(COLUMN_ROW_STATE), SELECTION_DOCUMENT_ID, strings(documentId), null, null, null)) { if (cursor.getCount() == 0) { throw new FileNotFoundException(); } cursor.moveToNext(); return cursor.getInt(0); } } private static class OpenHelper extends SQLiteOpenHelper { public OpenHelper(Context context, int flags) { super(context, Loading @@ -497,6 +557,12 @@ class MtpDatabase { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion == 1) { db.execSQL("DROP TABLE " + TABLE_DOCUMENTS); db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA); onCreate(db); return; } throw new UnsupportedOperationException(); } } Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java +8 −2 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ import java.util.Map; * Class containing MtpDatabase constants. */ class MtpDatabaseConstants { static final int DATABASE_VERSION = 1; static final int DATABASE_VERSION = 2; static final String DATABASE_NAME = "database"; static final int FLAG_DATABASE_IN_MEMORY = 1; Loading Loading @@ -77,6 +77,12 @@ class MtpDatabaseConstants { */ static final int ROW_STATE_INVALIDATED = 1; /** * The documents are of device/storage that are disconnected now. The documents are invisible * but their document ID will be reuse when the device/storage is connected again. */ static final int ROW_STATE_DISCONNECTED = 2; /** * Mapping mode that uses MTP identifier to find corresponding rows. */ Loading Loading @@ -113,7 +119,7 @@ class MtpDatabaseConstants { "CREATE TABLE " + TABLE_DOCUMENTS + " (" + Document.COLUMN_DOCUMENT_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + COLUMN_DEVICE_ID + " INTEGER NOT NULL," + COLUMN_DEVICE_ID + " INTEGER," + COLUMN_STORAGE_ID + " INTEGER," + COLUMN_OBJECT_HANDLE + " INTEGER," + COLUMN_PARENT_DOCUMENT_ID + " INTEGER," + Loading packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java +14 −59 Original line number Diff line number Diff line Loading @@ -95,7 +95,8 @@ public class MtpDatabaseTest extends AndroidTestCase { assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID)); assertEquals(1, getInt(cursor, COLUMN_STORAGE_ID)); assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE)); assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE)); assertEquals( DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE)); assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME)); assertTrue(isNull(cursor, COLUMN_SUMMARY)); assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED)); Loading @@ -103,7 +104,8 @@ public class MtpDatabaseTest extends AndroidTestCase { assertEquals(0, getInt(cursor, COLUMN_FLAGS)); assertEquals(1000, getInt(cursor, COLUMN_SIZE)); assertEquals( MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, getInt(cursor, COLUMN_DOCUMENT_TYPE)); MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, getInt(cursor, COLUMN_DOCUMENT_TYPE)); cursor.close(); } Loading Loading @@ -296,15 +298,7 @@ public class MtpDatabaseTest extends AndroidTestCase { { final Cursor cursor = mDatabase.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); cursor.moveToNext(); assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_STORAGE_ID)); assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.moveToNext(); assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_STORAGE_ID)); assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME)); assertEquals(0, cursor.getCount()); cursor.close(); } Loading @@ -314,28 +308,10 @@ public class MtpDatabaseTest extends AndroidTestCase { new MtpRoot(0, 200, "Storage A", 2000, 0, ""), new MtpRoot(0, 202, "Storage C", 2002, 0, "") }); { final Cursor cursor = mDatabase.queryRootDocuments(columns); assertEquals(3, cursor.getCount()); cursor.moveToNext(); assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID)); assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID)); assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.moveToNext(); assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_STORAGE_ID)); assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.moveToNext(); assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID)); assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID)); assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.close(); } mDatabase.getMapper().stopAddingDocuments("1"); { // After compeleting mapping, Storage A can be obtained with new storage ID. final Cursor cursor = mDatabase.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); cursor.moveToNext(); Loading Loading @@ -372,24 +348,9 @@ public class MtpDatabaseTest extends AndroidTestCase { addTestStorage("1"); { // Don't return objects that lost MTP object handles. final Cursor cursor = mDatabase.queryChildDocuments(columns, "2"); assertEquals(3, cursor.getCount()); cursor.moveToNext(); assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE)); assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.moveToNext(); assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE)); assertEquals("image.jpg", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.moveToNext(); assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE)); assertEquals("music.mp3", getString(cursor, COLUMN_DISPLAY_NAME)); assertEquals(0, cursor.getCount()); cursor.close(); } Loading Loading @@ -598,10 +559,7 @@ public class MtpDatabaseTest extends AndroidTestCase { Root.COLUMN_AVAILABLE_BYTES }; mDatabase.getMapper().startAddingDocuments(null); mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord( 0, "Device", false, new MtpRoot[0], null, null)); mDatabase.getMapper().stopAddingDocuments(null); addTestDevice(); mDatabase.getMapper().startAddingDocuments("1"); mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { Loading Loading @@ -701,10 +659,7 @@ public class MtpDatabaseTest extends AndroidTestCase { } public void testReplaceExistingRoots() throws Exception { mDatabase.getMapper().startAddingDocuments(null); mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord( 0, "Device", true, new MtpRoot[0], null, null)); mDatabase.getMapper().stopAddingDocuments(null); addTestDevice(); // The client code should be able to replace existing rows with new information. // Add one. Loading Loading @@ -802,10 +757,7 @@ public class MtpDatabaseTest extends AndroidTestCase { public void testQueryRoots() throws Exception { // Add device document. mDatabase.getMapper().startAddingDocuments(null); mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord( 0, "Device", false, new MtpRoot[0], null, null)); mDatabase.getMapper().stopAddingDocuments(null); addTestDevice(); // It the device does not have storages, it shows a device root. { Loading Loading @@ -930,6 +882,9 @@ public class MtpDatabaseTest extends AndroidTestCase { // The new document should not be mapped with existing invalidated document. mDatabase.getMapper().clearMapping(); addTestDevice(); addTestStorage("1"); mDatabase.getMapper().startAddingDocuments("2"); mDatabase.putNewDocument( 0, Loading Loading
packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java +46 −21 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package com.android.mtp; import static com.android.mtp.MtpDatabaseConstants.*; import android.annotation.Nullable; import android.content.ContentValues; import android.database.Cursor; Loading @@ -26,6 +24,7 @@ import android.database.sqlite.SQLiteDatabase; import android.mtp.MtpObjectInfo; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.util.Log; import com.android.internal.util.Preconditions; Loading @@ -33,6 +32,7 @@ import java.io.FileNotFoundException; import java.util.HashMap; import java.util.Map; import static com.android.mtp.MtpDatabaseConstants.*; import static com.android.mtp.MtpDatabase.strings; /** Loading Loading @@ -75,7 +75,7 @@ class Mapper { extraValuesList, COLUMN_PARENT_DOCUMENT_ID + " IS NULL", EMPTY_ARGS, COLUMN_DEVICE_ID); Document.COLUMN_DISPLAY_NAME); database.setTransactionSuccessful(); return changed; } finally { Loading Loading @@ -172,13 +172,16 @@ class Mapper { final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); database.beginTransaction(); try { final ContentValues values = new ContentValues(); values.putNull(COLUMN_OBJECT_HANDLE); values.putNull(COLUMN_STORAGE_ID); values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED); database.update(TABLE_DOCUMENTS, values, null, null); database.setTransactionSuccessful(); mMappingMode.clear(); // Disconnect all device rows. try { startAddingDocuments(null); stopAddingDocuments(null); } catch (FileNotFoundException exception) { Log.e(MtpDocumentsProvider.TAG, "Unexpected FileNotFoundException.", exception); throw new RuntimeException(exception); } database.setTransactionSuccessful(); } finally { database.endTransaction(); } Loading Loading @@ -210,16 +213,20 @@ class Mapper { getParentOrHaltMapping(parentDocumentId); Preconditions.checkState(!mMappingMode.containsKey(parentDocumentId)); // Set all documents as invalidated. // Set all valid documents as invalidated. final ContentValues values = new ContentValues(); values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED); database.update(TABLE_DOCUMENTS, values, selection, args); database.update( TABLE_DOCUMENTS, values, selection + " AND " + COLUMN_ROW_STATE + " = ?", DatabaseUtils.appendSelectionArgs(args, strings(ROW_STATE_VALID))); // If we have rows that does not have MTP identifier, do heuristic mapping by name. final boolean useNameForResolving = DatabaseUtils.queryNumEntries( database, TABLE_DOCUMENTS, selection + " AND " + COLUMN_STORAGE_ID + " IS NULL", selection + " AND " + COLUMN_DEVICE_ID + " IS NULL", args) > 0; database.setTransactionSuccessful(); mMappingMode.put( Loading Loading @@ -270,11 +277,13 @@ class Mapper { TABLE_DOCUMENTS, strings(Document.COLUMN_DOCUMENT_ID), selection + " AND " + COLUMN_ROW_STATE + "=? AND " + COLUMN_ROW_STATE + " IN (?, ?) AND " + mappingKey + "=?", DatabaseUtils.appendSelectionArgs( args, strings(ROW_STATE_INVALIDATED, values.getAsString(mappingKey))), strings(ROW_STATE_INVALIDATED, ROW_STATE_DISCONNECTED, values.getAsString(mappingKey))), null, null, null, Loading Loading @@ -335,17 +344,28 @@ class Mapper { final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); database.beginTransaction(); try { getParentOrHaltMapping(parentId); final Identifier parentIdentifier = getParentOrHaltMapping(parentId); Preconditions.checkState(mMappingMode.containsKey(parentId)); mMappingMode.remove(parentId); boolean changed = false; // Delete all invalidated rows that cannot be mapped. // Delete/disconnect all invalidated rows that cannot be mapped. final boolean keepUnmatchedDocument = parentIdentifier == null || parentIdentifier.mDocumentType == DOCUMENT_TYPE_DEVICE; if (keepUnmatchedDocument) { if (mDatabase.disconnectDocumentsRecursively( COLUMN_ROW_STATE + " = ? AND " + selection, DatabaseUtils.appendSelectionArgs(strings(ROW_STATE_INVALIDATED), args))) { changed = true; } } else { if (mDatabase.deleteDocumentsAndRootsRecursively( COLUMN_ROW_STATE + " = ? AND " + selection, DatabaseUtils.appendSelectionArgs(strings(ROW_STATE_INVALIDATED), args))) { changed = true; } } database.setTransactionSuccessful(); return changed; Loading @@ -368,7 +388,12 @@ class Mapper { return null; } try { return mDatabase.createIdentifier(parentId); final Identifier identifier = mDatabase.createIdentifier(parentId); if (mDatabase.getRowState(parentId) == ROW_STATE_DISCONNECTED) { throw new FileNotFoundException( "document: " + parentId + " is in disconnected device."); } return identifier; } catch (FileNotFoundException error) { mMappingMode.remove(parentId); throw error; Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +79 −13 Original line number Diff line number Diff line Loading @@ -294,15 +294,6 @@ class MtpDatabase { "1"); } /** * Remove all rows belong to a device. * @param deviceId Device ID. */ void removeDeviceRows(int deviceId) { // Call non-recursive version because it anyway deletes all rows in the devices. deleteDocumentsAndRoots(COLUMN_DEVICE_ID + "=?", strings(deviceId)); } @Nullable String getDocumentIdForDevice(int deviceId) { final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, Loading Loading @@ -456,7 +447,43 @@ class MtpDatabase { } } private boolean deleteDocumentsAndRoots(String selection, String[] args) { /** * Marks the documents and their child as disconnected documents. * @param selection * @param args * @return True if at least one row is updated. */ boolean disconnectDocumentsRecursively(String selection, String[] args) { mDatabase.beginTransaction(); try { boolean changed = false; try (final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(Document.COLUMN_DOCUMENT_ID), selection, args, null, null, null)) { while (cursor.moveToNext()) { if (disconnectDocumentsRecursively( COLUMN_PARENT_DOCUMENT_ID + " = ?", strings(cursor.getString(0)))) { changed = true; } } } if (disconnectDocuments(selection, args)) { changed = true; } mDatabase.setTransactionSuccessful(); return changed; } finally { mDatabase.endTransaction(); } } boolean deleteDocumentsAndRoots(String selection, String[] args) { mDatabase.beginTransaction(); try { int deleted = 0; Loading @@ -481,6 +508,39 @@ class MtpDatabase { } } boolean disconnectDocuments(String selection, String[] args) { mDatabase.beginTransaction(); try { final ContentValues values = new ContentValues(); values.put(COLUMN_ROW_STATE, ROW_STATE_DISCONNECTED); values.putNull(COLUMN_DEVICE_ID); values.putNull(COLUMN_STORAGE_ID); values.putNull(COLUMN_OBJECT_HANDLE); final boolean updated = mDatabase.update(TABLE_DOCUMENTS, values, selection, args) != 0; mDatabase.setTransactionSuccessful(); return updated; } finally { mDatabase.endTransaction(); } } int getRowState(String documentId) throws FileNotFoundException { try (final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(COLUMN_ROW_STATE), SELECTION_DOCUMENT_ID, strings(documentId), null, null, null)) { if (cursor.getCount() == 0) { throw new FileNotFoundException(); } cursor.moveToNext(); return cursor.getInt(0); } } private static class OpenHelper extends SQLiteOpenHelper { public OpenHelper(Context context, int flags) { super(context, Loading @@ -497,6 +557,12 @@ class MtpDatabase { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion == 1) { db.execSQL("DROP TABLE " + TABLE_DOCUMENTS); db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA); onCreate(db); return; } throw new UnsupportedOperationException(); } } Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java +8 −2 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ import java.util.Map; * Class containing MtpDatabase constants. */ class MtpDatabaseConstants { static final int DATABASE_VERSION = 1; static final int DATABASE_VERSION = 2; static final String DATABASE_NAME = "database"; static final int FLAG_DATABASE_IN_MEMORY = 1; Loading Loading @@ -77,6 +77,12 @@ class MtpDatabaseConstants { */ static final int ROW_STATE_INVALIDATED = 1; /** * The documents are of device/storage that are disconnected now. The documents are invisible * but their document ID will be reuse when the device/storage is connected again. */ static final int ROW_STATE_DISCONNECTED = 2; /** * Mapping mode that uses MTP identifier to find corresponding rows. */ Loading Loading @@ -113,7 +119,7 @@ class MtpDatabaseConstants { "CREATE TABLE " + TABLE_DOCUMENTS + " (" + Document.COLUMN_DOCUMENT_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + COLUMN_DEVICE_ID + " INTEGER NOT NULL," + COLUMN_DEVICE_ID + " INTEGER," + COLUMN_STORAGE_ID + " INTEGER," + COLUMN_OBJECT_HANDLE + " INTEGER," + COLUMN_PARENT_DOCUMENT_ID + " INTEGER," + Loading
packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java +14 −59 Original line number Diff line number Diff line Loading @@ -95,7 +95,8 @@ public class MtpDatabaseTest extends AndroidTestCase { assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID)); assertEquals(1, getInt(cursor, COLUMN_STORAGE_ID)); assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE)); assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE)); assertEquals( DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE)); assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME)); assertTrue(isNull(cursor, COLUMN_SUMMARY)); assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED)); Loading @@ -103,7 +104,8 @@ public class MtpDatabaseTest extends AndroidTestCase { assertEquals(0, getInt(cursor, COLUMN_FLAGS)); assertEquals(1000, getInt(cursor, COLUMN_SIZE)); assertEquals( MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, getInt(cursor, COLUMN_DOCUMENT_TYPE)); MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, getInt(cursor, COLUMN_DOCUMENT_TYPE)); cursor.close(); } Loading Loading @@ -296,15 +298,7 @@ public class MtpDatabaseTest extends AndroidTestCase { { final Cursor cursor = mDatabase.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); cursor.moveToNext(); assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_STORAGE_ID)); assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.moveToNext(); assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_STORAGE_ID)); assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME)); assertEquals(0, cursor.getCount()); cursor.close(); } Loading @@ -314,28 +308,10 @@ public class MtpDatabaseTest extends AndroidTestCase { new MtpRoot(0, 200, "Storage A", 2000, 0, ""), new MtpRoot(0, 202, "Storage C", 2002, 0, "") }); { final Cursor cursor = mDatabase.queryRootDocuments(columns); assertEquals(3, cursor.getCount()); cursor.moveToNext(); assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID)); assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID)); assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.moveToNext(); assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_STORAGE_ID)); assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.moveToNext(); assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID)); assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID)); assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.close(); } mDatabase.getMapper().stopAddingDocuments("1"); { // After compeleting mapping, Storage A can be obtained with new storage ID. final Cursor cursor = mDatabase.queryRootDocuments(columns); assertEquals(2, cursor.getCount()); cursor.moveToNext(); Loading Loading @@ -372,24 +348,9 @@ public class MtpDatabaseTest extends AndroidTestCase { addTestStorage("1"); { // Don't return objects that lost MTP object handles. final Cursor cursor = mDatabase.queryChildDocuments(columns, "2"); assertEquals(3, cursor.getCount()); cursor.moveToNext(); assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE)); assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.moveToNext(); assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE)); assertEquals("image.jpg", getString(cursor, COLUMN_DISPLAY_NAME)); cursor.moveToNext(); assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID)); assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE)); assertEquals("music.mp3", getString(cursor, COLUMN_DISPLAY_NAME)); assertEquals(0, cursor.getCount()); cursor.close(); } Loading Loading @@ -598,10 +559,7 @@ public class MtpDatabaseTest extends AndroidTestCase { Root.COLUMN_AVAILABLE_BYTES }; mDatabase.getMapper().startAddingDocuments(null); mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord( 0, "Device", false, new MtpRoot[0], null, null)); mDatabase.getMapper().stopAddingDocuments(null); addTestDevice(); mDatabase.getMapper().startAddingDocuments("1"); mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { Loading Loading @@ -701,10 +659,7 @@ public class MtpDatabaseTest extends AndroidTestCase { } public void testReplaceExistingRoots() throws Exception { mDatabase.getMapper().startAddingDocuments(null); mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord( 0, "Device", true, new MtpRoot[0], null, null)); mDatabase.getMapper().stopAddingDocuments(null); addTestDevice(); // The client code should be able to replace existing rows with new information. // Add one. Loading Loading @@ -802,10 +757,7 @@ public class MtpDatabaseTest extends AndroidTestCase { public void testQueryRoots() throws Exception { // Add device document. mDatabase.getMapper().startAddingDocuments(null); mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord( 0, "Device", false, new MtpRoot[0], null, null)); mDatabase.getMapper().stopAddingDocuments(null); addTestDevice(); // It the device does not have storages, it shows a device root. { Loading Loading @@ -930,6 +882,9 @@ public class MtpDatabaseTest extends AndroidTestCase { // The new document should not be mapped with existing invalidated document. mDatabase.getMapper().clearMapping(); addTestDevice(); addTestStorage("1"); mDatabase.getMapper().startAddingDocuments("2"); mDatabase.putNewDocument( 0, Loading