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

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

Keep metadata of documents as disconnected status after the device is disconnected.

am: 8e87364a

* commit '8e87364a':
  Keep metadata of documents as disconnected status after the device is disconnected.
parents ac197eff 8e87364a
Loading
Loading
Loading
Loading
+46 −21
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.mtp;

import static com.android.mtp.MtpDatabaseConstants.*;

import android.annotation.Nullable;
import android.content.ContentValues;
import android.database.Cursor;
@@ -26,6 +24,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.mtp.MtpObjectInfo;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.util.Log;

import com.android.internal.util.Preconditions;

@@ -33,6 +32,7 @@ import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;

import static com.android.mtp.MtpDatabaseConstants.*;
import static com.android.mtp.MtpDatabase.strings;

/**
@@ -75,7 +75,7 @@ class Mapper {
                    extraValuesList,
                    COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
                    EMPTY_ARGS,
                    COLUMN_DEVICE_ID);
                    Document.COLUMN_DISPLAY_NAME);
            database.setTransactionSuccessful();
            return changed;
        } finally {
@@ -172,13 +172,16 @@ class Mapper {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        database.beginTransaction();
        try {
            final ContentValues values = new ContentValues();
            values.putNull(COLUMN_OBJECT_HANDLE);
            values.putNull(COLUMN_STORAGE_ID);
            values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED);
            database.update(TABLE_DOCUMENTS, values, null, null);
            database.setTransactionSuccessful();
            mMappingMode.clear();
            // Disconnect all device rows.
            try {
                startAddingDocuments(null);
                stopAddingDocuments(null);
            } catch (FileNotFoundException exception) {
                Log.e(MtpDocumentsProvider.TAG, "Unexpected FileNotFoundException.", exception);
                throw new RuntimeException(exception);
            }
            database.setTransactionSuccessful();
        } finally {
            database.endTransaction();
        }
@@ -210,16 +213,20 @@ class Mapper {
            getParentOrHaltMapping(parentDocumentId);
            Preconditions.checkState(!mMappingMode.containsKey(parentDocumentId));

            // Set all documents as invalidated.
            // Set all valid documents as invalidated.
            final ContentValues values = new ContentValues();
            values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED);
            database.update(TABLE_DOCUMENTS, values, selection, args);
            database.update(
                    TABLE_DOCUMENTS,
                    values,
                    selection + " AND " + COLUMN_ROW_STATE + " = ?",
                    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_STORAGE_ID + " IS NULL",
                    selection + " AND " + COLUMN_DEVICE_ID + " IS NULL",
                    args) > 0;
            database.setTransactionSuccessful();
            mMappingMode.put(
@@ -270,11 +277,13 @@ class Mapper {
                        TABLE_DOCUMENTS,
                        strings(Document.COLUMN_DOCUMENT_ID),
                        selection + " AND " +
                        COLUMN_ROW_STATE + "=? AND " +
                        COLUMN_ROW_STATE + " IN (?, ?) AND " +
                        mappingKey + "=?",
                        DatabaseUtils.appendSelectionArgs(
                                args,
                                strings(ROW_STATE_INVALIDATED, values.getAsString(mappingKey))),
                                strings(ROW_STATE_INVALIDATED,
                                        ROW_STATE_DISCONNECTED,
                                        values.getAsString(mappingKey))),
                        null,
                        null,
                        null,
@@ -335,17 +344,28 @@ class Mapper {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        database.beginTransaction();
        try {
            getParentOrHaltMapping(parentId);
            final Identifier parentIdentifier = getParentOrHaltMapping(parentId);
            Preconditions.checkState(mMappingMode.containsKey(parentId));
            mMappingMode.remove(parentId);

            boolean changed = false;
            // Delete all invalidated rows that cannot be mapped.
            // Delete/disconnect all invalidated rows that cannot be mapped.
            final boolean keepUnmatchedDocument =
                    parentIdentifier == null ||
                    parentIdentifier.mDocumentType == DOCUMENT_TYPE_DEVICE;
            if (keepUnmatchedDocument) {
                if (mDatabase.disconnectDocumentsRecursively(
                        COLUMN_ROW_STATE + " = ? AND " + selection,
                        DatabaseUtils.appendSelectionArgs(strings(ROW_STATE_INVALIDATED), args))) {
                    changed = true;
                }
            } else {
                if (mDatabase.deleteDocumentsAndRootsRecursively(
                        COLUMN_ROW_STATE + " = ? AND " + selection,
                        DatabaseUtils.appendSelectionArgs(strings(ROW_STATE_INVALIDATED), args))) {
                    changed = true;
                }
            }

            database.setTransactionSuccessful();
            return changed;
@@ -368,7 +388,12 @@ class Mapper {
            return null;
        }
        try {
            return mDatabase.createIdentifier(parentId);
            final Identifier identifier = mDatabase.createIdentifier(parentId);
            if (mDatabase.getRowState(parentId) == ROW_STATE_DISCONNECTED) {
                throw new FileNotFoundException(
                        "document: " + parentId + " is in disconnected device.");
            }
            return identifier;
        } catch (FileNotFoundException error) {
            mMappingMode.remove(parentId);
            throw error;
+79 −13
Original line number Diff line number Diff line
@@ -294,15 +294,6 @@ class MtpDatabase {
                "1");
    }

    /**
     * Remove all rows belong to a device.
     * @param deviceId Device ID.
     */
    void removeDeviceRows(int deviceId) {
        // Call non-recursive version because it anyway deletes all rows in the devices.
        deleteDocumentsAndRoots(COLUMN_DEVICE_ID + "=?", strings(deviceId));
    }

    @Nullable String getDocumentIdForDevice(int deviceId) {
        final Cursor cursor = mDatabase.query(
                TABLE_DOCUMENTS,
@@ -456,7 +447,43 @@ class MtpDatabase {
        }
    }

    private boolean deleteDocumentsAndRoots(String selection, String[] args) {
    /**
     * Marks the documents and their child as disconnected documents.
     * @param selection
     * @param args
     * @return True if at least one row is updated.
     */
    boolean disconnectDocumentsRecursively(String selection, String[] args) {
        mDatabase.beginTransaction();
        try {
            boolean changed = false;
            try (final Cursor cursor = mDatabase.query(
                    TABLE_DOCUMENTS,
                    strings(Document.COLUMN_DOCUMENT_ID),
                    selection,
                    args,
                    null,
                    null,
                    null)) {
                while (cursor.moveToNext()) {
                    if (disconnectDocumentsRecursively(
                            COLUMN_PARENT_DOCUMENT_ID + " = ?",
                            strings(cursor.getString(0)))) {
                        changed = true;
                    }
                }
            }
            if (disconnectDocuments(selection, args)) {
                changed = true;
            }
            mDatabase.setTransactionSuccessful();
            return changed;
        } finally {
            mDatabase.endTransaction();
        }
    }

    boolean deleteDocumentsAndRoots(String selection, String[] args) {
        mDatabase.beginTransaction();
        try {
            int deleted = 0;
@@ -481,6 +508,39 @@ class MtpDatabase {
        }
    }

    boolean disconnectDocuments(String selection, String[] args) {
        mDatabase.beginTransaction();
        try {
            final ContentValues values = new ContentValues();
            values.put(COLUMN_ROW_STATE, ROW_STATE_DISCONNECTED);
            values.putNull(COLUMN_DEVICE_ID);
            values.putNull(COLUMN_STORAGE_ID);
            values.putNull(COLUMN_OBJECT_HANDLE);
            final boolean updated = mDatabase.update(TABLE_DOCUMENTS, values, selection, args) != 0;
            mDatabase.setTransactionSuccessful();
            return updated;
        } finally {
            mDatabase.endTransaction();
        }
    }

    int getRowState(String documentId) throws FileNotFoundException {
        try (final Cursor cursor = mDatabase.query(
                TABLE_DOCUMENTS,
                strings(COLUMN_ROW_STATE),
                SELECTION_DOCUMENT_ID,
                strings(documentId),
                null,
                null,
                null)) {
            if (cursor.getCount() == 0) {
                throw new FileNotFoundException();
            }
            cursor.moveToNext();
            return cursor.getInt(0);
        }
    }

    private static class OpenHelper extends SQLiteOpenHelper {
        public OpenHelper(Context context, int flags) {
            super(context,
@@ -497,6 +557,12 @@ class MtpDatabase {

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            if (oldVersion == 1) {
                db.execSQL("DROP TABLE " + TABLE_DOCUMENTS);
                db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA);
                onCreate(db);
                return;
            }
            throw new UnsupportedOperationException();
        }
    }
+8 −2
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ import java.util.Map;
 * Class containing MtpDatabase constants.
 */
class MtpDatabaseConstants {
    static final int DATABASE_VERSION = 1;
    static final int DATABASE_VERSION = 2;
    static final String DATABASE_NAME = "database";

    static final int FLAG_DATABASE_IN_MEMORY = 1;
@@ -77,6 +77,12 @@ class MtpDatabaseConstants {
     */
    static final int ROW_STATE_INVALIDATED = 1;

    /**
     * The documents are of device/storage that are disconnected now. The documents are invisible
     * but their document ID will be reuse when the device/storage is connected again.
     */
    static final int ROW_STATE_DISCONNECTED = 2;

    /**
     * Mapping mode that uses MTP identifier to find corresponding rows.
     */
@@ -113,7 +119,7 @@ class MtpDatabaseConstants {
            "CREATE TABLE " + TABLE_DOCUMENTS + " (" +
            Document.COLUMN_DOCUMENT_ID +
                " INTEGER PRIMARY KEY AUTOINCREMENT," +
            COLUMN_DEVICE_ID + " INTEGER NOT NULL," +
            COLUMN_DEVICE_ID + " INTEGER," +
            COLUMN_STORAGE_ID + " INTEGER," +
            COLUMN_OBJECT_HANDLE + " INTEGER," +
            COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
+14 −59
Original line number Diff line number Diff line
@@ -95,7 +95,8 @@ public class MtpDatabaseTest extends AndroidTestCase {
            assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
            assertEquals(1, getInt(cursor, COLUMN_STORAGE_ID));
            assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
            assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE));
            assertEquals(
                    DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE));
            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
            assertTrue(isNull(cursor, COLUMN_SUMMARY));
            assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
@@ -103,7 +104,8 @@ public class MtpDatabaseTest extends AndroidTestCase {
            assertEquals(0, getInt(cursor, COLUMN_FLAGS));
            assertEquals(1000, getInt(cursor, COLUMN_SIZE));
            assertEquals(
                    MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, getInt(cursor, COLUMN_DOCUMENT_TYPE));
                    MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE,
                    getInt(cursor, COLUMN_DOCUMENT_TYPE));

            cursor.close();
        }
@@ -296,15 +298,7 @@ public class MtpDatabaseTest extends AndroidTestCase {

        {
            final Cursor cursor = mDatabase.queryRootDocuments(columns);
            assertEquals(2, cursor.getCount());
            cursor.moveToNext();
            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.moveToNext();
            assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
            assertEquals(0, cursor.getCount());
            cursor.close();
        }

@@ -314,28 +308,10 @@ public class MtpDatabaseTest extends AndroidTestCase {
                new MtpRoot(0, 200, "Storage A", 2000, 0, ""),
                new MtpRoot(0, 202, "Storage C", 2002, 0, "")
        });

        {
            final Cursor cursor = mDatabase.queryRootDocuments(columns);
            assertEquals(3, cursor.getCount());
            cursor.moveToNext();
            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.moveToNext();
            assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.moveToNext();
            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
            assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
            cursor.close();
        }

        mDatabase.getMapper().stopAddingDocuments("1");

        {
            // After compeleting mapping, Storage A can be obtained with new storage ID.
            final Cursor cursor = mDatabase.queryRootDocuments(columns);
            assertEquals(2, cursor.getCount());
            cursor.moveToNext();
@@ -372,24 +348,9 @@ public class MtpDatabaseTest extends AndroidTestCase {
        addTestStorage("1");

        {
            // Don't return objects that lost MTP object handles.
            final Cursor cursor = mDatabase.queryChildDocuments(columns, "2");
            assertEquals(3, cursor.getCount());

            cursor.moveToNext();
            assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
            assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));

            cursor.moveToNext();
            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
            assertEquals("image.jpg", getString(cursor, COLUMN_DISPLAY_NAME));

            cursor.moveToNext();
            assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
            assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
            assertEquals("music.mp3", getString(cursor, COLUMN_DISPLAY_NAME));

            assertEquals(0, cursor.getCount());
            cursor.close();
        }

@@ -598,10 +559,7 @@ public class MtpDatabaseTest extends AndroidTestCase {
                Root.COLUMN_AVAILABLE_BYTES
        };

        mDatabase.getMapper().startAddingDocuments(null);
        mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
                0, "Device", false,  new MtpRoot[0], null, null));
        mDatabase.getMapper().stopAddingDocuments(null);
        addTestDevice();

        mDatabase.getMapper().startAddingDocuments("1");
        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
@@ -701,10 +659,7 @@ public class MtpDatabaseTest extends AndroidTestCase {
    }

    public void testReplaceExistingRoots() throws Exception {
        mDatabase.getMapper().startAddingDocuments(null);
        mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
                0, "Device", true, new MtpRoot[0], null, null));
        mDatabase.getMapper().stopAddingDocuments(null);
        addTestDevice();

        // The client code should be able to replace existing rows with new information.
        // Add one.
@@ -802,10 +757,7 @@ public class MtpDatabaseTest extends AndroidTestCase {

    public void testQueryRoots() throws Exception {
        // Add device document.
        mDatabase.getMapper().startAddingDocuments(null);
        mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
                0, "Device", false, new MtpRoot[0], null, null));
        mDatabase.getMapper().stopAddingDocuments(null);
        addTestDevice();

        // It the device does not have storages, it shows a device root.
        {
@@ -930,6 +882,9 @@ public class MtpDatabaseTest extends AndroidTestCase {

        // The new document should not be mapped with existing invalidated document.
        mDatabase.getMapper().clearMapping();
        addTestDevice();
        addTestStorage("1");

        mDatabase.getMapper().startAddingDocuments("2");
        mDatabase.putNewDocument(
                0,