Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dc473446 authored by Daichi Hirono's avatar Daichi Hirono
Browse files

Start to use MtpDatabase in RootScanner.

Change-Id: Id99cb61ad8680529b5ee502ca5bb2b3cdd143235
parent 18d70d5b
Loading
Loading
Loading
Loading
+107 −38
Original line number Original line Diff line number Diff line
@@ -71,6 +71,11 @@ import java.util.Map;
@VisibleForTesting
@VisibleForTesting
class MtpDatabase {
class MtpDatabase {
    private final MtpDatabaseInternal mDatabase;
    private final MtpDatabaseInternal mDatabase;

    /**
     * Mapping mode for roots/documents where we start adding child documents.
     * Methods operate the state needs to be synchronized.
     */
    private final Map<String, Integer> mMappingMode = new HashMap<>();
    private final Map<String, Integer> mMappingMode = new HashMap<>();


    @VisibleForTesting
    @VisibleForTesting
@@ -78,23 +83,49 @@ class MtpDatabase {
        mDatabase = new MtpDatabaseInternal(context);
        mDatabase = new MtpDatabaseInternal(context);
    }
    }


    /**
     * Closes the database.
     */
    @VisibleForTesting
    @VisibleForTesting
    void close() {
        mDatabase.close();
    }

    /**
     * {@link MtpDatabaseInternal#queryRoots}
     */
    Cursor queryRoots(String[] columnNames) {
    Cursor queryRoots(String[] columnNames) {
        return mDatabase.queryRoots(columnNames);
        return mDatabase.queryRoots(columnNames);
    }
    }


    /**
     * {@link MtpDatabaseInternal#queryRootDocuments}
     */
    @VisibleForTesting
    @VisibleForTesting
    Cursor queryRootDocuments(String[] columnNames) {
    Cursor queryRootDocuments(String[] columnNames) {
        return mDatabase.queryRootDocuments(columnNames);
        return mDatabase.queryRootDocuments(columnNames);
    }
    }


    /**
     * {@link MtpDatabaseInternal#queryChildDocuments}
     */
    @VisibleForTesting
    @VisibleForTesting
    Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) {
    Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) {
        return mDatabase.queryChildDocuments(columnNames, parentDocumentId);
        return mDatabase.queryChildDocuments(columnNames, parentDocumentId);
    }
    }


    @VisibleForTesting
    /**
    void startAddingRootDocuments(int deviceId) {
     * {@link MtpDatabaseInternal#removeDeviceRows}
     */
    void removeDeviceRows(int deviceId) {
        mDatabase.removeDeviceRows(deviceId);
    }

    /**
     * Invokes {@link MtpDatabaseInternal#startAddingDocuments} for root documents.
     * @param deviceId Device ID.
     */
    synchronized void startAddingRootDocuments(int deviceId) {
        final String mappingStateKey = getRootDocumentsMappingStateKey(deviceId);
        final String mappingStateKey = getRootDocumentsMappingStateKey(deviceId);
        if (mMappingMode.containsKey(mappingStateKey)) {
        if (mMappingMode.containsKey(mappingStateKey)) {
            throw new Error("Mapping for the root has already started.");
            throw new Error("Mapping for the root has already started.");
@@ -105,8 +136,12 @@ class MtpDatabase {
                        SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId)));
                        SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId)));
    }
    }


    /**
     * Invokes {@link MtpDatabaseInternal#startAddingDocuments} for child of specific documents.
     * @param parentDocumentId Document ID for parent document.
     */
    @VisibleForTesting
    @VisibleForTesting
    void startAddingChildDocuments(String parentDocumentId) {
    synchronized void startAddingChildDocuments(String parentDocumentId) {
        final String mappingStateKey = getChildDocumentsMappingStateKey(parentDocumentId);
        final String mappingStateKey = getChildDocumentsMappingStateKey(parentDocumentId);
        if (mMappingMode.containsKey(mappingStateKey)) {
        if (mMappingMode.containsKey(mappingStateKey)) {
            throw new Error("Mapping for the root has already started.");
            throw new Error("Mapping for the root has already started.");
@@ -116,20 +151,18 @@ class MtpDatabase {
                mDatabase.startAddingDocuments(SELECTION_CHILD_DOCUMENTS, parentDocumentId));
                mDatabase.startAddingDocuments(SELECTION_CHILD_DOCUMENTS, parentDocumentId));
    }
    }


    @VisibleForTesting
    /**
    void putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) {
     * Puts root information to database.
     * @param deviceId Device ID
     * @param resources Resources required to localize root name.
     * @param roots List of root information.
     * @return If roots are added or removed from the database.
     */
    synchronized boolean putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) {
        mDatabase.beginTransaction();
        mDatabase.beginTransaction();
        try {
        try {
            final ContentValues[] valuesList = new ContentValues[roots.length];
            final boolean heuristic;
            for (int i = 0; i < roots.length; i++) {
            final String mapColumn;
                if (roots[i].mDeviceId != deviceId) {
                    throw new IllegalArgumentException();
                }
                valuesList[i] = new ContentValues();
                getRootDocumentValues(valuesList[i], resources, roots[i]);
            }
            boolean heuristic;
            String mapColumn;
            switch (mMappingMode.get(getRootDocumentsMappingStateKey(deviceId))) {
            switch (mMappingMode.get(getRootDocumentsMappingStateKey(deviceId))) {
                case MAP_BY_MTP_IDENTIFIER:
                case MAP_BY_MTP_IDENTIFIER:
                    heuristic = false;
                    heuristic = false;
@@ -142,7 +175,15 @@ class MtpDatabase {
                default:
                default:
                    throw new Error("Unexpected map mode.");
                    throw new Error("Unexpected map mode.");
            }
            }
            final long[] documentIds = mDatabase.putDocuments(
            final ContentValues[] valuesList = new ContentValues[roots.length];
            for (int i = 0; i < roots.length; i++) {
                if (roots[i].mDeviceId != deviceId) {
                    throw new IllegalArgumentException();
                }
                valuesList[i] = new ContentValues();
                getRootDocumentValues(valuesList[i], resources, roots[i]);
            }
            final boolean changed = mDatabase.putDocuments(
                    valuesList,
                    valuesList,
                    SELECTION_ROOT_DOCUMENTS,
                    SELECTION_ROOT_DOCUMENTS,
                    Integer.toString(deviceId),
                    Integer.toString(deviceId),
@@ -152,33 +193,38 @@ class MtpDatabase {
            int i = 0;
            int i = 0;
            for (final MtpRoot root : roots) {
            for (final MtpRoot root : roots) {
                // Use the same value for the root ID and the corresponding document ID.
                // Use the same value for the root ID and the corresponding document ID.
                values.put(Root.COLUMN_ROOT_ID, documentIds[i++]);
                values.put(
                values.put(
                        Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
                        Root.COLUMN_ROOT_ID,
                        valuesList[i++].getAsString(Document.COLUMN_DOCUMENT_ID));
                values.put(
                        Root.COLUMN_FLAGS,
                        Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
                values.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace);
                values.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace);
                values.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity);
                values.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity);
                values.put(Root.COLUMN_MIME_TYPES, "");
                values.put(Root.COLUMN_MIME_TYPES, "");
                mDatabase.putRootExtra(values);
                mDatabase.putRootExtra(values);
            }
            }
            mDatabase.setTransactionSuccessful();
            mDatabase.setTransactionSuccessful();
            return changed;
        } finally {
        } finally {
            mDatabase.endTransaction();
            mDatabase.endTransaction();
        }
        }
    }
    }


    /**
     * Puts document information to database.
     * @param deviceId Device ID
     * @param parentId Parent document ID.
     * @param documents List of document information.
     */
    @VisibleForTesting
    @VisibleForTesting
    void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
    synchronized void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
        final ContentValues[] valuesList = new ContentValues[documents.length];
        final boolean heuristic;
        for (int i = 0; i < documents.length; i++) {
        final String mapColumn;
            valuesList[i] = new ContentValues();
            getChildDocumentValues(valuesList[i], deviceId, parentId, documents[i]);
        }
        boolean heuristic;
        String mapColumn;
        switch (mMappingMode.get(getChildDocumentsMappingStateKey(parentId))) {
        switch (mMappingMode.get(getChildDocumentsMappingStateKey(parentId))) {
            case MAP_BY_MTP_IDENTIFIER:
            case MAP_BY_MTP_IDENTIFIER:
                heuristic = false;
                heuristic = false;
                mapColumn = COLUMN_STORAGE_ID;
                mapColumn = COLUMN_OBJECT_HANDLE;
                break;
                break;
            case MAP_BY_NAME:
            case MAP_BY_NAME:
                heuristic = true;
                heuristic = true;
@@ -187,40 +233,55 @@ class MtpDatabase {
            default:
            default:
                throw new Error("Unexpected map mode.");
                throw new Error("Unexpected map mode.");
        }
        }
        final ContentValues[] valuesList = new ContentValues[documents.length];
        for (int i = 0; i < documents.length; i++) {
            valuesList[i] = new ContentValues();
            getChildDocumentValues(valuesList[i], deviceId, parentId, documents[i]);
        }
        mDatabase.putDocuments(
        mDatabase.putDocuments(
                valuesList, SELECTION_CHILD_DOCUMENTS, parentId, heuristic, mapColumn);
                valuesList, SELECTION_CHILD_DOCUMENTS, parentId, heuristic, mapColumn);
    }
    }


    /**
     * Clears mapping between MTP identifier and document/root ID.
     */
    @VisibleForTesting
    @VisibleForTesting
    void clearMapping() {
    synchronized void clearMapping() {
        mDatabase.clearMapping();
        mDatabase.clearMapping();
        mMappingMode.clear();
        mMappingMode.clear();
    }
    }


    @VisibleForTesting
    /**
    void stopAddingRootDocuments(int deviceId) {
     * Stops adding root documents.
     * @param deviceId Device ID.
     * @return True if new rows are added/removed.
     */
    synchronized boolean stopAddingRootDocuments(int deviceId) {
        final String mappingModeKey = getRootDocumentsMappingStateKey(deviceId);
        final String mappingModeKey = getRootDocumentsMappingStateKey(deviceId);
        switch (mMappingMode.get(mappingModeKey)) {
        switch (mMappingMode.get(mappingModeKey)) {
            case MAP_BY_MTP_IDENTIFIER:
            case MAP_BY_MTP_IDENTIFIER:
                mDatabase.stopAddingDocuments(
                mMappingMode.remove(mappingModeKey);
                return mDatabase.stopAddingDocuments(
                        SELECTION_ROOT_DOCUMENTS,
                        SELECTION_ROOT_DOCUMENTS,
                        Integer.toString(deviceId),
                        Integer.toString(deviceId),
                        COLUMN_STORAGE_ID);
                        COLUMN_STORAGE_ID);
                break;
            case MAP_BY_NAME:
            case MAP_BY_NAME:
                mDatabase.stopAddingDocuments(
                mMappingMode.remove(mappingModeKey);
                return mDatabase.stopAddingDocuments(
                        SELECTION_ROOT_DOCUMENTS,
                        SELECTION_ROOT_DOCUMENTS,
                        Integer.toString(deviceId),
                        Integer.toString(deviceId),
                        Document.COLUMN_DISPLAY_NAME);
                        Document.COLUMN_DISPLAY_NAME);
                break;
            default:
            default:
                throw new Error("Unexpected mapping state.");
                throw new Error("Unexpected mapping state.");
        }
        }
        mMappingMode.remove(mappingModeKey);
    }
    }


    /**
     * Stops adding documents under the parent.
     * @param parentId Document ID of the parent.
     */
    @VisibleForTesting
    @VisibleForTesting
    void stopAddingChildDocuments(String parentId) {
    synchronized void stopAddingChildDocuments(String parentId) {
        final String mappingModeKey = getChildDocumentsMappingStateKey(parentId);
        final String mappingModeKey = getChildDocumentsMappingStateKey(parentId);
        switch (mMappingMode.get(mappingModeKey)) {
        switch (mMappingMode.get(mappingModeKey)) {
            case MAP_BY_MTP_IDENTIFIER:
            case MAP_BY_MTP_IDENTIFIER:
@@ -303,11 +364,19 @@ class MtpDatabase {
        values.put(Document.COLUMN_SIZE, info.getCompressedSize());
        values.put(Document.COLUMN_SIZE, info.getCompressedSize());
    }
    }


    private String getRootDocumentsMappingStateKey(int deviceId) {
    /**
     * @param deviceId Device ID.
     * @return Key for {@link #mMappingMode}.
     */
    private static String getRootDocumentsMappingStateKey(int deviceId) {
        return "RootDocuments/" + deviceId;
        return "RootDocuments/" + deviceId;
    }
    }


    private String getChildDocumentsMappingStateKey(String parentDocumentId) {
    /**
     * @param parentDocumentId Document ID for the parent document.
     * @return Key for {@link #mMappingMode}.
     */
    private static String getChildDocumentsMappingStateKey(String parentDocumentId) {
        return "ChildDocuments/" + parentDocumentId;
        return "ChildDocuments/" + parentDocumentId;
    }
    }
}
}
+4 −0
Original line number Original line Diff line number Diff line
@@ -126,7 +126,11 @@ class MtpDatabaseConstants {
                            Root.COLUMN_TITLE + "," +
                            Root.COLUMN_TITLE + "," +
                    TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " +
                    TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " +
                            Root.COLUMN_SUMMARY + "," +
                            Root.COLUMN_SUMMARY + "," +
                    // Temporary replace COLUMN_DOCUMENT_ID with old format.
                    TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " +
                    TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " +
                    Root.COLUMN_DOCUMENT_ID + "_," +
                    TABLE_DOCUMENTS + "." + COLUMN_DEVICE_ID + "|| '_' ||" +
                    TABLE_DOCUMENTS + "." + COLUMN_STORAGE_ID + "||'_0' AS " +
                    Root.COLUMN_DOCUMENT_ID + "," +
                    Root.COLUMN_DOCUMENT_ID + "," +
                    TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES + "," +
                    TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES + "," +
                    TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES + "," +
                    TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES + "," +
+68 −15
Original line number Original line Diff line number Diff line
@@ -59,6 +59,15 @@ class MtpDatabaseInternal {
        mDatabase = helper.getWritableDatabase();
        mDatabase = helper.getWritableDatabase();
    }
    }


    void close() {
        mDatabase.close();
    }

    /**
     * Queries roots information.
     * @param columnNames Column names defined in {@link android.provider.DocumentsContract.Root}.
     * @return Database cursor.
     */
    Cursor queryRoots(String[] columnNames) {
    Cursor queryRoots(String[] columnNames) {
        return mDatabase.query(
        return mDatabase.query(
                VIEW_ROOTS,
                VIEW_ROOTS,
@@ -70,6 +79,12 @@ class MtpDatabaseInternal {
                null);
                null);
    }
    }


    /**
     * Queries root documents information.
     * @param columnNames Column names defined in
     *     {@link android.provider.DocumentsContract.Document}.
     * @return Database cursor.
     */
    Cursor queryRootDocuments(String[] columnNames) {
    Cursor queryRootDocuments(String[] columnNames) {
        return mDatabase.query(
        return mDatabase.query(
                TABLE_DOCUMENTS,
                TABLE_DOCUMENTS,
@@ -81,6 +96,12 @@ class MtpDatabaseInternal {
                null);
                null);
    }
    }


    /**
     * Queries documents information.
     * @param columnNames Column names defined in
     *     {@link android.provider.DocumentsContract.Document}.
     * @return Database cursor.
     */
    Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) {
    Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) {
        return mDatabase.query(
        return mDatabase.query(
                TABLE_DOCUMENTS,
                TABLE_DOCUMENTS,
@@ -92,6 +113,14 @@ class MtpDatabaseInternal {
                null);
                null);
    }
    }


    /**
     * Remove all rows belong to a device.
     * @param deviceId Device ID.
     */
    void removeDeviceRows(int deviceId) {
        deleteDocumentsAndRoots(COLUMN_DEVICE_ID + "=?", strings(deviceId));
    }

    /**
    /**
     * Starts adding new documents.
     * Starts adding new documents.
     * The methods decides mapping mode depends on if all documents under the given parent have MTP
     * The methods decides mapping mode depends on if all documents under the given parent have MTP
@@ -133,24 +162,23 @@ class MtpDatabaseInternal {
     * existing rows with the new values. If the mapping mode is heuristic, it adds some new rows as
     * existing rows with the new values. If the mapping mode is heuristic, it adds some new rows as
     * 'pending' state when that rows may be corresponding to existing 'invalidated' rows. Then
     * 'pending' state when that rows may be corresponding to existing 'invalidated' rows. Then
     * {@link #stopAddingDocuments(String, String, String)} turns the pending rows into 'valid'
     * {@link #stopAddingDocuments(String, String, String)} turns the pending rows into 'valid'
     * rows.
     * 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 that are stored in the database.
     * @param selection SQL where closure to select rows that shares the same parent.
     * @param selection SQL where closure to select rows that shares the same parent.
     * @param arg Argument for selection SQL.
     * @param arg Argument for selection SQL.
     * @param heuristic Whether the mapping mode is heuristic.
     * @param heuristic Whether the mapping mode is heuristic.
     * @return List of Document ID inserted to the table.
     * @return Whether the method adds new rows.
     */
     */
    long[] putDocuments(
    boolean putDocuments(
            ContentValues[] valuesList,
            ContentValues[] valuesList,
            String selection,
            String selection,
            String arg,
            String arg,
            boolean heuristic,
            boolean heuristic,
            String mappingKey) {
            String mappingKey) {
        boolean added = false;
        mDatabase.beginTransaction();
        mDatabase.beginTransaction();
        try {
        try {
            final long[] documentIds = new long[valuesList.length];
            int i = 0;
            for (final ContentValues values : valuesList) {
            for (final ContentValues values : valuesList) {
                final Cursor candidateCursor = mDatabase.query(
                final Cursor candidateCursor = mDatabase.query(
                        TABLE_DOCUMENTS,
                        TABLE_DOCUMENTS,
@@ -166,6 +194,7 @@ class MtpDatabaseInternal {
                final long rowId;
                final long rowId;
                if (candidateCursor.getCount() == 0) {
                if (candidateCursor.getCount() == 0) {
                    rowId = mDatabase.insert(TABLE_DOCUMENTS, null, values);
                    rowId = mDatabase.insert(TABLE_DOCUMENTS, null, values);
                    added = true;
                } else if (!heuristic) {
                } else if (!heuristic) {
                    candidateCursor.moveToNext();
                    candidateCursor.moveToNext();
                    final String documentId = candidateCursor.getString(0);
                    final String documentId = candidateCursor.getString(0);
@@ -177,17 +206,21 @@ class MtpDatabaseInternal {
                }
                }
                // Document ID is a primary integer key of the table. So the returned row
                // Document ID is a primary integer key of the table. So the returned row
                // IDs should be same with the document ID.
                // IDs should be same with the document ID.
                documentIds[i++] = rowId;
                values.put(Document.COLUMN_DOCUMENT_ID, rowId);
                candidateCursor.close();
                candidateCursor.close();
            }
            }


            mDatabase.setTransactionSuccessful();
            mDatabase.setTransactionSuccessful();
            return documentIds;
            return added;
        } finally {
        } finally {
            mDatabase.endTransaction();
            mDatabase.endTransaction();
        }
        }
    }
    }


    /**
     * Puts extra information for root documents.
     * @param values Values containing extra information.
     */
    void putRootExtra(ContentValues values) {
    void putRootExtra(ContentValues values) {
        mDatabase.replace(TABLE_ROOT_EXTRA, null, values);
        mDatabase.replace(TABLE_ROOT_EXTRA, null, values);
    }
    }
@@ -199,8 +232,9 @@ class MtpDatabaseInternal {
     * @param selection Query to select rows for resolving.
     * @param selection Query to select rows for resolving.
     * @param arg Argument for selection SQL.
     * @param arg Argument for selection SQL.
     * @param groupKey Column name used to find corresponding rows.
     * @param groupKey Column name used to find corresponding rows.
     * @return Whether the methods adds or removed visible rows.
     */
     */
    void stopAddingDocuments(String selection, String arg, String groupKey) {
    boolean stopAddingDocuments(String selection, String arg, String groupKey) {
        mDatabase.beginTransaction();
        mDatabase.beginTransaction();
        try {
        try {
            // Get 1-to-1 mapping of invalidated document and pending document.
            // Get 1-to-1 mapping of invalidated document and pending document.
@@ -265,22 +299,29 @@ class MtpDatabaseInternal {
            }
            }
            mergingCursor.close();
            mergingCursor.close();


            boolean changed = false;

            // Delete all invalidated rows that cannot be mapped.
            // Delete all invalidated rows that cannot be mapped.
            deleteDocumentsAndRoots(
            if (deleteDocumentsAndRoots(
                    COLUMN_ROW_STATE + " = ? AND " + selection,
                    COLUMN_ROW_STATE + " = ? AND " + selection,
                    strings(ROW_STATE_INVALIDATED, arg));
                    strings(ROW_STATE_INVALIDATED, arg))) {
                changed = true;
            }


            // The database cannot find old document ID for the pending rows.
            // The database cannot find old document ID for the pending rows.
            // Turn the all pending rows into valid state, which means the rows become to be
            // Turn the all pending rows into valid state, which means the rows become to be
            // valid with new document ID.
            // valid with new document ID.
            values.clear();
            values.clear();
            values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
            values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
            mDatabase.update(
            if (mDatabase.update(
                    TABLE_DOCUMENTS,
                    TABLE_DOCUMENTS,
                    values,
                    values,
                    COLUMN_ROW_STATE + " = ? AND " + selection,
                    COLUMN_ROW_STATE + " = ? AND " + selection,
                    strings(ROW_STATE_PENDING, arg));
                    strings(ROW_STATE_PENDING, arg)) != 0) {
                changed = true;
            }
            mDatabase.setTransactionSuccessful();
            mDatabase.setTransactionSuccessful();
            return changed;
        } finally {
        } finally {
            mDatabase.endTransaction();
            mDatabase.endTransaction();
        }
        }
@@ -307,14 +348,23 @@ class MtpDatabaseInternal {
        }
        }
    }
    }


    /**
     * {@link android.database.sqlite.SQLiteDatabase#beginTransaction()}
     */
    void beginTransaction() {
    void beginTransaction() {
        mDatabase.beginTransaction();
        mDatabase.beginTransaction();
    }
    }


    /**
     * {@link android.database.sqlite.SQLiteDatabase#setTransactionSuccessful()}
     */
    void setTransactionSuccessful() {
    void setTransactionSuccessful() {
        mDatabase.setTransactionSuccessful();
        mDatabase.setTransactionSuccessful();
    }
    }


    /**
     * {@link android.database.sqlite.SQLiteDatabase#endTransaction()}
     */
    void endTransaction() {
    void endTransaction() {
        mDatabase.endTransaction();
        mDatabase.endTransaction();
    }
    }
@@ -323,11 +373,13 @@ class MtpDatabaseInternal {
     * Deletes a document, and its root information if the document is a root document.
     * Deletes a document, and its root information if the document is a root document.
     * @param selection Query to select documents.
     * @param selection Query to select documents.
     * @param args Arguments for selection.
     * @param args Arguments for selection.
     * @return Whether the method deletes rows.
     */
     */
    private void deleteDocumentsAndRoots(String selection, String[] args) {
    private boolean deleteDocumentsAndRoots(String selection, String[] args) {
        mDatabase.beginTransaction();
        mDatabase.beginTransaction();
        try {
        try {
            mDatabase.delete(
            int deleted = 0;
            deleted += mDatabase.delete(
                    TABLE_ROOT_EXTRA,
                    TABLE_ROOT_EXTRA,
                    Root.COLUMN_ROOT_ID + " IN (" + SQLiteQueryBuilder.buildQueryString(
                    Root.COLUMN_ROOT_ID + " IN (" + SQLiteQueryBuilder.buildQueryString(
                            false,
                            false,
@@ -339,8 +391,9 @@ class MtpDatabaseInternal {
                            null,
                            null,
                            null) + ")",
                            null) + ")",
                    args);
                    args);
            mDatabase.delete(TABLE_DOCUMENTS, selection, args);
            deleted += mDatabase.delete(TABLE_DOCUMENTS, selection, args);
            mDatabase.setTransactionSuccessful();
            mDatabase.setTransactionSuccessful();
            return deleted != 0;
        } finally {
        } finally {
            mDatabase.endTransaction();
            mDatabase.endTransaction();
        }
        }
+18 −17
Original line number Original line Diff line number Diff line
@@ -62,6 +62,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
    private Map<Integer, DeviceToolkit> mDeviceToolkits;
    private Map<Integer, DeviceToolkit> mDeviceToolkits;
    private RootScanner mRootScanner;
    private RootScanner mRootScanner;
    private Resources mResources;
    private Resources mResources;
    private MtpDatabase mDatabase;


    /**
    /**
     * Provides singleton instance to MtpDocumentsService.
     * Provides singleton instance to MtpDocumentsService.
@@ -77,17 +78,23 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        mMtpManager = new MtpManager(getContext());
        mMtpManager = new MtpManager(getContext());
        mResolver = getContext().getContentResolver();
        mResolver = getContext().getContentResolver();
        mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
        mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
        mRootScanner = new RootScanner(mResolver, mMtpManager);
        mDatabase = new MtpDatabase(getContext());
        mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
        return true;
        return true;
    }
    }


    @VisibleForTesting
    @VisibleForTesting
    void onCreateForTesting(Resources resources, MtpManager mtpManager, ContentResolver resolver) {
    void onCreateForTesting(
            Resources resources,
            MtpManager mtpManager,
            ContentResolver resolver,
            MtpDatabase database) {
        mResources = resources;
        mResources = resources;
        mMtpManager = mtpManager;
        mMtpManager = mtpManager;
        mResolver = resolver;
        mResolver = resolver;
        mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
        mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
        mRootScanner = new RootScanner(mResolver, mMtpManager);
        mDatabase = database;
        mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
    }
    }


    @Override
    @Override
@@ -95,17 +102,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        if (projection == null) {
        if (projection == null) {
            projection = MtpDocumentsProvider.DEFAULT_ROOT_PROJECTION;
            projection = MtpDocumentsProvider.DEFAULT_ROOT_PROJECTION;
        }
        }
        final MatrixCursor cursor = new MatrixCursor(projection);
        final Cursor cursor = mDatabase.queryRoots(projection);
        final MtpRoot[] roots = mRootScanner.getRoots();
        for (final MtpRoot root : roots) {
            final Identifier rootIdentifier = new Identifier(root.mDeviceId, root.mStorageId);
            final MatrixCursor.RowBuilder builder = cursor.newRow();
            builder.add(Root.COLUMN_ROOT_ID, rootIdentifier.toRootId());
            builder.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
            builder.add(Root.COLUMN_TITLE, root.getRootName(mResources));
            builder.add(Root.COLUMN_DOCUMENT_ID, rootIdentifier.toDocumentId());
            builder.add(Root.COLUMN_AVAILABLE_BYTES , root.mFreeSpace);
        }
        cursor.setNotificationUri(
        cursor.setNotificationUri(
                mResolver, DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY));
                mResolver, DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY));
        return cursor;
        return cursor;
@@ -266,14 +263,16 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        // TODO: Flush the device before closing (if not closed externally).
        // TODO: Flush the device before closing (if not closed externally).
        getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
        getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
        mDeviceToolkits.remove(deviceId);
        mDeviceToolkits.remove(deviceId);
        mDatabase.removeDeviceRows(deviceId);
        mMtpManager.closeDevice(deviceId);
        mMtpManager.closeDevice(deviceId);
        mRootScanner.scanNow();
        mRootScanner.notifyChange();
    }
    }


    void closeAllDevices() {
    void close() throws InterruptedException {
        boolean closed = false;
        boolean closed = false;
        for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
        for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
            try {
            try {
                mDatabase.removeDeviceRows(deviceId);
                mMtpManager.closeDevice(deviceId);
                mMtpManager.closeDevice(deviceId);
                getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
                getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
                closed = true;
                closed = true;
@@ -282,8 +281,10 @@ public class MtpDocumentsProvider extends DocumentsProvider {
            }
            }
        }
        }
        if (closed) {
        if (closed) {
            mRootScanner.scanNow();
            mRootScanner.notifyChange();
        }
        }
        mRootScanner.close();
        mDatabase.close();
    }
    }


    boolean hasOpenedDevices() {
    boolean hasOpenedDevices() {
+5 −1
Original line number Original line Diff line number Diff line
@@ -79,9 +79,13 @@ public class MtpDocumentsService extends Service {
    @Override
    @Override
    public void onDestroy() {
    public void onDestroy() {
        final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
        final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
        provider.closeAllDevices();
        unregisterReceiver(mReceiver);
        unregisterReceiver(mReceiver);
        mReceiver = null;
        mReceiver = null;
        try {
            provider.close();
        } catch (InterruptedException e) {
            Log.e(MtpDocumentsProvider.TAG, e.getMessage());
        }
        super.onDestroy();
        super.onDestroy();
    }
    }


Loading