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

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

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

parents f52ebd11 3bb37e7f
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);
    }