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

Commit a5d771db authored by Daichi Hirono's avatar Daichi Hirono Committed by android-build-merger
Browse files

Relax mapping rule to make the mapping logic simple.

am: 9fca541a

* commit '9fca541a':
  Relax mapping rule to make the mapping logic simple.
parents 6e6470ce 9fca541a
Loading
Loading
Loading
Loading
+1 −145
Original line number Original line Diff line number Diff line
@@ -73,7 +73,6 @@ class Mapper {
                    extraValuesList,
                    extraValuesList,
                    COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
                    COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
                    EMPTY_ARGS,
                    EMPTY_ARGS,
                    /* heuristic */ false,
                    COLUMN_DEVICE_ID);
                    COLUMN_DEVICE_ID);
            database.setTransactionSuccessful();
            database.setTransactionSuccessful();
            return changed;
            return changed;
@@ -92,16 +91,13 @@ class Mapper {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        database.beginTransaction();
        database.beginTransaction();
        try {
        try {
            final boolean heuristic;
            final String mapColumn;
            final String mapColumn;
            Preconditions.checkState(mMappingMode.containsKey(parentDocumentId));
            Preconditions.checkState(mMappingMode.containsKey(parentDocumentId));
            switch (mMappingMode.get(parentDocumentId)) {
            switch (mMappingMode.get(parentDocumentId)) {
                case MAP_BY_MTP_IDENTIFIER:
                case MAP_BY_MTP_IDENTIFIER:
                    heuristic = false;
                    mapColumn = COLUMN_STORAGE_ID;
                    mapColumn = COLUMN_STORAGE_ID;
                    break;
                    break;
                case MAP_BY_NAME:
                case MAP_BY_NAME:
                    heuristic = true;
                    mapColumn = Document.COLUMN_DISPLAY_NAME;
                    mapColumn = Document.COLUMN_DISPLAY_NAME;
                    break;
                    break;
                default:
                default:
@@ -120,7 +116,6 @@ class Mapper {
                    extraValuesList,
                    extraValuesList,
                    COLUMN_PARENT_DOCUMENT_ID + "=?",
                    COLUMN_PARENT_DOCUMENT_ID + "=?",
                    strings(parentDocumentId),
                    strings(parentDocumentId),
                    heuristic,
                    mapColumn);
                    mapColumn);


            database.setTransactionSuccessful();
            database.setTransactionSuccessful();
@@ -137,16 +132,13 @@ class Mapper {
     * @param documents List of document information.
     * @param documents List of document information.
     */
     */
    synchronized void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
    synchronized void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
        final boolean heuristic;
        final String mapColumn;
        final String mapColumn;
        Preconditions.checkState(mMappingMode.containsKey(parentId));
        Preconditions.checkState(mMappingMode.containsKey(parentId));
        switch (mMappingMode.get(parentId)) {
        switch (mMappingMode.get(parentId)) {
            case MAP_BY_MTP_IDENTIFIER:
            case MAP_BY_MTP_IDENTIFIER:
                heuristic = false;
                mapColumn = COLUMN_OBJECT_HANDLE;
                mapColumn = COLUMN_OBJECT_HANDLE;
                break;
                break;
            case MAP_BY_NAME:
            case MAP_BY_NAME:
                heuristic = true;
                mapColumn = Document.COLUMN_DISPLAY_NAME;
                mapColumn = Document.COLUMN_DISPLAY_NAME;
                break;
                break;
            default:
            default:
@@ -163,17 +155,13 @@ class Mapper {
                null,
                null,
                COLUMN_PARENT_DOCUMENT_ID + "=?",
                COLUMN_PARENT_DOCUMENT_ID + "=?",
                strings(parentId),
                strings(parentId),
                heuristic,
                mapColumn);
                mapColumn);
    }
    }


    @VisibleForTesting
    void clearMapping() {
    void clearMapping() {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        database.beginTransaction();
        database.beginTransaction();
        try {
        try {
            mDatabase.deleteDocumentsAndRootsRecursively(
                    COLUMN_ROW_STATE + " = ?", strings(ROW_STATE_PENDING));
            final ContentValues values = new ContentValues();
            final ContentValues values = new ContentValues();
            values.putNull(COLUMN_OBJECT_HANDLE);
            values.putNull(COLUMN_OBJECT_HANDLE);
            values.putNull(COLUMN_STORAGE_ID);
            values.putNull(COLUMN_STORAGE_ID);
@@ -209,11 +197,6 @@ class Mapper {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        database.beginTransaction();
        database.beginTransaction();
        try {
        try {
            // Delete all pending rows.
            mDatabase.deleteDocumentsAndRootsRecursively(
                    selection + " AND " + COLUMN_ROW_STATE + "=?",
                    DatabaseUtils.appendSelectionArgs(args, strings(ROW_STATE_PENDING)));

            // Set all documents as invalidated.
            // Set all documents as invalidated.
            final ContentValues values = new ContentValues();
            final ContentValues values = new ContentValues();
            values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED);
            values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED);
@@ -245,7 +228,6 @@ 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.
     * @param heuristic Whether the mapping mode is heuristic.
     * @return Whether the method adds new rows.
     * @return Whether the method adds new rows.
     */
     */
    private boolean putDocuments(
    private boolean putDocuments(
@@ -253,7 +235,6 @@ class Mapper {
            @Nullable ContentValues[] rootExtraValuesList,
            @Nullable ContentValues[] rootExtraValuesList,
            String selection,
            String selection,
            String[] args,
            String[] args,
            boolean heuristic,
            String mappingKey) {
            String mappingKey) {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        boolean added = false;
        boolean added = false;
@@ -285,7 +266,7 @@ class Mapper {
                    if (candidateCursor.getCount() == 0) {
                    if (candidateCursor.getCount() == 0) {
                        rowId = database.insert(TABLE_DOCUMENTS, null, values);
                        rowId = database.insert(TABLE_DOCUMENTS, null, values);
                        added = true;
                        added = true;
                    } else if (!heuristic) {
                    } else {
                        candidateCursor.moveToNext();
                        candidateCursor.moveToNext();
                        rowId = candidateCursor.getLong(0);
                        rowId = candidateCursor.getLong(0);
                        database.update(
                        database.update(
@@ -293,9 +274,6 @@ class Mapper {
                                values,
                                values,
                                SELECTION_DOCUMENT_ID,
                                SELECTION_DOCUMENT_ID,
                                strings(rowId));
                                strings(rowId));
                    } else {
                        values.put(COLUMN_ROW_STATE, ROW_STATE_PENDING);
                        rowId = database.insertOrThrow(TABLE_DOCUMENTS, null, values);
                    }
                    }
                    // 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.
@@ -334,84 +312,10 @@ class Mapper {
            selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
            selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
            args = EMPTY_ARGS;
            args = EMPTY_ARGS;
        }
        }
        final String groupKey;
        switch (mMappingMode.get(parentId)) {
            case MAP_BY_MTP_IDENTIFIER:
                groupKey = parentId != null ? COLUMN_OBJECT_HANDLE : COLUMN_STORAGE_ID;
                break;
            case MAP_BY_NAME:
                groupKey = Document.COLUMN_DISPLAY_NAME;
                break;
            default:
                throw new Error("Unexpected mapping state.");
        }
        mMappingMode.remove(parentId);
        mMappingMode.remove(parentId);
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        database.beginTransaction();
        database.beginTransaction();
        try {
        try {
            // Get 1-to-1 mapping of invalidated document and pending document.
            final String invalidatedIdQuery = createStateFilter(
                    ROW_STATE_INVALIDATED, Document.COLUMN_DOCUMENT_ID);
            final String pendingIdQuery = createStateFilter(
                    ROW_STATE_PENDING, Document.COLUMN_DOCUMENT_ID);
            // SQL should be like:
            // SELECT group_concat(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END),
            //        group_concat(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END)
            // WHERE device_id = ? AND parent_document_id IS NULL
            // GROUP BY display_name
            // HAVING count(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END) = 1 AND
            //        count(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END) = 1
            final Cursor mergingCursor = database.query(
                    TABLE_DOCUMENTS,
                    new String[] {
                            "group_concat(" + invalidatedIdQuery + ")",
                            "group_concat(" + pendingIdQuery + ")"
                    },
                    selection,
                    args,
                    groupKey,
                    "count(" + invalidatedIdQuery + ") = 1 AND count(" + pendingIdQuery + ") = 1",
                    null);

            final ContentValues values = new ContentValues();
            while (mergingCursor.moveToNext()) {
                final String invalidatedId = mergingCursor.getString(0);
                final String pendingId = mergingCursor.getString(1);

                // Obtain the new values including the latest object handle from mapping row.
                getFirstRow(
                        TABLE_DOCUMENTS,
                        SELECTION_DOCUMENT_ID,
                        new String[] { pendingId },
                        values);
                values.remove(Document.COLUMN_DOCUMENT_ID);
                values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
                database.update(
                        TABLE_DOCUMENTS,
                        values,
                        SELECTION_DOCUMENT_ID,
                        new String[] { invalidatedId });

                getFirstRow(
                        TABLE_ROOT_EXTRA,
                        SELECTION_ROOT_ID,
                        new String[] { pendingId },
                        values);
                if (values.size() > 0) {
                    values.remove(Root.COLUMN_ROOT_ID);
                    database.update(
                            TABLE_ROOT_EXTRA,
                            values,
                            SELECTION_ROOT_ID,
                            new String[] { invalidatedId });
                }

                // Delete 'pending' row.
                mDatabase.deleteDocumentsAndRootsRecursively(
                        SELECTION_DOCUMENT_ID, new String[] { pendingId });
            }
            mergingCursor.close();

            boolean changed = false;
            boolean changed = false;


            // Delete all invalidated rows that cannot be mapped.
            // Delete all invalidated rows that cannot be mapped.
@@ -421,58 +325,10 @@ class Mapper {
                changed = true;
                changed = true;
            }
            }


            // 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
            // valid with new document ID.
            values.clear();
            values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
            if (database.update(
                    TABLE_DOCUMENTS,
                    values,
                    COLUMN_ROW_STATE + " = ? AND " + selection,
                    DatabaseUtils.appendSelectionArgs(strings(ROW_STATE_PENDING), args)) != 0) {
                changed = true;
            }
            database.setTransactionSuccessful();
            database.setTransactionSuccessful();
            return changed;
            return changed;
        } finally {
        } finally {
            database.endTransaction();
            database.endTransaction();
        }
        }
    }
    }

    /**
     * Obtains values of the first row for the query.
     * @param values ContentValues that the values are stored to.
     * @param table Target table.
     * @param selection Query to select rows.
     * @param args Argument for query.
     */
    private void getFirstRow(String table, String selection, String[] args, ContentValues values) {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        values.clear();
        final Cursor cursor = database.query(table, null, selection, args, null, null, null, "1");
        try {
            if (cursor.getCount() == 0) {
                return;
            }
            cursor.moveToNext();
            DatabaseUtils.cursorRowToContentValues(cursor, values);
        } finally {
            cursor.close();
        }
    }

    /**
     * Gets SQL expression that represents the given value or NULL depends on the row state.
     * You must pass static constants to this methods otherwise you may be suffered from SQL
     * injections.
     * @param state Expected row state.
     * @param a SQL value.
     * @return Expression that represents a if the row state is expected one, and represents NULL
     *     otherwise.
     */
    private static String createStateFilter(int state, String a) {
        return "CASE WHEN " + COLUMN_ROW_STATE + " = " + Integer.toString(state) +
                " THEN " + a + " ELSE NULL END";
    }
}
}
+0 −8
Original line number Original line Diff line number Diff line
@@ -77,14 +77,6 @@ class MtpDatabaseConstants {
     */
     */
    static final int ROW_STATE_INVALIDATED = 1;
    static final int ROW_STATE_INVALIDATED = 1;


    /**
     * The state represents the raw has a valid object handle but it may be going to be mapped with
     * another rows invalidated. After fetching all documents under the parent, the database tries
     * to map the pending documents and the invalidated documents in order to keep old document ID
     * alive.
     */
    static final int ROW_STATE_PENDING = 2;

    /**
    /**
     * Mapping mode that uses MTP identifier to find corresponding rows.
     * Mapping mode that uses MTP identifier to find corresponding rows.
     */
     */
+9 −8
Original line number Original line Diff line number Diff line
@@ -310,14 +310,14 @@ public class MtpDatabaseTest extends AndroidTestCase {
            assertEquals(3, cursor.getCount());
            assertEquals(3, cursor.getCount());
            cursor.moveToNext();
            cursor.moveToNext();
            assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
            assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
            assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.moveToNext();
            cursor.moveToNext();
            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
            assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
            assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.moveToNext();
            cursor.moveToNext();
            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
            assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.close();
            cursor.close();
@@ -333,7 +333,7 @@ public class MtpDatabaseTest extends AndroidTestCase {
            assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
            assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.moveToNext();
            cursor.moveToNext();
            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
            assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.close();
            cursor.close();
@@ -387,7 +387,7 @@ public class MtpDatabaseTest extends AndroidTestCase {
            assertEquals(4, cursor.getCount());
            assertEquals(4, cursor.getCount());


            cursor.moveToPosition(3);
            cursor.moveToPosition(3);
            assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
            assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
            assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
            assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));


@@ -406,7 +406,7 @@ public class MtpDatabaseTest extends AndroidTestCase {
            assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
            assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));


            cursor.moveToNext();
            cursor.moveToNext();
            assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
            assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
            assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
            assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.close();
            cursor.close();
@@ -544,7 +544,7 @@ public class MtpDatabaseTest extends AndroidTestCase {
            assertEquals(1, cursor.getCount());
            assertEquals(1, cursor.getCount());
            cursor.moveToNext();
            cursor.moveToNext();
            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
            assertEquals(201, getInt(cursor, COLUMN_OBJECT_HANDLE));
            cursor.close();
            cursor.close();
        }
        }
    }
    }
@@ -626,11 +626,12 @@ public class MtpDatabaseTest extends AndroidTestCase {
            final Cursor cursor = mDatabase.queryRootDocuments(columns);
            final Cursor cursor = mDatabase.queryRootDocuments(columns);
            assertEquals(2, cursor.getCount());
            assertEquals(2, cursor.getCount());
            cursor.moveToNext();
            cursor.moveToNext();
            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
            // One reuses exisitng document ID 1.
            assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.moveToNext();
            cursor.moveToNext();
            assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(201, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals(201, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.close();
            cursor.close();