Loading packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java +1 −1 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ class DocumentLoader { int parentHandle = parent.mObjectHandle; // Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to // getObjectHandles if we would like to obtain children under the root. if (parentHandle == Identifier.DUMMY_HANDLE_FOR_ROOT) { if (parent.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE) { parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN; } // TODO: Handle nit race around here. Loading packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java +7 −5 Original line number Diff line number Diff line Loading @@ -17,23 +17,25 @@ package com.android.mtp; import java.util.Objects; import static com.android.mtp.MtpDatabaseConstants.DocumentType; /** * Static utilities for ID. */ class Identifier { final static int DUMMY_HANDLE_FOR_ROOT = 0; final int mDeviceId; final int mStorageId; final int mObjectHandle; final String mDocumentId; final @DocumentType int mDocumentType; Identifier(int deviceId, int storageId, int objectHandle, String documentId) { Identifier(int deviceId, int storageId, int objectHandle, String documentId, @DocumentType int documentType) { mDeviceId = deviceId; mStorageId = storageId; mObjectHandle = objectHandle; mDocumentId = documentId; mDocumentType = documentType; } @Override Loading @@ -42,7 +44,7 @@ class Identifier { return false; final Identifier other = (Identifier) obj; return mDeviceId == other.mDeviceId && mStorageId == other.mStorageId && mObjectHandle == other.mObjectHandle; mObjectHandle == other.mObjectHandle && mDocumentId == other.mDocumentId; } @Override Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +86 −48 Original line number Diff line number Diff line Loading @@ -143,11 +143,7 @@ class MtpDatabase { null); try { values.clear(); if (storageCursor.getCount() == 1) { storageCursor.moveToNext(); DatabaseUtils.cursorRowToContentValues(storageCursor, values); } else { final Cursor cursor = builder.query( try (final Cursor deviceRoot = builder.query( mDatabase, columnNames, selection + " AND " + COLUMN_DEVICE_ID + " = ?", Loading @@ -157,27 +153,26 @@ class MtpDatabase { deviceId), null, null, null); try { cursor.moveToNext(); DatabaseUtils.cursorRowToContentValues(cursor, values); } finally { cursor.close(); null)) { deviceRoot.moveToNext(); DatabaseUtils.cursorRowToContentValues(deviceRoot, values); } if (storageCursor.getCount() != 0) { long capacityBytes = 0; long availableBytes = 0; int capacityIndex = cursor.getColumnIndex(Root.COLUMN_CAPACITY_BYTES); int availableIndex = cursor.getColumnIndex(Root.COLUMN_AVAILABLE_BYTES); final int capacityIndex = storageCursor.getColumnIndex(Root.COLUMN_CAPACITY_BYTES); final int availableIndex = storageCursor.getColumnIndex(Root.COLUMN_AVAILABLE_BYTES); while (storageCursor.moveToNext()) { // If requested columnNames does not include COLUMN_XXX_BYTES, we // don't calculate corresponding values. if (capacityIndex != -1) { capacityBytes += cursor.getLong(capacityIndex); capacityBytes += storageCursor.getLong(capacityIndex); } if (availableIndex != -1) { availableBytes += cursor.getLong(availableIndex); availableBytes += storageCursor.getLong(availableIndex); } } values.put(Root.COLUMN_CAPACITY_BYTES, capacityBytes); Loading @@ -186,6 +181,12 @@ class MtpDatabase { values.putNull(Root.COLUMN_CAPACITY_BYTES); values.putNull(Root.COLUMN_AVAILABLE_BYTES); } if (storageCursor.getCount() == 1 && values.containsKey(Root.COLUMN_TITLE)) { storageCursor.moveToFirst(); values.put( Root.COLUMN_TITLE, storageCursor.getString( storageCursor.getColumnIndex(Root.COLUMN_TITLE))); } } finally { storageCursor.close(); Loading Loading @@ -238,13 +239,46 @@ class MtpDatabase { null); } /** * Returns identifier of single storage if given document points device and it has only one * storage. Otherwise null. * * @param documentId Document ID that may point a device. * @return Identifier for single storage or null. * @throws FileNotFoundException The given document ID is not registered in database. */ @Nullable Identifier getSingleStorageIdentifier(String documentId) throws FileNotFoundException { // Check if the parent document is device that has single storage. try (final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(Document.COLUMN_DOCUMENT_ID), COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_PARENT_DOCUMENT_ID + " = ? AND " + COLUMN_DOCUMENT_TYPE + " = ?", strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, documentId, DOCUMENT_TYPE_STORAGE), null, null, null)) { if (cursor.getCount() == 1) { cursor.moveToNext(); return createIdentifier(cursor.getString(0)); } else { return null; } } } /** * Queries a single document. * @param documentId * @param projection * @return Database cursor. */ public Cursor queryDocument(String documentId, String[] projection) { Cursor queryDocument(String documentId, String[] projection) { return mDatabase.query( TABLE_DOCUMENTS, projection, Loading Loading @@ -287,12 +321,12 @@ class MtpDatabase { } /** * Obtains parent document ID. * Obtains parent identifier. * @param documentId * @return parent document ID. * @return parent identifier. * @throws FileNotFoundException */ String getParentId(String documentId) throws FileNotFoundException { Identifier getParentIdentifier(String documentId) throws FileNotFoundException { final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(COLUMN_PARENT_DOCUMENT_ID), Loading @@ -304,7 +338,7 @@ class MtpDatabase { "1"); try { if (cursor.moveToNext()) { return cursor.getString(0); return createIdentifier(cursor.getString(0)); } else { throw new FileNotFoundException("Cannot find a row having ID=" + documentId); } Loading Loading @@ -352,7 +386,10 @@ class MtpDatabase { // Currently documentId is old format. final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(COLUMN_DEVICE_ID, COLUMN_STORAGE_ID, COLUMN_OBJECT_HANDLE), strings(COLUMN_DEVICE_ID, COLUMN_STORAGE_ID, COLUMN_OBJECT_HANDLE, COLUMN_DOCUMENT_TYPE), SELECTION_DOCUMENT_ID, strings(documentId), null, Loading @@ -367,8 +404,9 @@ class MtpDatabase { return new Identifier( cursor.getInt(0), cursor.getInt(1), cursor.isNull(2) ? Identifier.DUMMY_HANDLE_FOR_ROOT : cursor.getInt(2), documentId); cursor.getInt(2), documentId, cursor.getInt(3)); } } finally { cursor.close(); Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java +7 −0 Original line number Diff line number Diff line Loading @@ -16,10 +16,13 @@ package com.android.mtp; import android.annotation.IntDef; import android.database.sqlite.SQLiteQueryBuilder; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.HashMap; import java.util.Map; Loading Loading @@ -92,6 +95,10 @@ class MtpDatabaseConstants { */ static final int MAP_BY_NAME = 1; @IntDef(value = { DOCUMENT_TYPE_DEVICE, DOCUMENT_TYPE_STORAGE, DOCUMENT_TYPE_OBJECT }) @Retention(RetentionPolicy.SOURCE) public @interface DocumentType {} /** * Document that represents a MTP device. */ Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +24 −4 Original line number Diff line number Diff line Loading @@ -150,11 +150,22 @@ public class MtpDocumentsProvider extends DocumentsProvider { if (projection == null) { projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION; } final Identifier parentIdentifier = mDatabase.createIdentifier(parentDocumentId); Identifier parentIdentifier = mDatabase.createIdentifier(parentDocumentId); try { if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) { final Identifier singleStorageIdentifier = mDatabase.getSingleStorageIdentifier(parentDocumentId); if (singleStorageIdentifier == null) { // Returns storage list from database. return mDatabase.queryChildDocuments(projection, parentDocumentId); } parentIdentifier = singleStorageIdentifier; } // Returns object list from document loader. return getDocumentLoader(parentIdentifier).queryChildDocuments( projection, parentIdentifier); } catch (IOException exception) { Log.e(MtpDocumentsProvider.TAG, "queryChildDocuments", exception); throw new FileNotFoundException(exception.getMessage()); } } Loading Loading @@ -190,6 +201,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { throw new IllegalArgumentException("Unknown mode for openDocument: " + mode); } } catch (IOException error) { Log.e(MtpDocumentsProvider.TAG, "openDocument", error); throw new FileNotFoundException(error.getMessage()); } } Loading @@ -206,6 +218,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { 0, // Start offset. AssetFileDescriptor.UNKNOWN_LENGTH); } catch (IOException error) { Log.e(MtpDocumentsProvider.TAG, "openDocumentThumbnail", error); throw new FileNotFoundException(error.getMessage()); } } Loading @@ -214,13 +227,20 @@ public class MtpDocumentsProvider extends DocumentsProvider { public void deleteDocument(String documentId) throws FileNotFoundException { try { final Identifier identifier = mDatabase.createIdentifier(documentId); final Identifier parentIdentifier = mDatabase.createIdentifier(mDatabase.getParentId(documentId)); final Identifier parentIdentifier = mDatabase.getParentIdentifier(documentId); mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle); mDatabase.deleteDocument(documentId); getDocumentLoader(parentIdentifier).clearTask(parentIdentifier); notifyChildDocumentsChange(parentIdentifier.mDocumentId); if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE) { // If the parent is storage, the object might be appeared as child of device because // we skip storage when the device has only one storage. final Identifier deviceIdentifier = mDatabase.getParentIdentifier( parentIdentifier.mDocumentId); notifyChildDocumentsChange(deviceIdentifier.mDocumentId); } } catch (IOException error) { Log.e(MtpDocumentsProvider.TAG, "deleteDocument", error); throw new FileNotFoundException(error.getMessage()); } } Loading Loading @@ -259,7 +279,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { notifyChildDocumentsChange(parentDocumentId); return documentId; } catch (IOException error) { Log.e(TAG, error.getMessage()); Log.e(TAG, "createDocument", error); throw new FileNotFoundException(error.getMessage()); } } Loading Loading
packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java +1 −1 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ class DocumentLoader { int parentHandle = parent.mObjectHandle; // Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to // getObjectHandles if we would like to obtain children under the root. if (parentHandle == Identifier.DUMMY_HANDLE_FOR_ROOT) { if (parent.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE) { parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN; } // TODO: Handle nit race around here. Loading
packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java +7 −5 Original line number Diff line number Diff line Loading @@ -17,23 +17,25 @@ package com.android.mtp; import java.util.Objects; import static com.android.mtp.MtpDatabaseConstants.DocumentType; /** * Static utilities for ID. */ class Identifier { final static int DUMMY_HANDLE_FOR_ROOT = 0; final int mDeviceId; final int mStorageId; final int mObjectHandle; final String mDocumentId; final @DocumentType int mDocumentType; Identifier(int deviceId, int storageId, int objectHandle, String documentId) { Identifier(int deviceId, int storageId, int objectHandle, String documentId, @DocumentType int documentType) { mDeviceId = deviceId; mStorageId = storageId; mObjectHandle = objectHandle; mDocumentId = documentId; mDocumentType = documentType; } @Override Loading @@ -42,7 +44,7 @@ class Identifier { return false; final Identifier other = (Identifier) obj; return mDeviceId == other.mDeviceId && mStorageId == other.mStorageId && mObjectHandle == other.mObjectHandle; mObjectHandle == other.mObjectHandle && mDocumentId == other.mDocumentId; } @Override Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +86 −48 Original line number Diff line number Diff line Loading @@ -143,11 +143,7 @@ class MtpDatabase { null); try { values.clear(); if (storageCursor.getCount() == 1) { storageCursor.moveToNext(); DatabaseUtils.cursorRowToContentValues(storageCursor, values); } else { final Cursor cursor = builder.query( try (final Cursor deviceRoot = builder.query( mDatabase, columnNames, selection + " AND " + COLUMN_DEVICE_ID + " = ?", Loading @@ -157,27 +153,26 @@ class MtpDatabase { deviceId), null, null, null); try { cursor.moveToNext(); DatabaseUtils.cursorRowToContentValues(cursor, values); } finally { cursor.close(); null)) { deviceRoot.moveToNext(); DatabaseUtils.cursorRowToContentValues(deviceRoot, values); } if (storageCursor.getCount() != 0) { long capacityBytes = 0; long availableBytes = 0; int capacityIndex = cursor.getColumnIndex(Root.COLUMN_CAPACITY_BYTES); int availableIndex = cursor.getColumnIndex(Root.COLUMN_AVAILABLE_BYTES); final int capacityIndex = storageCursor.getColumnIndex(Root.COLUMN_CAPACITY_BYTES); final int availableIndex = storageCursor.getColumnIndex(Root.COLUMN_AVAILABLE_BYTES); while (storageCursor.moveToNext()) { // If requested columnNames does not include COLUMN_XXX_BYTES, we // don't calculate corresponding values. if (capacityIndex != -1) { capacityBytes += cursor.getLong(capacityIndex); capacityBytes += storageCursor.getLong(capacityIndex); } if (availableIndex != -1) { availableBytes += cursor.getLong(availableIndex); availableBytes += storageCursor.getLong(availableIndex); } } values.put(Root.COLUMN_CAPACITY_BYTES, capacityBytes); Loading @@ -186,6 +181,12 @@ class MtpDatabase { values.putNull(Root.COLUMN_CAPACITY_BYTES); values.putNull(Root.COLUMN_AVAILABLE_BYTES); } if (storageCursor.getCount() == 1 && values.containsKey(Root.COLUMN_TITLE)) { storageCursor.moveToFirst(); values.put( Root.COLUMN_TITLE, storageCursor.getString( storageCursor.getColumnIndex(Root.COLUMN_TITLE))); } } finally { storageCursor.close(); Loading Loading @@ -238,13 +239,46 @@ class MtpDatabase { null); } /** * Returns identifier of single storage if given document points device and it has only one * storage. Otherwise null. * * @param documentId Document ID that may point a device. * @return Identifier for single storage or null. * @throws FileNotFoundException The given document ID is not registered in database. */ @Nullable Identifier getSingleStorageIdentifier(String documentId) throws FileNotFoundException { // Check if the parent document is device that has single storage. try (final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(Document.COLUMN_DOCUMENT_ID), COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_PARENT_DOCUMENT_ID + " = ? AND " + COLUMN_DOCUMENT_TYPE + " = ?", strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, documentId, DOCUMENT_TYPE_STORAGE), null, null, null)) { if (cursor.getCount() == 1) { cursor.moveToNext(); return createIdentifier(cursor.getString(0)); } else { return null; } } } /** * Queries a single document. * @param documentId * @param projection * @return Database cursor. */ public Cursor queryDocument(String documentId, String[] projection) { Cursor queryDocument(String documentId, String[] projection) { return mDatabase.query( TABLE_DOCUMENTS, projection, Loading Loading @@ -287,12 +321,12 @@ class MtpDatabase { } /** * Obtains parent document ID. * Obtains parent identifier. * @param documentId * @return parent document ID. * @return parent identifier. * @throws FileNotFoundException */ String getParentId(String documentId) throws FileNotFoundException { Identifier getParentIdentifier(String documentId) throws FileNotFoundException { final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(COLUMN_PARENT_DOCUMENT_ID), Loading @@ -304,7 +338,7 @@ class MtpDatabase { "1"); try { if (cursor.moveToNext()) { return cursor.getString(0); return createIdentifier(cursor.getString(0)); } else { throw new FileNotFoundException("Cannot find a row having ID=" + documentId); } Loading Loading @@ -352,7 +386,10 @@ class MtpDatabase { // Currently documentId is old format. final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(COLUMN_DEVICE_ID, COLUMN_STORAGE_ID, COLUMN_OBJECT_HANDLE), strings(COLUMN_DEVICE_ID, COLUMN_STORAGE_ID, COLUMN_OBJECT_HANDLE, COLUMN_DOCUMENT_TYPE), SELECTION_DOCUMENT_ID, strings(documentId), null, Loading @@ -367,8 +404,9 @@ class MtpDatabase { return new Identifier( cursor.getInt(0), cursor.getInt(1), cursor.isNull(2) ? Identifier.DUMMY_HANDLE_FOR_ROOT : cursor.getInt(2), documentId); cursor.getInt(2), documentId, cursor.getInt(3)); } } finally { cursor.close(); Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java +7 −0 Original line number Diff line number Diff line Loading @@ -16,10 +16,13 @@ package com.android.mtp; import android.annotation.IntDef; import android.database.sqlite.SQLiteQueryBuilder; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.HashMap; import java.util.Map; Loading Loading @@ -92,6 +95,10 @@ class MtpDatabaseConstants { */ static final int MAP_BY_NAME = 1; @IntDef(value = { DOCUMENT_TYPE_DEVICE, DOCUMENT_TYPE_STORAGE, DOCUMENT_TYPE_OBJECT }) @Retention(RetentionPolicy.SOURCE) public @interface DocumentType {} /** * Document that represents a MTP device. */ Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +24 −4 Original line number Diff line number Diff line Loading @@ -150,11 +150,22 @@ public class MtpDocumentsProvider extends DocumentsProvider { if (projection == null) { projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION; } final Identifier parentIdentifier = mDatabase.createIdentifier(parentDocumentId); Identifier parentIdentifier = mDatabase.createIdentifier(parentDocumentId); try { if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) { final Identifier singleStorageIdentifier = mDatabase.getSingleStorageIdentifier(parentDocumentId); if (singleStorageIdentifier == null) { // Returns storage list from database. return mDatabase.queryChildDocuments(projection, parentDocumentId); } parentIdentifier = singleStorageIdentifier; } // Returns object list from document loader. return getDocumentLoader(parentIdentifier).queryChildDocuments( projection, parentIdentifier); } catch (IOException exception) { Log.e(MtpDocumentsProvider.TAG, "queryChildDocuments", exception); throw new FileNotFoundException(exception.getMessage()); } } Loading Loading @@ -190,6 +201,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { throw new IllegalArgumentException("Unknown mode for openDocument: " + mode); } } catch (IOException error) { Log.e(MtpDocumentsProvider.TAG, "openDocument", error); throw new FileNotFoundException(error.getMessage()); } } Loading @@ -206,6 +218,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { 0, // Start offset. AssetFileDescriptor.UNKNOWN_LENGTH); } catch (IOException error) { Log.e(MtpDocumentsProvider.TAG, "openDocumentThumbnail", error); throw new FileNotFoundException(error.getMessage()); } } Loading @@ -214,13 +227,20 @@ public class MtpDocumentsProvider extends DocumentsProvider { public void deleteDocument(String documentId) throws FileNotFoundException { try { final Identifier identifier = mDatabase.createIdentifier(documentId); final Identifier parentIdentifier = mDatabase.createIdentifier(mDatabase.getParentId(documentId)); final Identifier parentIdentifier = mDatabase.getParentIdentifier(documentId); mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle); mDatabase.deleteDocument(documentId); getDocumentLoader(parentIdentifier).clearTask(parentIdentifier); notifyChildDocumentsChange(parentIdentifier.mDocumentId); if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE) { // If the parent is storage, the object might be appeared as child of device because // we skip storage when the device has only one storage. final Identifier deviceIdentifier = mDatabase.getParentIdentifier( parentIdentifier.mDocumentId); notifyChildDocumentsChange(deviceIdentifier.mDocumentId); } } catch (IOException error) { Log.e(MtpDocumentsProvider.TAG, "deleteDocument", error); throw new FileNotFoundException(error.getMessage()); } } Loading Loading @@ -259,7 +279,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { notifyChildDocumentsChange(parentDocumentId); return documentId; } catch (IOException error) { Log.e(TAG, error.getMessage()); Log.e(TAG, "createDocument", error); throw new FileNotFoundException(error.getMessage()); } } Loading