Loading packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java +79 −41 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ package com.android.mtp; import android.content.ContentResolver; import android.database.Cursor; import android.database.MatrixCursor; import android.database.sqlite.SQLiteException; import android.mtp.MtpObjectInfo; import android.net.Uri; import android.os.Bundle; Loading @@ -26,6 +26,7 @@ import android.os.Process; import android.provider.DocumentsContract; import android.util.Log; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; import java.util.Date; Loading @@ -44,12 +45,14 @@ class DocumentLoader { private final MtpManager mMtpManager; private final ContentResolver mResolver; private final MtpDatabase mDatabase; private final TaskList mTaskList = new TaskList(); private boolean mHasBackgroundThread = false; DocumentLoader(MtpManager mtpManager, ContentResolver resolver) { DocumentLoader(MtpManager mtpManager, ContentResolver resolver, MtpDatabase database) { mMtpManager = mtpManager; mResolver = resolver; mDatabase = database; } private static MtpObjectInfo[] loadDocuments(MtpManager manager, int deviceId, int[] handles) Loading @@ -65,13 +68,17 @@ class DocumentLoader { throws IOException { LoaderTask task = mTaskList.findTask(parent); if (task == null) { if (parent.mDocumentId == null) { throw new FileNotFoundException("Parent not found."); } 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 == CursorHelper.DUMMY_HANDLE_FOR_ROOT) { parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN; } task = new LoaderTask(parent, mMtpManager.getObjectHandles( task = new LoaderTask(mDatabase, parent, mMtpManager.getObjectHandles( parent.mDeviceId, parent.mStorageId, parentHandle)); task.fillDocuments(loadDocuments( mMtpManager, Loading @@ -83,11 +90,10 @@ class DocumentLoader { } mTaskList.addFirst(task); if (!task.completed() && !mHasBackgroundThread) { if (task.getState() == LoaderTask.STATE_LOADING && !mHasBackgroundThread) { mHasBackgroundThread = true; new BackgroundLoaderThread().start(); } return task.createCursor(mResolver, columnNames); } Loading Loading @@ -120,26 +126,20 @@ class DocumentLoader { deviceId = task.mIdentifier.mDeviceId; handles = task.getUnloadedObjectHandles(NUM_LOADING_ENTRIES); } MtpObjectInfo[] objectInfos; try { objectInfos = loadDocuments(mMtpManager, deviceId, handles); } catch (IOException exception) { objectInfos = null; Log.d(MtpDocumentsProvider.TAG, exception.getMessage()); } synchronized (DocumentLoader.this) { if (objectInfos != null) { final MtpObjectInfo[] objectInfos = loadDocuments(mMtpManager, deviceId, handles); task.fillDocuments(objectInfos); final boolean shouldNotify = task.mLastNotified.getTime() < new Date().getTime() - NOTIFY_PERIOD_MS || task.completed(); task.getState() != LoaderTask.STATE_LOADING; if (shouldNotify) { task.notify(mResolver); } } else { mTaskList.remove(task); } } catch (IOException exception) { task.setError(exception); } } } Loading @@ -156,7 +156,7 @@ class DocumentLoader { LoaderTask findRunningTask() { for (int i = 0; i < size(); i++) { if (!get(i).completed()) if (get(i).getState() == LoaderTask.STATE_LOADING) return get(i); } return null; Loading @@ -165,7 +165,7 @@ class DocumentLoader { void clearCompletedTasks() { int i = 0; while (i < size()) { if (get(i).completed()) { if (get(i).getState() == LoaderTask.STATE_COMPLETED) { remove(i); } else { i++; Loading @@ -186,36 +186,51 @@ class DocumentLoader { } private static class LoaderTask { static final int STATE_LOADING = 0; static final int STATE_COMPLETED = 1; static final int STATE_ERROR = 2; final MtpDatabase mDatabase; final Identifier mIdentifier; final int[] mObjectHandles; final MtpObjectInfo[] mObjectInfos; Date mLastNotified; int mNumLoaded; Exception mError; LoaderTask(Identifier identifier, int[] objectHandles) { LoaderTask(MtpDatabase database, Identifier identifier, int[] objectHandles) { mDatabase = database; mIdentifier = identifier; mObjectHandles = objectHandles; mObjectInfos = new MtpObjectInfo[mObjectHandles.length]; mNumLoaded = 0; mLastNotified = new Date(); } Cursor createCursor(ContentResolver resolver, String[] columnNames) { final MatrixCursor cursor = new MatrixCursor(columnNames); final Identifier rootIdentifier = new Identifier( mIdentifier.mDeviceId, mIdentifier.mStorageId); for (int i = 0; i < mNumLoaded; i++) { CursorHelper.addToCursor(mObjectInfos[i], rootIdentifier, cursor.newRow()); } Cursor createCursor(ContentResolver resolver, String[] columnNames) throws IOException { final Bundle extras = new Bundle(); extras.putBoolean(DocumentsContract.EXTRA_LOADING, !completed()); switch (getState()) { case STATE_LOADING: extras.putBoolean(DocumentsContract.EXTRA_LOADING, true); break; case STATE_ERROR: throw new IOException(mError); } final Cursor cursor = mDatabase.queryChildDocuments( columnNames, mIdentifier.mDocumentId, /* use old ID format */ true); cursor.setNotificationUri(resolver, createUri()); cursor.respond(extras); return cursor; } boolean completed() { return mNumLoaded == mObjectInfos.length; int getState() { if (mError != null) { return STATE_ERROR; } else if (mNumLoaded == mObjectHandles.length) { return STATE_COMPLETED; } else { return STATE_LOADING; } } int[] getUnloadedObjectHandles(int count) { Loading @@ -230,9 +245,32 @@ class DocumentLoader { mLastNotified = new Date(); } void fillDocuments(MtpObjectInfo[] objectInfos) { for (int i = 0; i < objectInfos.length; i++) { mObjectInfos[mNumLoaded++] = objectInfos[i]; void fillDocuments(MtpObjectInfo[] objectInfoList) { if (objectInfoList.length == 0 || getState() != STATE_LOADING) { return; } if (mNumLoaded == 0) { mDatabase.startAddingChildDocuments(mIdentifier.mDocumentId); } try { mDatabase.putChildDocuments( mIdentifier.mDeviceId, mIdentifier.mDocumentId, objectInfoList); mNumLoaded += objectInfoList.length; } catch (SQLiteException exp) { mError = exp; mNumLoaded = 0; } if (getState() != STATE_LOADING) { mDatabase.stopAddingChildDocuments(mIdentifier.mDocumentId); } } void setError(Exception message) { final int lastState = getState(); mError = message; mNumLoaded = 0; if (lastState == STATE_LOADING) { mDatabase.stopAddingChildDocuments(mIdentifier.mDocumentId); } } Loading packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java +6 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ class Identifier { final int mDeviceId; final int mStorageId; final int mObjectHandle; final String mDocumentId; static Identifier createFromRootId(String rootId) { final String[] components = rootId.split("_"); Loading @@ -45,9 +46,14 @@ class Identifier { } Identifier(int deviceId, int storageId, int objectHandle) { this(deviceId, storageId, objectHandle, null); } Identifier(int deviceId, int storageId, int objectHandle, String documentId) { mDeviceId = deviceId; mStorageId = storageId; mObjectHandle = objectHandle; mDocumentId = documentId; } // TODO: Make the ID persistent. Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +33 −6 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Context; import android.content.res.Resources; import android.database.Cursor; import android.mtp.MtpObjectInfo; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; Loading Loading @@ -79,8 +80,8 @@ class MtpDatabase { private final Map<String, Integer> mMappingMode = new HashMap<>(); @VisibleForTesting MtpDatabase(Context context, boolean inMemory) { mDatabase = new MtpDatabaseInternal(context, inMemory); MtpDatabase(Context context, int flags) { mDatabase = new MtpDatabaseInternal(context, flags); } /** Loading Loading @@ -111,7 +112,29 @@ class MtpDatabase { */ @VisibleForTesting Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) { return mDatabase.queryChildDocuments(columnNames, parentDocumentId); return queryChildDocuments(columnNames, parentDocumentId, false); } @VisibleForTesting Cursor queryChildDocuments(String[] columnNames, String parentDocumentId, boolean useOldId) { final String[] newColumnNames = new String[columnNames.length]; // TODO: Temporary replace document ID with old format. for (int i = 0; i < columnNames.length; i++) { if (useOldId && DocumentsContract.Document.COLUMN_DOCUMENT_ID.equals(columnNames[i])) { newColumnNames[i] = COLUMN_DEVICE_ID + " || '_' || " + COLUMN_STORAGE_ID + " || '_' || IFNULL(" + COLUMN_OBJECT_HANDLE + ",0) AS " + DocumentsContract.Document.COLUMN_DOCUMENT_ID; } else { newColumnNames[i] = columnNames[i]; } } return mDatabase.queryChildDocuments(newColumnNames, parentDocumentId); } Identifier createIdentifier(String parentDocumentId) { return mDatabase.createIdentifier(parentDocumentId); } /** Loading Loading @@ -193,9 +216,13 @@ class MtpDatabase { int i = 0; for (final MtpRoot root : roots) { // Use the same value for the root ID and the corresponding document ID. values.put( Root.COLUMN_ROOT_ID, valuesList[i++].getAsString(Document.COLUMN_DOCUMENT_ID)); final String documentId = valuesList[i++].getAsString(Document.COLUMN_DOCUMENT_ID); // If it fails to insert/update documents, the document ID will be set with -1. // In this case we don't insert/update root extra information neither. if (documentId == null) { continue; } values.put(Root.COLUMN_ROOT_ID, documentId); values.put( Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE); Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java +3 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ class MtpDatabaseConstants { static final int DATABASE_VERSION = 1; static final String DATABASE_NAME = null; static final int FLAG_DATABASE_IN_MEMORY = 1; static final int FLAG_DATABASE_IN_FILE = 0; /** * Table representing documents including root documents. */ Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseInternal.java +92 −21 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Context; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.provider.DocumentsContract.Document; Loading @@ -35,8 +36,11 @@ import java.util.Objects; */ class MtpDatabaseInternal { private static class OpenHelper extends SQLiteOpenHelper { public OpenHelper(Context context, boolean inMemory) { super(context, inMemory ? null : DATABASE_NAME, null, DATABASE_VERSION); public OpenHelper(Context context, int flags) { super(context, flags == FLAG_DATABASE_IN_MEMORY ? null : DATABASE_NAME, null, DATABASE_VERSION); } @Override Loading @@ -54,8 +58,8 @@ class MtpDatabaseInternal { private final SQLiteDatabase mDatabase; MtpDatabaseInternal(Context context, boolean inMemory) { final OpenHelper helper = new OpenHelper(context, inMemory); MtpDatabaseInternal(Context context, int flags) { final OpenHelper helper = new OpenHelper(context, flags); mDatabase = helper.getWritableDatabase(); } Loading Loading @@ -121,6 +125,64 @@ class MtpDatabaseInternal { deleteDocumentsAndRoots(COLUMN_DEVICE_ID + "=?", strings(deviceId)); } /** * Gets identifier from document ID. * @param documentId Document ID. * @return Identifier. */ Identifier createIdentifier(String documentId) { // Currently documentId is old format. final Identifier oldIdentifier = Identifier.createFromDocumentId(documentId); final String selection; final String[] args; if (oldIdentifier.mObjectHandle == CursorHelper.DUMMY_HANDLE_FOR_ROOT) { selection = COLUMN_DEVICE_ID + "= ? AND " + COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_STORAGE_ID + "= ? AND " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; args = strings( oldIdentifier.mDeviceId, ROW_STATE_VALID, ROW_STATE_INVALIDATED, oldIdentifier.mStorageId); } else { selection = COLUMN_DEVICE_ID + "= ? AND " + COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_STORAGE_ID + "= ? AND " + COLUMN_OBJECT_HANDLE + " = ?"; args = strings( oldIdentifier.mDeviceId, ROW_STATE_VALID, ROW_STATE_INVALIDATED, oldIdentifier.mStorageId, oldIdentifier.mObjectHandle); } final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(Document.COLUMN_DOCUMENT_ID), selection, args, null, null, null, "1"); try { if (cursor.getCount() == 0) { return oldIdentifier; } else { cursor.moveToNext(); return new Identifier( oldIdentifier.mDeviceId, oldIdentifier.mStorageId, oldIdentifier.mObjectHandle, cursor.getString(0)); } } finally { cursor.close(); } } /** * Starts adding new documents. * The methods decides mapping mode depends on if all documents under the given parent have MTP Loading Loading @@ -164,7 +226,7 @@ class MtpDatabaseInternal { * {@link #stopAddingDocuments(String, String, String)} turns the pending rows into 'valid' * rows. If the methods adds rows to database, it updates valueList with correct document ID. * * @param valuesList Values that are stored in the database. * @param valuesList Values for documents to be stored in the database. * @param selection SQL where closure to select rows that shares the same parent. * @param arg Argument for selection SQL. * @param heuristic Whether the mapping mode is heuristic. Loading @@ -191,15 +253,22 @@ class MtpDatabaseInternal { null, null, "1"); try { final long rowId; if (candidateCursor.getCount() == 0) { rowId = mDatabase.insert(TABLE_DOCUMENTS, null, values); if (rowId == -1) { throw new SQLiteException("Failed to put a document into database."); } added = true; } else if (!heuristic) { candidateCursor.moveToNext(); final String documentId = candidateCursor.getString(0); rowId = mDatabase.update( TABLE_DOCUMENTS, values, SELECTION_DOCUMENT_ID, strings(documentId)); TABLE_DOCUMENTS, values, SELECTION_DOCUMENT_ID, strings(documentId)); } else { values.put(COLUMN_ROW_STATE, ROW_STATE_PENDING); rowId = mDatabase.insert(TABLE_DOCUMENTS, null, values); Loading @@ -207,8 +276,10 @@ class MtpDatabaseInternal { // Document ID is a primary integer key of the table. So the returned row // IDs should be same with the document ID. values.put(Document.COLUMN_DOCUMENT_ID, rowId); } finally { candidateCursor.close(); } } mDatabase.setTransactionSuccessful(); return added; Loading Loading
packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java +79 −41 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ package com.android.mtp; import android.content.ContentResolver; import android.database.Cursor; import android.database.MatrixCursor; import android.database.sqlite.SQLiteException; import android.mtp.MtpObjectInfo; import android.net.Uri; import android.os.Bundle; Loading @@ -26,6 +26,7 @@ import android.os.Process; import android.provider.DocumentsContract; import android.util.Log; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; import java.util.Date; Loading @@ -44,12 +45,14 @@ class DocumentLoader { private final MtpManager mMtpManager; private final ContentResolver mResolver; private final MtpDatabase mDatabase; private final TaskList mTaskList = new TaskList(); private boolean mHasBackgroundThread = false; DocumentLoader(MtpManager mtpManager, ContentResolver resolver) { DocumentLoader(MtpManager mtpManager, ContentResolver resolver, MtpDatabase database) { mMtpManager = mtpManager; mResolver = resolver; mDatabase = database; } private static MtpObjectInfo[] loadDocuments(MtpManager manager, int deviceId, int[] handles) Loading @@ -65,13 +68,17 @@ class DocumentLoader { throws IOException { LoaderTask task = mTaskList.findTask(parent); if (task == null) { if (parent.mDocumentId == null) { throw new FileNotFoundException("Parent not found."); } 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 == CursorHelper.DUMMY_HANDLE_FOR_ROOT) { parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN; } task = new LoaderTask(parent, mMtpManager.getObjectHandles( task = new LoaderTask(mDatabase, parent, mMtpManager.getObjectHandles( parent.mDeviceId, parent.mStorageId, parentHandle)); task.fillDocuments(loadDocuments( mMtpManager, Loading @@ -83,11 +90,10 @@ class DocumentLoader { } mTaskList.addFirst(task); if (!task.completed() && !mHasBackgroundThread) { if (task.getState() == LoaderTask.STATE_LOADING && !mHasBackgroundThread) { mHasBackgroundThread = true; new BackgroundLoaderThread().start(); } return task.createCursor(mResolver, columnNames); } Loading Loading @@ -120,26 +126,20 @@ class DocumentLoader { deviceId = task.mIdentifier.mDeviceId; handles = task.getUnloadedObjectHandles(NUM_LOADING_ENTRIES); } MtpObjectInfo[] objectInfos; try { objectInfos = loadDocuments(mMtpManager, deviceId, handles); } catch (IOException exception) { objectInfos = null; Log.d(MtpDocumentsProvider.TAG, exception.getMessage()); } synchronized (DocumentLoader.this) { if (objectInfos != null) { final MtpObjectInfo[] objectInfos = loadDocuments(mMtpManager, deviceId, handles); task.fillDocuments(objectInfos); final boolean shouldNotify = task.mLastNotified.getTime() < new Date().getTime() - NOTIFY_PERIOD_MS || task.completed(); task.getState() != LoaderTask.STATE_LOADING; if (shouldNotify) { task.notify(mResolver); } } else { mTaskList.remove(task); } } catch (IOException exception) { task.setError(exception); } } } Loading @@ -156,7 +156,7 @@ class DocumentLoader { LoaderTask findRunningTask() { for (int i = 0; i < size(); i++) { if (!get(i).completed()) if (get(i).getState() == LoaderTask.STATE_LOADING) return get(i); } return null; Loading @@ -165,7 +165,7 @@ class DocumentLoader { void clearCompletedTasks() { int i = 0; while (i < size()) { if (get(i).completed()) { if (get(i).getState() == LoaderTask.STATE_COMPLETED) { remove(i); } else { i++; Loading @@ -186,36 +186,51 @@ class DocumentLoader { } private static class LoaderTask { static final int STATE_LOADING = 0; static final int STATE_COMPLETED = 1; static final int STATE_ERROR = 2; final MtpDatabase mDatabase; final Identifier mIdentifier; final int[] mObjectHandles; final MtpObjectInfo[] mObjectInfos; Date mLastNotified; int mNumLoaded; Exception mError; LoaderTask(Identifier identifier, int[] objectHandles) { LoaderTask(MtpDatabase database, Identifier identifier, int[] objectHandles) { mDatabase = database; mIdentifier = identifier; mObjectHandles = objectHandles; mObjectInfos = new MtpObjectInfo[mObjectHandles.length]; mNumLoaded = 0; mLastNotified = new Date(); } Cursor createCursor(ContentResolver resolver, String[] columnNames) { final MatrixCursor cursor = new MatrixCursor(columnNames); final Identifier rootIdentifier = new Identifier( mIdentifier.mDeviceId, mIdentifier.mStorageId); for (int i = 0; i < mNumLoaded; i++) { CursorHelper.addToCursor(mObjectInfos[i], rootIdentifier, cursor.newRow()); } Cursor createCursor(ContentResolver resolver, String[] columnNames) throws IOException { final Bundle extras = new Bundle(); extras.putBoolean(DocumentsContract.EXTRA_LOADING, !completed()); switch (getState()) { case STATE_LOADING: extras.putBoolean(DocumentsContract.EXTRA_LOADING, true); break; case STATE_ERROR: throw new IOException(mError); } final Cursor cursor = mDatabase.queryChildDocuments( columnNames, mIdentifier.mDocumentId, /* use old ID format */ true); cursor.setNotificationUri(resolver, createUri()); cursor.respond(extras); return cursor; } boolean completed() { return mNumLoaded == mObjectInfos.length; int getState() { if (mError != null) { return STATE_ERROR; } else if (mNumLoaded == mObjectHandles.length) { return STATE_COMPLETED; } else { return STATE_LOADING; } } int[] getUnloadedObjectHandles(int count) { Loading @@ -230,9 +245,32 @@ class DocumentLoader { mLastNotified = new Date(); } void fillDocuments(MtpObjectInfo[] objectInfos) { for (int i = 0; i < objectInfos.length; i++) { mObjectInfos[mNumLoaded++] = objectInfos[i]; void fillDocuments(MtpObjectInfo[] objectInfoList) { if (objectInfoList.length == 0 || getState() != STATE_LOADING) { return; } if (mNumLoaded == 0) { mDatabase.startAddingChildDocuments(mIdentifier.mDocumentId); } try { mDatabase.putChildDocuments( mIdentifier.mDeviceId, mIdentifier.mDocumentId, objectInfoList); mNumLoaded += objectInfoList.length; } catch (SQLiteException exp) { mError = exp; mNumLoaded = 0; } if (getState() != STATE_LOADING) { mDatabase.stopAddingChildDocuments(mIdentifier.mDocumentId); } } void setError(Exception message) { final int lastState = getState(); mError = message; mNumLoaded = 0; if (lastState == STATE_LOADING) { mDatabase.stopAddingChildDocuments(mIdentifier.mDocumentId); } } Loading
packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java +6 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ class Identifier { final int mDeviceId; final int mStorageId; final int mObjectHandle; final String mDocumentId; static Identifier createFromRootId(String rootId) { final String[] components = rootId.split("_"); Loading @@ -45,9 +46,14 @@ class Identifier { } Identifier(int deviceId, int storageId, int objectHandle) { this(deviceId, storageId, objectHandle, null); } Identifier(int deviceId, int storageId, int objectHandle, String documentId) { mDeviceId = deviceId; mStorageId = storageId; mObjectHandle = objectHandle; mDocumentId = documentId; } // TODO: Make the ID persistent. Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +33 −6 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Context; import android.content.res.Resources; import android.database.Cursor; import android.mtp.MtpObjectInfo; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; Loading Loading @@ -79,8 +80,8 @@ class MtpDatabase { private final Map<String, Integer> mMappingMode = new HashMap<>(); @VisibleForTesting MtpDatabase(Context context, boolean inMemory) { mDatabase = new MtpDatabaseInternal(context, inMemory); MtpDatabase(Context context, int flags) { mDatabase = new MtpDatabaseInternal(context, flags); } /** Loading Loading @@ -111,7 +112,29 @@ class MtpDatabase { */ @VisibleForTesting Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) { return mDatabase.queryChildDocuments(columnNames, parentDocumentId); return queryChildDocuments(columnNames, parentDocumentId, false); } @VisibleForTesting Cursor queryChildDocuments(String[] columnNames, String parentDocumentId, boolean useOldId) { final String[] newColumnNames = new String[columnNames.length]; // TODO: Temporary replace document ID with old format. for (int i = 0; i < columnNames.length; i++) { if (useOldId && DocumentsContract.Document.COLUMN_DOCUMENT_ID.equals(columnNames[i])) { newColumnNames[i] = COLUMN_DEVICE_ID + " || '_' || " + COLUMN_STORAGE_ID + " || '_' || IFNULL(" + COLUMN_OBJECT_HANDLE + ",0) AS " + DocumentsContract.Document.COLUMN_DOCUMENT_ID; } else { newColumnNames[i] = columnNames[i]; } } return mDatabase.queryChildDocuments(newColumnNames, parentDocumentId); } Identifier createIdentifier(String parentDocumentId) { return mDatabase.createIdentifier(parentDocumentId); } /** Loading Loading @@ -193,9 +216,13 @@ class MtpDatabase { int i = 0; for (final MtpRoot root : roots) { // Use the same value for the root ID and the corresponding document ID. values.put( Root.COLUMN_ROOT_ID, valuesList[i++].getAsString(Document.COLUMN_DOCUMENT_ID)); final String documentId = valuesList[i++].getAsString(Document.COLUMN_DOCUMENT_ID); // If it fails to insert/update documents, the document ID will be set with -1. // In this case we don't insert/update root extra information neither. if (documentId == null) { continue; } values.put(Root.COLUMN_ROOT_ID, documentId); values.put( Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE); Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java +3 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ class MtpDatabaseConstants { static final int DATABASE_VERSION = 1; static final String DATABASE_NAME = null; static final int FLAG_DATABASE_IN_MEMORY = 1; static final int FLAG_DATABASE_IN_FILE = 0; /** * Table representing documents including root documents. */ Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseInternal.java +92 −21 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Context; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.provider.DocumentsContract.Document; Loading @@ -35,8 +36,11 @@ import java.util.Objects; */ class MtpDatabaseInternal { private static class OpenHelper extends SQLiteOpenHelper { public OpenHelper(Context context, boolean inMemory) { super(context, inMemory ? null : DATABASE_NAME, null, DATABASE_VERSION); public OpenHelper(Context context, int flags) { super(context, flags == FLAG_DATABASE_IN_MEMORY ? null : DATABASE_NAME, null, DATABASE_VERSION); } @Override Loading @@ -54,8 +58,8 @@ class MtpDatabaseInternal { private final SQLiteDatabase mDatabase; MtpDatabaseInternal(Context context, boolean inMemory) { final OpenHelper helper = new OpenHelper(context, inMemory); MtpDatabaseInternal(Context context, int flags) { final OpenHelper helper = new OpenHelper(context, flags); mDatabase = helper.getWritableDatabase(); } Loading Loading @@ -121,6 +125,64 @@ class MtpDatabaseInternal { deleteDocumentsAndRoots(COLUMN_DEVICE_ID + "=?", strings(deviceId)); } /** * Gets identifier from document ID. * @param documentId Document ID. * @return Identifier. */ Identifier createIdentifier(String documentId) { // Currently documentId is old format. final Identifier oldIdentifier = Identifier.createFromDocumentId(documentId); final String selection; final String[] args; if (oldIdentifier.mObjectHandle == CursorHelper.DUMMY_HANDLE_FOR_ROOT) { selection = COLUMN_DEVICE_ID + "= ? AND " + COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_STORAGE_ID + "= ? AND " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL"; args = strings( oldIdentifier.mDeviceId, ROW_STATE_VALID, ROW_STATE_INVALIDATED, oldIdentifier.mStorageId); } else { selection = COLUMN_DEVICE_ID + "= ? AND " + COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_STORAGE_ID + "= ? AND " + COLUMN_OBJECT_HANDLE + " = ?"; args = strings( oldIdentifier.mDeviceId, ROW_STATE_VALID, ROW_STATE_INVALIDATED, oldIdentifier.mStorageId, oldIdentifier.mObjectHandle); } final Cursor cursor = mDatabase.query( TABLE_DOCUMENTS, strings(Document.COLUMN_DOCUMENT_ID), selection, args, null, null, null, "1"); try { if (cursor.getCount() == 0) { return oldIdentifier; } else { cursor.moveToNext(); return new Identifier( oldIdentifier.mDeviceId, oldIdentifier.mStorageId, oldIdentifier.mObjectHandle, cursor.getString(0)); } } finally { cursor.close(); } } /** * Starts adding new documents. * The methods decides mapping mode depends on if all documents under the given parent have MTP Loading Loading @@ -164,7 +226,7 @@ class MtpDatabaseInternal { * {@link #stopAddingDocuments(String, String, String)} turns the pending rows into 'valid' * rows. If the methods adds rows to database, it updates valueList with correct document ID. * * @param valuesList Values that are stored in the database. * @param valuesList Values for documents to be stored in the database. * @param selection SQL where closure to select rows that shares the same parent. * @param arg Argument for selection SQL. * @param heuristic Whether the mapping mode is heuristic. Loading @@ -191,15 +253,22 @@ class MtpDatabaseInternal { null, null, "1"); try { final long rowId; if (candidateCursor.getCount() == 0) { rowId = mDatabase.insert(TABLE_DOCUMENTS, null, values); if (rowId == -1) { throw new SQLiteException("Failed to put a document into database."); } added = true; } else if (!heuristic) { candidateCursor.moveToNext(); final String documentId = candidateCursor.getString(0); rowId = mDatabase.update( TABLE_DOCUMENTS, values, SELECTION_DOCUMENT_ID, strings(documentId)); TABLE_DOCUMENTS, values, SELECTION_DOCUMENT_ID, strings(documentId)); } else { values.put(COLUMN_ROW_STATE, ROW_STATE_PENDING); rowId = mDatabase.insert(TABLE_DOCUMENTS, null, values); Loading @@ -207,8 +276,10 @@ class MtpDatabaseInternal { // Document ID is a primary integer key of the table. So the returned row // IDs should be same with the document ID. values.put(Document.COLUMN_DOCUMENT_ID, rowId); } finally { candidateCursor.close(); } } mDatabase.setTransactionSuccessful(); return added; Loading