Loading packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java +34 −13 Original line number Original line Diff line number Diff line Loading @@ -42,6 +42,7 @@ import static com.android.mtp.MtpDatabase.strings; * Also see the comments of {@link MtpDatabase}. * Also see the comments of {@link MtpDatabase}. */ */ class Mapper { class Mapper { private static final String[] EMPTY_ARGS = new String[0]; private final MtpDatabase mDatabase; private final MtpDatabase mDatabase; /** /** Loading @@ -55,21 +56,43 @@ class Mapper { mDatabase = database; mDatabase = database; } } synchronized String putDeviceDocument(int deviceId, String name, MtpRoot[] roots) { final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); Preconditions.checkState(mMappingMode.containsKey(/* no parent for root */ null)); database.beginTransaction(); try { final ContentValues[] valuesList = new ContentValues[1]; valuesList[0] = new ContentValues(); MtpDatabase.getDeviceDocumentValues(valuesList[0], deviceId, name, roots); putDocuments( valuesList, COLUMN_PARENT_DOCUMENT_ID + " IS NULL", EMPTY_ARGS, /* heuristic */ false, COLUMN_DEVICE_ID); database.setTransactionSuccessful(); return valuesList[0].getAsString(Document.COLUMN_DOCUMENT_ID); } finally { database.endTransaction(); } } /** /** * Puts root information to database. * Puts root information to database. * @param deviceId Device ID * @param parentDocumentId Document ID of device document. * @param resources Resources required to localize root name. * @param resources Resources required to localize root name. * @param roots List of root information. * @param roots List of root information. * @return If roots are added or removed from the database. * @return If roots are added or removed from the database. */ */ synchronized boolean putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) { synchronized boolean putRootDocuments( String parentDocumentId, Resources resources, MtpRoot[] roots) { final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); database.beginTransaction(); database.beginTransaction(); try { try { final boolean heuristic; final boolean heuristic; final String mapColumn; final String mapColumn; Preconditions.checkState(mMappingMode.containsKey(/* no parent for root */ null)); Preconditions.checkState(mMappingMode.containsKey(parentDocumentId)); switch (mMappingMode.get(/* no parent for root */ null)) { switch (mMappingMode.get(parentDocumentId)) { case MAP_BY_MTP_IDENTIFIER: case MAP_BY_MTP_IDENTIFIER: heuristic = false; heuristic = false; mapColumn = COLUMN_STORAGE_ID; mapColumn = COLUMN_STORAGE_ID; Loading @@ -83,16 +106,14 @@ class Mapper { } } final ContentValues[] valuesList = new ContentValues[roots.length]; final ContentValues[] valuesList = new ContentValues[roots.length]; for (int i = 0; i < roots.length; i++) { for (int i = 0; i < roots.length; i++) { if (roots[i].mDeviceId != deviceId) { throw new IllegalArgumentException(); } valuesList[i] = new ContentValues(); valuesList[i] = new ContentValues(); MtpDatabase.getRootDocumentValues(valuesList[i], resources, roots[i]); MtpDatabase.getStorageDocumentValues( valuesList[i], resources, parentDocumentId, roots[i]); } } final boolean changed = putDocuments( final boolean changed = putDocuments( valuesList, valuesList, COLUMN_PARENT_DOCUMENT_ID + " IS NULL", COLUMN_PARENT_DOCUMENT_ID + "=?", new String[0], strings(parentDocumentId), heuristic, heuristic, mapColumn); mapColumn); final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues(); Loading Loading @@ -146,7 +167,7 @@ class Mapper { final ContentValues[] valuesList = new ContentValues[documents.length]; final ContentValues[] valuesList = new ContentValues[documents.length]; for (int i = 0; i < documents.length; i++) { for (int i = 0; i < documents.length; i++) { valuesList[i] = new ContentValues(); valuesList[i] = new ContentValues(); MtpDatabase.getChildDocumentValues( MtpDatabase.getObjectDocumentValues( valuesList[i], deviceId, parentId, documents[i]); valuesList[i], deviceId, parentId, documents[i]); } } putDocuments( putDocuments( Loading Loading @@ -193,7 +214,7 @@ class Mapper { args = strings(parentDocumentId); args = strings(parentDocumentId); } else { } else { selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; args = new String[0]; args = EMPTY_ARGS; } } final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); Loading Loading @@ -312,7 +333,7 @@ class Mapper { args = strings(parentId); args = strings(parentId); } else { } else { selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; args = new String[0]; args = EMPTY_ARGS; } } final String groupKey; final String groupKey; switch (mMappingMode.get(parentId)) { switch (mMappingMode.get(parentId)) { Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +39 −13 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.Context; import android.content.res.Resources; import android.content.res.Resources; import android.database.Cursor; import android.database.Cursor; import android.database.MatrixCursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.database.sqlite.SQLiteQueryBuilder; Loading @@ -35,9 +36,9 @@ import android.provider.DocumentsContract.Root; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import java.io.FileNotFoundException; import java.io.FileNotFoundException; import java.util.HashSet; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Objects; import java.util.Set; /** /** * Database for MTP objects. * Database for MTP objects. Loading Loading @@ -107,11 +108,14 @@ class MtpDatabase { * @return Database cursor. * @return Database cursor. */ */ Cursor queryRoots(String[] columnNames) { Cursor queryRoots(String[] columnNames) { return mDatabase.query( final SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); VIEW_ROOTS, builder.setTables(JOIN_ROOTS); builder.setProjectionMap(COLUMN_MAP_ROOTS); return builder.query( mDatabase, columnNames, columnNames, COLUMN_ROW_STATE + " IN (?, ?)", COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + " = ?", strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED), strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_STORAGE), null, null, null, null, null); null); Loading @@ -128,8 +132,8 @@ class MtpDatabase { return mDatabase.query( return mDatabase.query( TABLE_DOCUMENTS, TABLE_DOCUMENTS, columnNames, columnNames, COLUMN_ROW_STATE + " IN (?, ?)", COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + "=?", strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED), strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_STORAGE), null, null, null, null, null); null); Loading Loading @@ -216,7 +220,7 @@ class MtpDatabase { */ */ String putNewDocument(int deviceId, String parentDocumentId, MtpObjectInfo info) { String putNewDocument(int deviceId, String parentDocumentId, MtpObjectInfo info) { final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues(); getChildDocumentValues(values, deviceId, parentDocumentId, info); getObjectDocumentValues(values, deviceId, parentDocumentId, info); mDatabase.beginTransaction(); mDatabase.beginTransaction(); try { try { final long id = mDatabase.insert(TABLE_DOCUMENTS, null, values); final long id = mDatabase.insert(TABLE_DOCUMENTS, null, values); Loading Loading @@ -344,7 +348,6 @@ class MtpDatabase { public void onCreate(SQLiteDatabase db) { public void onCreate(SQLiteDatabase db) { db.execSQL(QUERY_CREATE_DOCUMENTS); db.execSQL(QUERY_CREATE_DOCUMENTS); db.execSQL(QUERY_CREATE_ROOT_EXTRA); db.execSQL(QUERY_CREATE_ROOT_EXTRA); db.execSQL(QUERY_CREATE_VIEW_ROOTS); } } @Override @Override Loading @@ -358,18 +361,41 @@ class MtpDatabase { context.deleteDatabase(DATABASE_NAME); context.deleteDatabase(DATABASE_NAME); } } static void getDeviceDocumentValues( ContentValues values, int deviceId, String name, MtpRoot[] roots) { values.clear(); values.put(COLUMN_DEVICE_ID, deviceId); values.putNull(COLUMN_STORAGE_ID); values.putNull(COLUMN_OBJECT_HANDLE); values.putNull(COLUMN_PARENT_DOCUMENT_ID); values.put(COLUMN_ROW_STATE, ROW_STATE_VALID); values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_DEVICE); values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR); values.put(Document.COLUMN_DISPLAY_NAME, name); values.putNull(Document.COLUMN_SUMMARY); values.putNull(Document.COLUMN_LAST_MODIFIED); values.put(Document.COLUMN_ICON, R.drawable.ic_root_mtp); values.put(Document.COLUMN_FLAGS, 0); long size = 0; for (final MtpRoot root : roots) { size += root.mMaxCapacity - root.mFreeSpace; } values.put(Document.COLUMN_SIZE, size); } /** /** * Gets {@link ContentValues} for the given root. * Gets {@link ContentValues} for the given root. * @param values {@link ContentValues} that receives values. * @param values {@link ContentValues} that receives values. * @param resources Resources used to get localized root name. * @param resources Resources used to get localized root name. * @param root Root to be converted {@link ContentValues}. * @param root Root to be converted {@link ContentValues}. */ */ static void getRootDocumentValues(ContentValues values, Resources resources, MtpRoot root) { static void getStorageDocumentValues( ContentValues values, Resources resources, String parentDocumentId, MtpRoot root) { values.clear(); values.clear(); values.put(COLUMN_DEVICE_ID, root.mDeviceId); values.put(COLUMN_DEVICE_ID, root.mDeviceId); values.put(COLUMN_STORAGE_ID, root.mStorageId); values.put(COLUMN_STORAGE_ID, root.mStorageId); values.putNull(COLUMN_OBJECT_HANDLE); values.putNull(COLUMN_OBJECT_HANDLE); values.putNull(COLUMN_PARENT_DOCUMENT_ID); values.put(COLUMN_PARENT_DOCUMENT_ID, parentDocumentId); values.put(COLUMN_ROW_STATE, ROW_STATE_VALID); values.put(COLUMN_ROW_STATE, ROW_STATE_VALID); values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_STORAGE); values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_STORAGE); values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR); values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR); Loading @@ -389,7 +415,7 @@ class MtpDatabase { * @param parentId Parent document ID of the object. * @param parentId Parent document ID of the object. * @param info MTP object info. * @param info MTP object info. */ */ static void getChildDocumentValues( static void getObjectDocumentValues( ContentValues values, int deviceId, String parentId, MtpObjectInfo info) { ContentValues values, int deviceId, String parentId, MtpObjectInfo info) { values.clear(); values.clear(); final String mimeType = info.getFormat() == MtpConstants.FORMAT_ASSOCIATION ? final String mimeType = info.getFormat() == MtpConstants.FORMAT_ASSOCIATION ? Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java +36 −29 Original line number Original line Diff line number Diff line Loading @@ -16,9 +16,13 @@ package com.android.mtp; package com.android.mtp; import android.database.sqlite.SQLiteQueryBuilder; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.provider.DocumentsContract.Root; import java.util.HashMap; import java.util.Map; /** /** * Class containing MtpDatabase constants. * Class containing MtpDatabase constants. */ */ Loading @@ -41,9 +45,13 @@ class MtpDatabaseConstants { static final String TABLE_ROOT_EXTRA = "RootExtra"; static final String TABLE_ROOT_EXTRA = "RootExtra"; /** /** * View to join Documents and RootExtra tables to provide roots information. * 'FROM' closure of joining TABLE_DOCUMENTS and TABLE_ROOT_EXTRA. */ */ static final String VIEW_ROOTS = "Roots"; static final String JOIN_ROOTS = createJoinFromClosure( TABLE_DOCUMENTS, TABLE_ROOT_EXTRA, Document.COLUMN_DOCUMENT_ID, Root.COLUMN_ROOT_ID); static final String COLUMN_DEVICE_ID = "device_id"; static final String COLUMN_DEVICE_ID = "device_id"; static final String COLUMN_STORAGE_ID = "storage_id"; static final String COLUMN_STORAGE_ID = "storage_id"; Loading Loading @@ -86,8 +94,6 @@ class MtpDatabaseConstants { /** /** * Document that represents a MTP device. * Document that represents a MTP device. * Note we have "device" document only when the device has multiple storage volumes. Otherwise * we regard the single "storage" document as root. */ */ static final int DOCUMENT_TYPE_DEVICE = 0; static final int DOCUMENT_TYPE_DEVICE = 0; Loading Loading @@ -131,30 +137,31 @@ class MtpDatabaseConstants { Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);"; Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);"; /** /** * Creates a view to join Documents table and RootExtra table on their primary keys to * Map for columns names to provide DocumentContract.Root compatible columns. * provide DocumentContract.Root equivalent information. * @see SQLiteQueryBuilder#setProjectionMap(Map) */ */ static final String QUERY_CREATE_VIEW_ROOTS = static final Map<String, String> COLUMN_MAP_ROOTS; "CREATE VIEW " + VIEW_ROOTS + " AS SELECT " + static { TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " + COLUMN_MAP_ROOTS = new HashMap<>(); Root.COLUMN_ROOT_ID + "," + COLUMN_MAP_ROOTS.put(Root.COLUMN_ROOT_ID, TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID); TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS + "," + COLUMN_MAP_ROOTS.put(Root.COLUMN_FLAGS, TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS); TABLE_DOCUMENTS + "." + Document.COLUMN_ICON + " AS " + COLUMN_MAP_ROOTS.put(Root.COLUMN_ICON, TABLE_DOCUMENTS + "." + Document.COLUMN_ICON); Root.COLUMN_ICON + "," + COLUMN_MAP_ROOTS.put( TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME + " AS " + Root.COLUMN_TITLE, TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME); Root.COLUMN_TITLE + "," + COLUMN_MAP_ROOTS.put(Root.COLUMN_SUMMARY, TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY); TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " + COLUMN_MAP_ROOTS.put( Root.COLUMN_SUMMARY + "," + Root.COLUMN_DOCUMENT_ID, TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID); TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " + COLUMN_MAP_ROOTS.put( Root.COLUMN_DOCUMENT_ID + "," + Root.COLUMN_AVAILABLE_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES); TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES + "," + COLUMN_MAP_ROOTS.put( TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES + "," + Root.COLUMN_CAPACITY_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES); TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES + "," + COLUMN_MAP_ROOTS.put( TABLE_DOCUMENTS + "." + COLUMN_ROW_STATE + Root.COLUMN_MIME_TYPES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES); " FROM " + TABLE_DOCUMENTS + " INNER JOIN " + TABLE_ROOT_EXTRA + } " ON " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL AND " + private static String createJoinFromClosure( TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + String table1, String table2, String column1, String column2) { "=" + return table1 + " INNER JOIN " + table2 + TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID; " ON " + table1 + "." + column1 + " = " + table2 + "." + column2; } } } packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +0 −3 Original line number Original line Diff line number Diff line Loading @@ -190,9 +190,6 @@ public class MtpDocumentsProvider extends DocumentsProvider { getDocumentLoader(parentIdentifier).clearTask(parentIdentifier); getDocumentLoader(parentIdentifier).clearTask(parentIdentifier); notifyChildDocumentsChange(parentIdentifier.mDocumentId); notifyChildDocumentsChange(parentIdentifier.mDocumentId); } catch (IOException error) { } catch (IOException error) { for (final StackTraceElement element : error.getStackTrace()) { Log.e("hirono", element.toString()); } throw new FileNotFoundException(error.getMessage()); throw new FileNotFoundException(error.getMessage()); } } } } Loading packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java +27 −8 Original line number Original line Diff line number Diff line Loading @@ -7,8 +7,11 @@ import android.net.Uri; import android.os.Process; import android.os.Process; import android.provider.DocumentsContract; import android.provider.DocumentsContract; import android.util.Log; import android.util.Log; import android.util.SparseArray; import java.io.IOException; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask; Loading Loading @@ -106,26 +109,42 @@ final class RootScanner { int pollingCount = 0; int pollingCount = 0; while (!Thread.interrupted()) { while (!Thread.interrupted()) { final int[] deviceIds = mManager.getOpenedDeviceIds(); final int[] deviceIds = mManager.getOpenedDeviceIds(); if (deviceIds.length == 0) { final Map<String, MtpRoot[]> rootsMap = new HashMap<>(); return; } boolean changed = false; boolean changed = false; // Update devices. mDatabase.getMapper().startAddingDocuments(null /* parentDocumentId */); mDatabase.getMapper().startAddingDocuments(null /* parentDocumentId */); for (int deviceId : deviceIds) { for (final int deviceId : deviceIds) { try { try { final MtpRoot[] roots = mManager.getRoots(deviceId); final MtpRoot[] roots = mManager.getRoots(deviceId); if (mDatabase.getMapper().putRootDocuments(deviceId, mResources, roots)) { final String id = mDatabase.getMapper().putDeviceDocument( deviceId, mManager.getDeviceName(deviceId), roots); if (id != null) { changed = true; changed = true; rootsMap.put(id, roots); } } } catch (IOException | SQLiteException exception) { } catch (IOException exception) { // The error may happen on the device. We would like to continue getting // The error may happen on the device. We would like to continue getting // roots for other devices. // roots for other devices. Log.e(MtpDocumentsProvider.TAG, exception.getMessage()); Log.e(MtpDocumentsProvider.TAG, exception.getMessage()); } } } } if (mDatabase.getMapper().stopAddingDocuments(null /* parentDocumentId */)) { mDatabase.getMapper().stopAddingDocuments(null /* parentDocumentId */); // Update roots. for (final String documentId : rootsMap.keySet()) { mDatabase.getMapper().startAddingDocuments(documentId); if (mDatabase.getMapper().putRootDocuments( documentId, mResources, rootsMap.get(documentId))) { changed = true; } if (mDatabase.getMapper().stopAddingDocuments(documentId)) { changed = true; changed = true; } } } if (changed) { if (changed) { notifyChange(); notifyChange(); } } Loading Loading
packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java +34 −13 Original line number Original line Diff line number Diff line Loading @@ -42,6 +42,7 @@ import static com.android.mtp.MtpDatabase.strings; * Also see the comments of {@link MtpDatabase}. * Also see the comments of {@link MtpDatabase}. */ */ class Mapper { class Mapper { private static final String[] EMPTY_ARGS = new String[0]; private final MtpDatabase mDatabase; private final MtpDatabase mDatabase; /** /** Loading @@ -55,21 +56,43 @@ class Mapper { mDatabase = database; mDatabase = database; } } synchronized String putDeviceDocument(int deviceId, String name, MtpRoot[] roots) { final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); Preconditions.checkState(mMappingMode.containsKey(/* no parent for root */ null)); database.beginTransaction(); try { final ContentValues[] valuesList = new ContentValues[1]; valuesList[0] = new ContentValues(); MtpDatabase.getDeviceDocumentValues(valuesList[0], deviceId, name, roots); putDocuments( valuesList, COLUMN_PARENT_DOCUMENT_ID + " IS NULL", EMPTY_ARGS, /* heuristic */ false, COLUMN_DEVICE_ID); database.setTransactionSuccessful(); return valuesList[0].getAsString(Document.COLUMN_DOCUMENT_ID); } finally { database.endTransaction(); } } /** /** * Puts root information to database. * Puts root information to database. * @param deviceId Device ID * @param parentDocumentId Document ID of device document. * @param resources Resources required to localize root name. * @param resources Resources required to localize root name. * @param roots List of root information. * @param roots List of root information. * @return If roots are added or removed from the database. * @return If roots are added or removed from the database. */ */ synchronized boolean putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) { synchronized boolean putRootDocuments( String parentDocumentId, Resources resources, MtpRoot[] roots) { final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); database.beginTransaction(); database.beginTransaction(); try { try { final boolean heuristic; final boolean heuristic; final String mapColumn; final String mapColumn; Preconditions.checkState(mMappingMode.containsKey(/* no parent for root */ null)); Preconditions.checkState(mMappingMode.containsKey(parentDocumentId)); switch (mMappingMode.get(/* no parent for root */ null)) { switch (mMappingMode.get(parentDocumentId)) { case MAP_BY_MTP_IDENTIFIER: case MAP_BY_MTP_IDENTIFIER: heuristic = false; heuristic = false; mapColumn = COLUMN_STORAGE_ID; mapColumn = COLUMN_STORAGE_ID; Loading @@ -83,16 +106,14 @@ class Mapper { } } final ContentValues[] valuesList = new ContentValues[roots.length]; final ContentValues[] valuesList = new ContentValues[roots.length]; for (int i = 0; i < roots.length; i++) { for (int i = 0; i < roots.length; i++) { if (roots[i].mDeviceId != deviceId) { throw new IllegalArgumentException(); } valuesList[i] = new ContentValues(); valuesList[i] = new ContentValues(); MtpDatabase.getRootDocumentValues(valuesList[i], resources, roots[i]); MtpDatabase.getStorageDocumentValues( valuesList[i], resources, parentDocumentId, roots[i]); } } final boolean changed = putDocuments( final boolean changed = putDocuments( valuesList, valuesList, COLUMN_PARENT_DOCUMENT_ID + " IS NULL", COLUMN_PARENT_DOCUMENT_ID + "=?", new String[0], strings(parentDocumentId), heuristic, heuristic, mapColumn); mapColumn); final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues(); Loading Loading @@ -146,7 +167,7 @@ class Mapper { final ContentValues[] valuesList = new ContentValues[documents.length]; final ContentValues[] valuesList = new ContentValues[documents.length]; for (int i = 0; i < documents.length; i++) { for (int i = 0; i < documents.length; i++) { valuesList[i] = new ContentValues(); valuesList[i] = new ContentValues(); MtpDatabase.getChildDocumentValues( MtpDatabase.getObjectDocumentValues( valuesList[i], deviceId, parentId, documents[i]); valuesList[i], deviceId, parentId, documents[i]); } } putDocuments( putDocuments( Loading Loading @@ -193,7 +214,7 @@ class Mapper { args = strings(parentDocumentId); args = strings(parentDocumentId); } else { } else { selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; args = new String[0]; args = EMPTY_ARGS; } } final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); Loading Loading @@ -312,7 +333,7 @@ class Mapper { args = strings(parentId); args = strings(parentId); } else { } else { selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; args = new String[0]; args = EMPTY_ARGS; } } final String groupKey; final String groupKey; switch (mMappingMode.get(parentId)) { switch (mMappingMode.get(parentId)) { Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +39 −13 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.Context; import android.content.res.Resources; import android.content.res.Resources; import android.database.Cursor; import android.database.Cursor; import android.database.MatrixCursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.database.sqlite.SQLiteQueryBuilder; Loading @@ -35,9 +36,9 @@ import android.provider.DocumentsContract.Root; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import java.io.FileNotFoundException; import java.io.FileNotFoundException; import java.util.HashSet; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Objects; import java.util.Set; /** /** * Database for MTP objects. * Database for MTP objects. Loading Loading @@ -107,11 +108,14 @@ class MtpDatabase { * @return Database cursor. * @return Database cursor. */ */ Cursor queryRoots(String[] columnNames) { Cursor queryRoots(String[] columnNames) { return mDatabase.query( final SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); VIEW_ROOTS, builder.setTables(JOIN_ROOTS); builder.setProjectionMap(COLUMN_MAP_ROOTS); return builder.query( mDatabase, columnNames, columnNames, COLUMN_ROW_STATE + " IN (?, ?)", COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + " = ?", strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED), strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_STORAGE), null, null, null, null, null); null); Loading @@ -128,8 +132,8 @@ class MtpDatabase { return mDatabase.query( return mDatabase.query( TABLE_DOCUMENTS, TABLE_DOCUMENTS, columnNames, columnNames, COLUMN_ROW_STATE + " IN (?, ?)", COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + "=?", strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED), strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_STORAGE), null, null, null, null, null); null); Loading Loading @@ -216,7 +220,7 @@ class MtpDatabase { */ */ String putNewDocument(int deviceId, String parentDocumentId, MtpObjectInfo info) { String putNewDocument(int deviceId, String parentDocumentId, MtpObjectInfo info) { final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues(); getChildDocumentValues(values, deviceId, parentDocumentId, info); getObjectDocumentValues(values, deviceId, parentDocumentId, info); mDatabase.beginTransaction(); mDatabase.beginTransaction(); try { try { final long id = mDatabase.insert(TABLE_DOCUMENTS, null, values); final long id = mDatabase.insert(TABLE_DOCUMENTS, null, values); Loading Loading @@ -344,7 +348,6 @@ class MtpDatabase { public void onCreate(SQLiteDatabase db) { public void onCreate(SQLiteDatabase db) { db.execSQL(QUERY_CREATE_DOCUMENTS); db.execSQL(QUERY_CREATE_DOCUMENTS); db.execSQL(QUERY_CREATE_ROOT_EXTRA); db.execSQL(QUERY_CREATE_ROOT_EXTRA); db.execSQL(QUERY_CREATE_VIEW_ROOTS); } } @Override @Override Loading @@ -358,18 +361,41 @@ class MtpDatabase { context.deleteDatabase(DATABASE_NAME); context.deleteDatabase(DATABASE_NAME); } } static void getDeviceDocumentValues( ContentValues values, int deviceId, String name, MtpRoot[] roots) { values.clear(); values.put(COLUMN_DEVICE_ID, deviceId); values.putNull(COLUMN_STORAGE_ID); values.putNull(COLUMN_OBJECT_HANDLE); values.putNull(COLUMN_PARENT_DOCUMENT_ID); values.put(COLUMN_ROW_STATE, ROW_STATE_VALID); values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_DEVICE); values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR); values.put(Document.COLUMN_DISPLAY_NAME, name); values.putNull(Document.COLUMN_SUMMARY); values.putNull(Document.COLUMN_LAST_MODIFIED); values.put(Document.COLUMN_ICON, R.drawable.ic_root_mtp); values.put(Document.COLUMN_FLAGS, 0); long size = 0; for (final MtpRoot root : roots) { size += root.mMaxCapacity - root.mFreeSpace; } values.put(Document.COLUMN_SIZE, size); } /** /** * Gets {@link ContentValues} for the given root. * Gets {@link ContentValues} for the given root. * @param values {@link ContentValues} that receives values. * @param values {@link ContentValues} that receives values. * @param resources Resources used to get localized root name. * @param resources Resources used to get localized root name. * @param root Root to be converted {@link ContentValues}. * @param root Root to be converted {@link ContentValues}. */ */ static void getRootDocumentValues(ContentValues values, Resources resources, MtpRoot root) { static void getStorageDocumentValues( ContentValues values, Resources resources, String parentDocumentId, MtpRoot root) { values.clear(); values.clear(); values.put(COLUMN_DEVICE_ID, root.mDeviceId); values.put(COLUMN_DEVICE_ID, root.mDeviceId); values.put(COLUMN_STORAGE_ID, root.mStorageId); values.put(COLUMN_STORAGE_ID, root.mStorageId); values.putNull(COLUMN_OBJECT_HANDLE); values.putNull(COLUMN_OBJECT_HANDLE); values.putNull(COLUMN_PARENT_DOCUMENT_ID); values.put(COLUMN_PARENT_DOCUMENT_ID, parentDocumentId); values.put(COLUMN_ROW_STATE, ROW_STATE_VALID); values.put(COLUMN_ROW_STATE, ROW_STATE_VALID); values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_STORAGE); values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_STORAGE); values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR); values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR); Loading @@ -389,7 +415,7 @@ class MtpDatabase { * @param parentId Parent document ID of the object. * @param parentId Parent document ID of the object. * @param info MTP object info. * @param info MTP object info. */ */ static void getChildDocumentValues( static void getObjectDocumentValues( ContentValues values, int deviceId, String parentId, MtpObjectInfo info) { ContentValues values, int deviceId, String parentId, MtpObjectInfo info) { values.clear(); values.clear(); final String mimeType = info.getFormat() == MtpConstants.FORMAT_ASSOCIATION ? final String mimeType = info.getFormat() == MtpConstants.FORMAT_ASSOCIATION ? Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java +36 −29 Original line number Original line Diff line number Diff line Loading @@ -16,9 +16,13 @@ package com.android.mtp; package com.android.mtp; import android.database.sqlite.SQLiteQueryBuilder; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.provider.DocumentsContract.Root; import java.util.HashMap; import java.util.Map; /** /** * Class containing MtpDatabase constants. * Class containing MtpDatabase constants. */ */ Loading @@ -41,9 +45,13 @@ class MtpDatabaseConstants { static final String TABLE_ROOT_EXTRA = "RootExtra"; static final String TABLE_ROOT_EXTRA = "RootExtra"; /** /** * View to join Documents and RootExtra tables to provide roots information. * 'FROM' closure of joining TABLE_DOCUMENTS and TABLE_ROOT_EXTRA. */ */ static final String VIEW_ROOTS = "Roots"; static final String JOIN_ROOTS = createJoinFromClosure( TABLE_DOCUMENTS, TABLE_ROOT_EXTRA, Document.COLUMN_DOCUMENT_ID, Root.COLUMN_ROOT_ID); static final String COLUMN_DEVICE_ID = "device_id"; static final String COLUMN_DEVICE_ID = "device_id"; static final String COLUMN_STORAGE_ID = "storage_id"; static final String COLUMN_STORAGE_ID = "storage_id"; Loading Loading @@ -86,8 +94,6 @@ class MtpDatabaseConstants { /** /** * Document that represents a MTP device. * Document that represents a MTP device. * Note we have "device" document only when the device has multiple storage volumes. Otherwise * we regard the single "storage" document as root. */ */ static final int DOCUMENT_TYPE_DEVICE = 0; static final int DOCUMENT_TYPE_DEVICE = 0; Loading Loading @@ -131,30 +137,31 @@ class MtpDatabaseConstants { Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);"; Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);"; /** /** * Creates a view to join Documents table and RootExtra table on their primary keys to * Map for columns names to provide DocumentContract.Root compatible columns. * provide DocumentContract.Root equivalent information. * @see SQLiteQueryBuilder#setProjectionMap(Map) */ */ static final String QUERY_CREATE_VIEW_ROOTS = static final Map<String, String> COLUMN_MAP_ROOTS; "CREATE VIEW " + VIEW_ROOTS + " AS SELECT " + static { TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " + COLUMN_MAP_ROOTS = new HashMap<>(); Root.COLUMN_ROOT_ID + "," + COLUMN_MAP_ROOTS.put(Root.COLUMN_ROOT_ID, TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID); TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS + "," + COLUMN_MAP_ROOTS.put(Root.COLUMN_FLAGS, TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS); TABLE_DOCUMENTS + "." + Document.COLUMN_ICON + " AS " + COLUMN_MAP_ROOTS.put(Root.COLUMN_ICON, TABLE_DOCUMENTS + "." + Document.COLUMN_ICON); Root.COLUMN_ICON + "," + COLUMN_MAP_ROOTS.put( TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME + " AS " + Root.COLUMN_TITLE, TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME); Root.COLUMN_TITLE + "," + COLUMN_MAP_ROOTS.put(Root.COLUMN_SUMMARY, TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY); TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " + COLUMN_MAP_ROOTS.put( Root.COLUMN_SUMMARY + "," + Root.COLUMN_DOCUMENT_ID, TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID); TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " + COLUMN_MAP_ROOTS.put( Root.COLUMN_DOCUMENT_ID + "," + Root.COLUMN_AVAILABLE_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES); TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES + "," + COLUMN_MAP_ROOTS.put( TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES + "," + Root.COLUMN_CAPACITY_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES); TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES + "," + COLUMN_MAP_ROOTS.put( TABLE_DOCUMENTS + "." + COLUMN_ROW_STATE + Root.COLUMN_MIME_TYPES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES); " FROM " + TABLE_DOCUMENTS + " INNER JOIN " + TABLE_ROOT_EXTRA + } " ON " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL AND " + private static String createJoinFromClosure( TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + String table1, String table2, String column1, String column2) { "=" + return table1 + " INNER JOIN " + table2 + TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID; " ON " + table1 + "." + column1 + " = " + table2 + "." + column2; } } }
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +0 −3 Original line number Original line Diff line number Diff line Loading @@ -190,9 +190,6 @@ public class MtpDocumentsProvider extends DocumentsProvider { getDocumentLoader(parentIdentifier).clearTask(parentIdentifier); getDocumentLoader(parentIdentifier).clearTask(parentIdentifier); notifyChildDocumentsChange(parentIdentifier.mDocumentId); notifyChildDocumentsChange(parentIdentifier.mDocumentId); } catch (IOException error) { } catch (IOException error) { for (final StackTraceElement element : error.getStackTrace()) { Log.e("hirono", element.toString()); } throw new FileNotFoundException(error.getMessage()); throw new FileNotFoundException(error.getMessage()); } } } } Loading
packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java +27 −8 Original line number Original line Diff line number Diff line Loading @@ -7,8 +7,11 @@ import android.net.Uri; import android.os.Process; import android.os.Process; import android.provider.DocumentsContract; import android.provider.DocumentsContract; import android.util.Log; import android.util.Log; import android.util.SparseArray; import java.io.IOException; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask; Loading Loading @@ -106,26 +109,42 @@ final class RootScanner { int pollingCount = 0; int pollingCount = 0; while (!Thread.interrupted()) { while (!Thread.interrupted()) { final int[] deviceIds = mManager.getOpenedDeviceIds(); final int[] deviceIds = mManager.getOpenedDeviceIds(); if (deviceIds.length == 0) { final Map<String, MtpRoot[]> rootsMap = new HashMap<>(); return; } boolean changed = false; boolean changed = false; // Update devices. mDatabase.getMapper().startAddingDocuments(null /* parentDocumentId */); mDatabase.getMapper().startAddingDocuments(null /* parentDocumentId */); for (int deviceId : deviceIds) { for (final int deviceId : deviceIds) { try { try { final MtpRoot[] roots = mManager.getRoots(deviceId); final MtpRoot[] roots = mManager.getRoots(deviceId); if (mDatabase.getMapper().putRootDocuments(deviceId, mResources, roots)) { final String id = mDatabase.getMapper().putDeviceDocument( deviceId, mManager.getDeviceName(deviceId), roots); if (id != null) { changed = true; changed = true; rootsMap.put(id, roots); } } } catch (IOException | SQLiteException exception) { } catch (IOException exception) { // The error may happen on the device. We would like to continue getting // The error may happen on the device. We would like to continue getting // roots for other devices. // roots for other devices. Log.e(MtpDocumentsProvider.TAG, exception.getMessage()); Log.e(MtpDocumentsProvider.TAG, exception.getMessage()); } } } } if (mDatabase.getMapper().stopAddingDocuments(null /* parentDocumentId */)) { mDatabase.getMapper().stopAddingDocuments(null /* parentDocumentId */); // Update roots. for (final String documentId : rootsMap.keySet()) { mDatabase.getMapper().startAddingDocuments(documentId); if (mDatabase.getMapper().putRootDocuments( documentId, mResources, rootsMap.get(documentId))) { changed = true; } if (mDatabase.getMapper().stopAddingDocuments(documentId)) { changed = true; changed = true; } } } if (changed) { if (changed) { notifyChange(); notifyChange(); } } Loading