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

Commit 75753842 authored by Daichi Hirono's avatar Daichi Hirono Committed by Android (Google) Code Review
Browse files

Merge "Use device key to map device documents."

parents 66a603c5 637a2010
Loading
Loading
Loading
Loading
+92 −76
Original line number Original line Diff line number Diff line
@@ -24,13 +24,13 @@ import android.database.sqlite.SQLiteDatabase;
import android.mtp.MtpObjectInfo;
import android.mtp.MtpObjectInfo;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract.Root;
import android.util.ArraySet;
import android.util.Log;
import android.util.Log;


import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;


import java.io.FileNotFoundException;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Set;
import java.util.Map;


import static com.android.mtp.MtpDatabaseConstants.*;
import static com.android.mtp.MtpDatabaseConstants.*;
import static com.android.mtp.MtpDatabase.strings;
import static com.android.mtp.MtpDatabase.strings;
@@ -44,11 +44,9 @@ class Mapper {
    private final MtpDatabase mDatabase;
    private final MtpDatabase mDatabase;


    /**
    /**
     * Mapping mode for a parent. The key is document ID of parent, or null for root documents.
     * IDs which currently Mapper operates mapping for.
     * Methods operate the state needs to be synchronized.
     * TODO: Replace this with unboxing int map.
     */
     */
    private final Map<String, Integer> mMappingMode = new HashMap<>();
    private final Set<String> mInMappingIds = new ArraySet<>();


    Mapper(MtpDatabase database) {
    Mapper(MtpDatabase database) {
        mDatabase = database;
        mDatabase = database;
@@ -75,7 +73,7 @@ class Mapper {
                    extraValuesList,
                    extraValuesList,
                    COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
                    COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
                    EMPTY_ARGS,
                    EMPTY_ARGS,
                    Document.COLUMN_DISPLAY_NAME);
                    strings(COLUMN_DEVICE_ID, COLUMN_MAPPING_KEY));
            database.setTransactionSuccessful();
            database.setTransactionSuccessful();
            return changed;
            return changed;
        } finally {
        } finally {
@@ -96,18 +94,6 @@ class Mapper {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        database.beginTransaction();
        database.beginTransaction();
        try {
        try {
            final String mapColumn;
            Preconditions.checkState(mMappingMode.containsKey(parentDocumentId));
            switch (mMappingMode.get(parentDocumentId)) {
                case MAP_BY_MTP_IDENTIFIER:
                    mapColumn = COLUMN_STORAGE_ID;
                    break;
                case MAP_BY_NAME:
                    mapColumn = Document.COLUMN_DISPLAY_NAME;
                    break;
                default:
                    throw new Error("Unexpected map mode.");
            }
            final ContentValues[] valuesList = new ContentValues[roots.length];
            final ContentValues[] valuesList = new ContentValues[roots.length];
            final ContentValues[] extraValuesList = new ContentValues[roots.length];
            final ContentValues[] extraValuesList = new ContentValues[roots.length];
            for (int i = 0; i < roots.length; i++) {
            for (int i = 0; i < roots.length; i++) {
@@ -122,7 +108,7 @@ class Mapper {
                    extraValuesList,
                    extraValuesList,
                    COLUMN_PARENT_DOCUMENT_ID + " = ?",
                    COLUMN_PARENT_DOCUMENT_ID + " = ?",
                    strings(parentDocumentId),
                    strings(parentDocumentId),
                    mapColumn);
                    strings(COLUMN_STORAGE_ID, Document.COLUMN_DISPLAY_NAME));


            database.setTransactionSuccessful();
            database.setTransactionSuccessful();
            return changed;
            return changed;
@@ -141,23 +127,10 @@ class Mapper {
     */
     */
    synchronized void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents)
    synchronized void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents)
            throws FileNotFoundException {
            throws FileNotFoundException {
        final String mapColumn;
        Preconditions.checkState(mMappingMode.containsKey(parentId));
        switch (mMappingMode.get(parentId)) {
            case MAP_BY_MTP_IDENTIFIER:
                mapColumn = COLUMN_OBJECT_HANDLE;
                break;
            case MAP_BY_NAME:
                mapColumn = Document.COLUMN_DISPLAY_NAME;
                break;
            default:
                throw new Error("Unexpected map mode.");
        }
        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.getObjectDocumentValues(
            MtpDatabase.getObjectDocumentValues(valuesList[i], deviceId, parentId, documents[i]);
                    valuesList[i], deviceId, parentId, documents[i]);
        }
        }
        putDocuments(
        putDocuments(
                parentId,
                parentId,
@@ -165,14 +138,14 @@ class Mapper {
                null,
                null,
                COLUMN_PARENT_DOCUMENT_ID + " = ?",
                COLUMN_PARENT_DOCUMENT_ID + " = ?",
                strings(parentId),
                strings(parentId),
                mapColumn);
                strings(COLUMN_OBJECT_HANDLE, Document.COLUMN_DISPLAY_NAME));
    }
    }


    void clearMapping() {
    void clearMapping() {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        database.beginTransaction();
        database.beginTransaction();
        try {
        try {
            mMappingMode.clear();
            mInMappingIds.clear();
            // Disconnect all device rows.
            // Disconnect all device rows.
            try {
            try {
                startAddingDocuments(null);
                startAddingDocuments(null);
@@ -211,7 +184,7 @@ class Mapper {
        database.beginTransaction();
        database.beginTransaction();
        try {
        try {
            getParentOrHaltMapping(parentDocumentId);
            getParentOrHaltMapping(parentDocumentId);
            Preconditions.checkState(!mMappingMode.containsKey(parentDocumentId));
            Preconditions.checkState(!mInMappingIds.contains(parentDocumentId));


            // Set all valid documents as invalidated.
            // Set all valid documents as invalidated.
            final ContentValues values = new ContentValues();
            final ContentValues values = new ContentValues();
@@ -222,15 +195,8 @@ class Mapper {
                    selection + " AND " + COLUMN_ROW_STATE + " = ?",
                    selection + " AND " + COLUMN_ROW_STATE + " = ?",
                    DatabaseUtils.appendSelectionArgs(args, strings(ROW_STATE_VALID)));
                    DatabaseUtils.appendSelectionArgs(args, strings(ROW_STATE_VALID)));


            // If we have rows that does not have MTP identifier, do heuristic mapping by name.
            final boolean useNameForResolving = DatabaseUtils.queryNumEntries(
                    database,
                    TABLE_DOCUMENTS,
                    selection + " AND " + COLUMN_DEVICE_ID + " IS NULL",
                    args) > 0;
            database.setTransactionSuccessful();
            database.setTransactionSuccessful();
            mMappingMode.put(
            mInMappingIds.add(parentDocumentId);
                    parentDocumentId, useNameForResolving ? MAP_BY_NAME : MAP_BY_MTP_IDENTIFIER);
        } finally {
        } finally {
            database.endTransaction();
            database.endTransaction();
        }
        }
@@ -249,7 +215,7 @@ class Mapper {
     * @param rootExtraValuesList Values for root extra to be stored in the database.
     * @param rootExtraValuesList Values for root extra to be 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 args Argument for selection SQL.
     * @param args Argument for selection SQL.
     * @return Whether it adds at least one new row that is not mapped with existing document ID.
     * @return Whether the database content is changed.
     * @throws FileNotFoundException When parentId is not registered in the database.
     * @throws FileNotFoundException When parentId is not registered in the database.
     */
     */
    private boolean putDocuments(
    private boolean putDocuments(
@@ -258,13 +224,15 @@ class Mapper {
            @Nullable ContentValues[] rootExtraValuesList,
            @Nullable ContentValues[] rootExtraValuesList,
            String selection,
            String selection,
            String[] args,
            String[] args,
            String mappingKey) throws FileNotFoundException {
            String[] mappingKeys) throws FileNotFoundException {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        boolean added = false;
        boolean changed = false;
        database.beginTransaction();
        database.beginTransaction();
        try {
        try {
            getParentOrHaltMapping(parentId);
            getParentOrHaltMapping(parentId);
            Preconditions.checkState(mMappingMode.containsKey(parentId));
            Preconditions.checkState(mInMappingIds.contains(parentId));
            final ContentValues oldRowSnapshot = new ContentValues();
            final ContentValues newRowSnapshot = new ContentValues();
            for (int i = 0; i < valuesList.length; i++) {
            for (int i = 0; i < valuesList.length; i++) {
                final ContentValues values = valuesList[i];
                final ContentValues values = valuesList[i];
                final ContentValues rootExtraValues;
                final ContentValues rootExtraValues;
@@ -273,29 +241,18 @@ class Mapper {
                } else {
                } else {
                    rootExtraValues = null;
                    rootExtraValues = null;
                }
                }
                final Cursor candidateCursor = database.query(
                try (final Cursor candidateCursor =
                        TABLE_DOCUMENTS,
                        queryCandidate(selection, args, mappingKeys, values)) {
                        strings(Document.COLUMN_DOCUMENT_ID),
                        selection + " AND " +
                        COLUMN_ROW_STATE + " IN (?, ?) AND " +
                        mappingKey + "=?",
                        DatabaseUtils.appendSelectionArgs(
                                args,
                                strings(ROW_STATE_INVALIDATED,
                                        ROW_STATE_DISCONNECTED,
                                        values.getAsString(mappingKey))),
                        null,
                        null,
                        null,
                        "1");
                try {
                    final long rowId;
                    final long rowId;
                    if (candidateCursor.getCount() == 0) {
                    if (candidateCursor == null) {
                        rowId = database.insert(TABLE_DOCUMENTS, null, values);
                        rowId = database.insert(TABLE_DOCUMENTS, null, values);
                        added = true;
                        changed = true;
                    } else {
                    } else {
                        candidateCursor.moveToNext();
                        candidateCursor.moveToNext();
                        rowId = candidateCursor.getLong(0);
                        rowId = candidateCursor.getLong(0);
                        if (!changed) {
                            mDatabase.writeRowSnapshot(String.valueOf(rowId), oldRowSnapshot);
                        }
                        database.update(
                        database.update(
                                TABLE_DOCUMENTS,
                                TABLE_DOCUMENTS,
                                values,
                                values,
@@ -309,13 +266,20 @@ class Mapper {
                        rootExtraValues.put(Root.COLUMN_ROOT_ID, rowId);
                        rootExtraValues.put(Root.COLUMN_ROOT_ID, rowId);
                        database.replace(TABLE_ROOT_EXTRA, null, rootExtraValues);
                        database.replace(TABLE_ROOT_EXTRA, null, rootExtraValues);
                    }
                    }
                } finally {

                    candidateCursor.close();
                    if (!changed) {
                        mDatabase.writeRowSnapshot(String.valueOf(rowId), newRowSnapshot);
                        // Put row state as string because SQLite returns snapshot values as string.
                        oldRowSnapshot.put(COLUMN_ROW_STATE, String.valueOf(ROW_STATE_VALID));
                        if (!oldRowSnapshot.equals(newRowSnapshot)) {
                            changed = true;
                        }
                    }
                }
                }
            }
            }


            database.setTransactionSuccessful();
            database.setTransactionSuccessful();
            return added;
            return changed;
        } finally {
        } finally {
            database.endTransaction();
            database.endTransaction();
        }
        }
@@ -345,8 +309,8 @@ class Mapper {
        database.beginTransaction();
        database.beginTransaction();
        try {
        try {
            final Identifier parentIdentifier = getParentOrHaltMapping(parentId);
            final Identifier parentIdentifier = getParentOrHaltMapping(parentId);
            Preconditions.checkState(mMappingMode.containsKey(parentId));
            Preconditions.checkState(mInMappingIds.contains(parentId));
            mMappingMode.remove(parentId);
            mInMappingIds.remove(parentId);


            boolean changed = false;
            boolean changed = false;
            // Delete/disconnect all invalidated rows that cannot be mapped.
            // Delete/disconnect all invalidated rows that cannot be mapped.
@@ -374,6 +338,58 @@ class Mapper {
        }
        }
    }
    }


    /**
     * Queries candidate for each mappingKey, and returns the first cursor that includes a
     * candidate.
     *
     * @param selection Pre-selection for candidate.
     * @param args Arguments for selection.
     * @param mappingKeys List of mapping key columns.
     * @param values Values of document that Mapper tries to map.
     * @return Cursor for mapping candidate or null when Mapper does not find any candidate.
     */
    private @Nullable Cursor queryCandidate(
            String selection, String[] args, String[] mappingKeys, ContentValues values) {
        for (final String mappingKey : mappingKeys) {
            final Cursor candidateCursor = queryCandidate(selection, args, mappingKey, values);
            if (candidateCursor.getCount() == 0) {
                candidateCursor.close();
                continue;
            }
            return candidateCursor;
        }
        return null;
    }

    /**
     * Looks for mapping candidate with given mappingKey.
     *
     * @param selection Pre-selection for candidate.
     * @param args Arguments for selection.
     * @param mappingKey Column name of mapping key.
     * @param values Values of document that Mapper tries to map.
     * @return Cursor for mapping candidate.
     */
    private Cursor queryCandidate(
            String selection, String[] args, String mappingKey, ContentValues values) {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        return database.query(
                TABLE_DOCUMENTS,
                strings(Document.COLUMN_DOCUMENT_ID),
                selection + " AND " +
                COLUMN_ROW_STATE + " IN (?, ?) AND " +
                mappingKey + " = ?",
                DatabaseUtils.appendSelectionArgs(
                        args,
                        strings(ROW_STATE_INVALIDATED,
                                ROW_STATE_DISCONNECTED,
                                values.getAsString(mappingKey))),
                null,
                null,
                null,
                "1");
    }

    /**
    /**
     * Returns the parent identifier from parent document ID if the parent ID is found in the
     * Returns the parent identifier from parent document ID if the parent ID is found in the
     * database. Otherwise it halts mapping and throws FileNotFoundException.
     * database. Otherwise it halts mapping and throws FileNotFoundException.
@@ -395,7 +411,7 @@ class Mapper {
            }
            }
            return identifier;
            return identifier;
        } catch (FileNotFoundException error) {
        } catch (FileNotFoundException error) {
            mMappingMode.remove(parentId);
            mInMappingIds.remove(parentId);
            throw error;
            throw error;
        }
        }
    }
    }
+20 −0
Original line number Original line Diff line number Diff line
@@ -562,6 +562,25 @@ class MtpDatabase {
        }
        }
    }
    }


    void writeRowSnapshot(String documentId, ContentValues values) throws FileNotFoundException {
        try (final Cursor cursor = mDatabase.query(
                JOIN_ROOTS,
                strings("*"),
                SELECTION_DOCUMENT_ID,
                strings(documentId),
                null,
                null,
                null,
                "1")) {
            if (cursor.getCount() == 0) {
                throw new FileNotFoundException();
            }
            cursor.moveToNext();
            values.clear();
            DatabaseUtils.cursorRowToContentValues(cursor, values);
        }
    }

    private static class OpenHelper extends SQLiteOpenHelper {
    private static class OpenHelper extends SQLiteOpenHelper {
        public OpenHelper(Context context, int flags) {
        public OpenHelper(Context context, int flags) {
            super(context,
            super(context,
@@ -600,6 +619,7 @@ class MtpDatabase {
        values.putNull(COLUMN_PARENT_DOCUMENT_ID);
        values.putNull(COLUMN_PARENT_DOCUMENT_ID);
        values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
        values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
        values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_DEVICE);
        values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_DEVICE);
        values.put(COLUMN_MAPPING_KEY, device.deviceKey);
        values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
        values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
        values.put(Document.COLUMN_DISPLAY_NAME, device.name);
        values.put(Document.COLUMN_DISPLAY_NAME, device.name);
        values.putNull(Document.COLUMN_SUMMARY);
        values.putNull(Document.COLUMN_SUMMARY);
+4 −12
Original line number Original line Diff line number Diff line
@@ -30,7 +30,7 @@ import java.util.Map;
 * Class containing MtpDatabase constants.
 * Class containing MtpDatabase constants.
 */
 */
class MtpDatabaseConstants {
class MtpDatabaseConstants {
    static final int DATABASE_VERSION = 3;
    static final int DATABASE_VERSION = 4;
    static final String DATABASE_NAME = "database";
    static final String DATABASE_NAME = "database";


    static final int FLAG_DATABASE_IN_MEMORY = 1;
    static final int FLAG_DATABASE_IN_MEMORY = 1;
@@ -62,6 +62,7 @@ class MtpDatabaseConstants {
    static final String COLUMN_PARENT_DOCUMENT_ID = "parent_document_id";
    static final String COLUMN_PARENT_DOCUMENT_ID = "parent_document_id";
    static final String COLUMN_DOCUMENT_TYPE = "document_type";
    static final String COLUMN_DOCUMENT_TYPE = "document_type";
    static final String COLUMN_ROW_STATE = "row_state";
    static final String COLUMN_ROW_STATE = "row_state";
    static final String COLUMN_MAPPING_KEY = "column_mapping_key";


    /**
    /**
     * The state represents that the row has a valid object handle.
     * The state represents that the row has a valid object handle.
@@ -83,16 +84,6 @@ class MtpDatabaseConstants {
     */
     */
    static final int ROW_STATE_DISCONNECTED = 2;
    static final int ROW_STATE_DISCONNECTED = 2;


    /**
     * Mapping mode that uses MTP identifier to find corresponding rows.
     */
    static final int MAP_BY_MTP_IDENTIFIER = 0;

    /**
     * Mapping mode that uses name to find corresponding rows.
     */
    static final int MAP_BY_NAME = 1;

    @IntDef(value = { DOCUMENT_TYPE_DEVICE, DOCUMENT_TYPE_STORAGE, DOCUMENT_TYPE_OBJECT })
    @IntDef(value = { DOCUMENT_TYPE_DEVICE, DOCUMENT_TYPE_STORAGE, DOCUMENT_TYPE_OBJECT })
    @Retention(RetentionPolicy.SOURCE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface DocumentType {}
    public @interface DocumentType {}
@@ -125,6 +116,7 @@ class MtpDatabaseConstants {
            COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
            COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
            COLUMN_ROW_STATE + " INTEGER NOT NULL," +
            COLUMN_ROW_STATE + " INTEGER NOT NULL," +
            COLUMN_DOCUMENT_TYPE + " INTEGER NOT NULL," +
            COLUMN_DOCUMENT_TYPE + " INTEGER NOT NULL," +
            COLUMN_MAPPING_KEY + " STRING," +
            Document.COLUMN_MIME_TYPE + " TEXT NOT NULL," +
            Document.COLUMN_MIME_TYPE + " TEXT NOT NULL," +
            Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
            Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
            Document.COLUMN_SUMMARY + " TEXT," +
            Document.COLUMN_SUMMARY + " TEXT," +
@@ -174,7 +166,7 @@ class MtpDatabaseConstants {


    private static String createJoinFromClosure(
    private static String createJoinFromClosure(
            String table1, String table2, String column1, String column2) {
            String table1, String table2, String column1, String column2) {
        return table1 + " INNER JOIN " + table2 +
        return table1 + " LEFT JOIN " + table2 +
                " ON " + table1 + "." + column1 + " = " + table2 + "." + column2;
                " ON " + table1 + "." + column1 + " = " + table2 + "." + column2;
    }
    }
}
}
+5 −2
Original line number Original line Diff line number Diff line
@@ -21,17 +21,20 @@ import android.annotation.Nullable;
class MtpDeviceRecord {
class MtpDeviceRecord {
    public final int deviceId;
    public final int deviceId;
    public final String name;
    public final String name;
    public final @Nullable String deviceKey;
    public final boolean opened;
    public final boolean opened;
    public final MtpRoot[] roots;
    public final MtpRoot[] roots;
    public final @Nullable int[] operationsSupported;
    public final @Nullable int[] operationsSupported;
    public final @Nullable int[] eventsSupported;
    public final @Nullable int[] eventsSupported;


    MtpDeviceRecord(int deviceId, String name, boolean opened, MtpRoot[] roots,
    MtpDeviceRecord(int deviceId, String name, @Nullable String deviceKey, boolean opened,
                    @Nullable int[] operationsSupported, @Nullable int[] eventsSupported) {
                    MtpRoot[] roots, @Nullable int[] operationsSupported,
                    @Nullable int[] eventsSupported) {
        this.deviceId = deviceId;
        this.deviceId = deviceId;
        this.name = name;
        this.name = name;
        this.opened = opened;
        this.opened = opened;
        this.roots = roots;
        this.roots = roots;
        this.deviceKey = deviceKey;
        this.operationsSupported = operationsSupported;
        this.operationsSupported = operationsSupported;
        this.eventsSupported = eventsSupported;
        this.eventsSupported = eventsSupported;
    }
    }
+3 −0
Original line number Original line Diff line number Diff line
@@ -374,6 +374,9 @@ public class MtpDocumentsProvider extends DocumentsProvider {
     * Resumes root scanner to handle the update of device list.
     * Resumes root scanner to handle the update of device list.
     */
     */
    void resumeRootScanner() {
    void resumeRootScanner() {
        if (DEBUG) {
            Log.d(MtpDocumentsProvider.TAG, "resumeRootScanner");
        }
        mRootScanner.resume();
        mRootScanner.resume();
    }
    }


Loading