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

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

Merge "Cleans up the metadata in MtpDatabase at the first launch after booting." into nyc-dev

am: 7b5479f2

* commit '7b5479f2':
  Cleans up the metadata in MtpDatabase at the first launch after booting.
parents faa1b930 7b5479f2
Loading
Loading
Loading
Loading
+79 −4
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.database.sqlite.SQLiteQueryBuilder;
import android.media.MediaFile;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
@@ -40,8 +41,9 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

/**
 * Database for MTP objects.
@@ -606,7 +608,7 @@ class MtpDatabase {
     * @param deviceId Device to find documents.
     * @return Identifier of found document or null.
     */
    public @Nullable Identifier getUnmappedDocumentsParent(int deviceId) {
    @Nullable Identifier getUnmappedDocumentsParent(int deviceId) {
        final String fromClosure =
                TABLE_DOCUMENTS + " AS child INNER JOIN " +
                TABLE_DOCUMENTS + " AS parent ON " +
@@ -643,6 +645,65 @@ class MtpDatabase {
        }
    }

    /**
     * Removes metadata except for data used by outgoingPersistedUriPermissions.
     */
    void cleanDatabase(Uri[] outgoingPersistedUris) {
        mDatabase.beginTransaction();
        try {
            final Set<String> ids = new HashSet<>();
            for (final Uri uri : outgoingPersistedUris) {
                String documentId = DocumentsContract.getDocumentId(uri);
                while (documentId != null) {
                    if (ids.contains(documentId)) {
                        break;
                    }
                    ids.add(documentId);
                    try (final Cursor cursor = mDatabase.query(
                            TABLE_DOCUMENTS,
                            strings(COLUMN_PARENT_DOCUMENT_ID),
                            SELECTION_DOCUMENT_ID,
                            strings(documentId),
                            null,
                            null,
                            null)) {
                        documentId = cursor.moveToNext() ? cursor.getString(0) : null;
                    }
                }
            }
            deleteDocumentsAndRoots(
                    Document.COLUMN_DOCUMENT_ID + " NOT IN " + getIdList(ids), null);
            mDatabase.setTransactionSuccessful();
        } finally {
            mDatabase.endTransaction();
        }
    }

    int getLastBootCount() {
        try (final Cursor cursor = mDatabase.query(
                TABLE_LAST_BOOT_COUNT, strings(COLUMN_VALUE), null, null, null, null, null)) {
            if (cursor.moveToNext()) {
                return cursor.getInt(0);
            } else {
                return 0;
            }
        }
    }

    void setLastBootCount(int value) {
        Preconditions.checkArgumentNonnegative(value, "Boot count must not be negative.");
        mDatabase.beginTransaction();
        try {
            final ContentValues values = new ContentValues();
            values.put(COLUMN_VALUE, value);
            mDatabase.delete(TABLE_LAST_BOOT_COUNT, null, null);
            mDatabase.insert(TABLE_LAST_BOOT_COUNT, null, values);
            mDatabase.setTransactionSuccessful();
        } finally {
            mDatabase.endTransaction();
        }
    }

    private static class OpenHelper extends SQLiteOpenHelper {
        public OpenHelper(Context context, int flags) {
            super(context,
@@ -655,12 +716,14 @@ class MtpDatabase {
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(QUERY_CREATE_DOCUMENTS);
            db.execSQL(QUERY_CREATE_ROOT_EXTRA);
            db.execSQL(QUERY_CREATE_LAST_BOOT_COUNT);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("DROP TABLE " + TABLE_DOCUMENTS);
            db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA);
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_DOCUMENTS);
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_ROOT_EXTRA);
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_LAST_BOOT_COUNT);
            onCreate(db);
        }
    }
@@ -818,4 +881,16 @@ class MtpDatabase {
        }
        return results;
    }

    private static String getIdList(Set<String> ids) {
        String result = "(";
        for (final String id : ids) {
            if (result.length() > 1) {
                result += ",";
            }
            result += id;
        }
        result += ")";
        return result;
    }
}
+16 −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 = 4;
    static final int DATABASE_VERSION = 5;
    static final String DATABASE_NAME = "database";

    static final int FLAG_DATABASE_IN_MEMORY = 1;
@@ -47,6 +47,11 @@ class MtpDatabaseConstants {
     */
    static final String TABLE_ROOT_EXTRA = "RootExtra";

    /**
     * Table containing last boot count.
     */
    static final String TABLE_LAST_BOOT_COUNT = "LastBootCount";

    /**
     * 'FROM' closure of joining TABLE_DOCUMENTS and TABLE_ROOT_EXTRA.
     */
@@ -62,7 +67,13 @@ class MtpDatabaseConstants {
    static final String COLUMN_PARENT_DOCUMENT_ID = "parent_document_id";
    static final String COLUMN_DOCUMENT_TYPE = "document_type";
    static final String COLUMN_ROW_STATE = "row_state";
    static final String COLUMN_MAPPING_KEY = "column_mapping_key";
    static final String COLUMN_MAPPING_KEY = "mapping_key";

    /**
     * Value for TABLE_LAST_BOOT_COUNT.
     * Type: INTEGER
     */
    static final String COLUMN_VALUE = "value";

    /**
     * The state represents that the row has a valid object handle.
@@ -133,6 +144,9 @@ class MtpDatabaseConstants {
            Root.COLUMN_CAPACITY_BYTES + " INTEGER," +
            Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);";

    static final String QUERY_CREATE_LAST_BOOT_COUNT =
            "CREATE TABLE " + TABLE_LAST_BOOT_COUNT + " (value INTEGER NOT NULL);";

    /**
     * Map for columns names to provide DocumentContract.Root compatible columns.
     * @see SQLiteQueryBuilder#setProjectionMap(Map)
+21 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.mtp;

import android.content.ContentResolver;
import android.content.UriPermission;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
@@ -25,6 +26,7 @@ import android.graphics.Point;
import android.media.MediaFile;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
@@ -33,6 +35,8 @@ import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
@@ -42,6 +46,7 @@ import com.android.mtp.exceptions.BusyDeviceException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
@@ -95,6 +100,21 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
        mAppFuse = new AppFuse(TAG, new AppFuseCallback());
        mIntentSender = new ServiceIntentSender(getContext());

        // Check boot count and cleans database if it's first time to launch MtpDocumentsProvider
        // after booting.
        final int bootCount = Settings.Global.getInt(mResolver, Settings.Global.BOOT_COUNT, -1);
        final int lastBootCount = mDatabase.getLastBootCount();
        if (bootCount != -1 && bootCount != lastBootCount) {
            mDatabase.setLastBootCount(bootCount);
            final List<UriPermission> permissions = mResolver.getOutgoingPersistedUriPermissions();
            final Uri[] uris = new Uri[permissions.size()];
            for (int i = 0; i < permissions.size(); i++) {
                uris[i] = permissions.get(i).getUri();
            }
            mDatabase.cleanDatabase(uris);
        }

        // TODO: Mount AppFuse on demands.
        try {
            mAppFuse.mount(getContext().getSystemService(StorageManager.class));
@@ -122,6 +142,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
        mAppFuse = new AppFuse(TAG, new AppFuseCallback());
        mIntentSender = intentSender;

        // TODO: Mount AppFuse on demands.
        try {
            mAppFuse.mount(storageManager);
+58 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.mtp;
import android.database.Cursor;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
@@ -26,6 +27,7 @@ import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;

import java.io.FileNotFoundException;
import java.util.Arrays;

import static android.provider.DocumentsContract.Document.*;
import static com.android.mtp.MtpDatabase.strings;
@@ -1023,6 +1025,62 @@ public class MtpDatabaseTest extends AndroidTestCase {
        assertFalse(mDatabase.getMapper().stopAddingDocuments(null));
    }

    public void testSetBootCount() {
        assertEquals(0, mDatabase.getLastBootCount());
        mDatabase.setLastBootCount(10);
        assertEquals(10, mDatabase.getLastBootCount());
        try {
            mDatabase.setLastBootCount(-1);
            fail();
        } catch (IllegalArgumentException e) {}
    }

    public void testCleanDatabase() throws FileNotFoundException {
        // Add tree.
        addTestDevice();
        addTestStorage("1");
        mDatabase.getMapper().startAddingDocuments("2");
        mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
                createDocument(100, "apple.txt", MtpConstants.FORMAT_TEXT, 1024),
                createDocument(101, "orange.txt", MtpConstants.FORMAT_TEXT, 1024),
        });
        mDatabase.getMapper().stopAddingDocuments("2");

        // Disconnect the device.
        mDatabase.getMapper().startAddingDocuments(null);
        mDatabase.getMapper().stopAddingDocuments(null);

        // Clean database.
        mDatabase.cleanDatabase(new Uri[] {
                DocumentsContract.buildDocumentUri(MtpDocumentsProvider.AUTHORITY, "3")
        });

        // Add tree again.
        addTestDevice();
        addTestStorage("1");
        mDatabase.getMapper().startAddingDocuments("2");
        mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
                createDocument(100, "apple.txt", MtpConstants.FORMAT_TEXT, 1024),
                createDocument(101, "orange.txt", MtpConstants.FORMAT_TEXT, 1024),
        });
        mDatabase.getMapper().stopAddingDocuments("2");

        try (final Cursor cursor = mDatabase.queryChildDocuments(
                strings(COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME), "2")) {
            assertEquals(2, cursor.getCount());

            // Persistent uri uses the same ID.
            cursor.moveToNext();
            assertEquals("3", cursor.getString(0));
            assertEquals("apple.txt", cursor.getString(1));

            // Others does not.
            cursor.moveToNext();
            assertEquals("5", cursor.getString(0));
            assertEquals("orange.txt", cursor.getString(1));
        }
    }

    private void addTestDevice() throws FileNotFoundException {
        TestUtil.addTestDevice(mDatabase);
    }