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

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

Merge "Add device document to MtpDatabase."

parents f4f09b1a c0ae45be
Loading
Loading
Loading
Loading
+34 −13
Original line number Original line Diff line number Diff line
@@ -42,6 +42,7 @@ import static com.android.mtp.MtpDatabase.strings;
 * Also see the comments of {@link MtpDatabase}.
 * Also see the comments of {@link MtpDatabase}.
 */
 */
class Mapper {
class Mapper {
    private static final String[] EMPTY_ARGS = new String[0];
    private final MtpDatabase mDatabase;
    private final MtpDatabase mDatabase;


    /**
    /**
@@ -55,21 +56,43 @@ class Mapper {
        mDatabase = database;
        mDatabase = database;
    }
    }


    synchronized String putDeviceDocument(int deviceId, String name, MtpRoot[] roots) {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        Preconditions.checkState(mMappingMode.containsKey(/* no parent for root */ null));
        database.beginTransaction();
        try {
            final ContentValues[] valuesList = new ContentValues[1];
            valuesList[0] = new ContentValues();
            MtpDatabase.getDeviceDocumentValues(valuesList[0], deviceId, name, roots);
            putDocuments(
                    valuesList,
                    COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
                    EMPTY_ARGS,
                    /* heuristic */ false,
                    COLUMN_DEVICE_ID);
            database.setTransactionSuccessful();
            return valuesList[0].getAsString(Document.COLUMN_DOCUMENT_ID);
        } finally {
            database.endTransaction();
        }
    }

    /**
    /**
     * Puts root information to database.
     * Puts root information to database.
     * @param deviceId Device ID
     * @param parentDocumentId Document ID of device document.
     * @param resources Resources required to localize root name.
     * @param resources Resources required to localize root name.
     * @param roots List of root information.
     * @param roots List of root information.
     * @return If roots are added or removed from the database.
     * @return If roots are added or removed from the database.
     */
     */
    synchronized boolean putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) {
    synchronized boolean putRootDocuments(
            String parentDocumentId, Resources resources, MtpRoot[] roots) {
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        database.beginTransaction();
        database.beginTransaction();
        try {
        try {
            final boolean heuristic;
            final boolean heuristic;
            final String mapColumn;
            final String mapColumn;
            Preconditions.checkState(mMappingMode.containsKey(/* no parent for root */ null));
            Preconditions.checkState(mMappingMode.containsKey(parentDocumentId));
            switch (mMappingMode.get(/* no parent for root */ null)) {
            switch (mMappingMode.get(parentDocumentId)) {
                case MAP_BY_MTP_IDENTIFIER:
                case MAP_BY_MTP_IDENTIFIER:
                    heuristic = false;
                    heuristic = false;
                    mapColumn = COLUMN_STORAGE_ID;
                    mapColumn = COLUMN_STORAGE_ID;
@@ -83,16 +106,14 @@ class Mapper {
            }
            }
            final ContentValues[] valuesList = new ContentValues[roots.length];
            final ContentValues[] valuesList = new ContentValues[roots.length];
            for (int i = 0; i < roots.length; i++) {
            for (int i = 0; i < roots.length; i++) {
                if (roots[i].mDeviceId != deviceId) {
                    throw new IllegalArgumentException();
                }
                valuesList[i] = new ContentValues();
                valuesList[i] = new ContentValues();
                MtpDatabase.getRootDocumentValues(valuesList[i], resources, roots[i]);
                MtpDatabase.getStorageDocumentValues(
                        valuesList[i], resources, parentDocumentId, roots[i]);
            }
            }
            final boolean changed = putDocuments(
            final boolean changed = putDocuments(
                    valuesList,
                    valuesList,
                    COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
                    COLUMN_PARENT_DOCUMENT_ID + "=?",
                    new String[0],
                    strings(parentDocumentId),
                    heuristic,
                    heuristic,
                    mapColumn);
                    mapColumn);
            final ContentValues values = new ContentValues();
            final ContentValues values = new ContentValues();
@@ -146,7 +167,7 @@ class Mapper {
        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.getChildDocumentValues(
            MtpDatabase.getObjectDocumentValues(
                    valuesList[i], deviceId, parentId, documents[i]);
                    valuesList[i], deviceId, parentId, documents[i]);
        }
        }
        putDocuments(
        putDocuments(
@@ -193,7 +214,7 @@ class Mapper {
            args = strings(parentDocumentId);
            args = strings(parentDocumentId);
        } else {
        } else {
            selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
            selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
            args = new String[0];
            args = EMPTY_ARGS;
        }
        }


        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
@@ -312,7 +333,7 @@ class Mapper {
            args = strings(parentId);
            args = strings(parentId);
        } else {
        } else {
            selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
            selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
            args = new String[0];
            args = EMPTY_ARGS;
        }
        }
        final String groupKey;
        final String groupKey;
        switch (mMappingMode.get(parentId)) {
        switch (mMappingMode.get(parentId)) {
+39 −13
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteQueryBuilder;
@@ -35,9 +36,9 @@ import android.provider.DocumentsContract.Root;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;


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


/**
/**
 * Database for MTP objects.
 * Database for MTP objects.
@@ -107,11 +108,14 @@ class MtpDatabase {
     * @return Database cursor.
     * @return Database cursor.
     */
     */
    Cursor queryRoots(String[] columnNames) {
    Cursor queryRoots(String[] columnNames) {
        return mDatabase.query(
        final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
                VIEW_ROOTS,
        builder.setTables(JOIN_ROOTS);
        builder.setProjectionMap(COLUMN_MAP_ROOTS);
        return builder.query(
                mDatabase,
                columnNames,
                columnNames,
                COLUMN_ROW_STATE + " IN (?, ?)",
                COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + " = ?",
                strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED),
                strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_STORAGE),
                null,
                null,
                null,
                null,
                null);
                null);
@@ -128,8 +132,8 @@ class MtpDatabase {
        return mDatabase.query(
        return mDatabase.query(
                TABLE_DOCUMENTS,
                TABLE_DOCUMENTS,
                columnNames,
                columnNames,
                COLUMN_ROW_STATE + " IN (?, ?)",
                COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + "=?",
                strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED),
                strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_STORAGE),
                null,
                null,
                null,
                null,
                null);
                null);
@@ -216,7 +220,7 @@ class MtpDatabase {
     */
     */
    String putNewDocument(int deviceId, String parentDocumentId, MtpObjectInfo info) {
    String putNewDocument(int deviceId, String parentDocumentId, MtpObjectInfo info) {
        final ContentValues values = new ContentValues();
        final ContentValues values = new ContentValues();
        getChildDocumentValues(values, deviceId, parentDocumentId, info);
        getObjectDocumentValues(values, deviceId, parentDocumentId, info);
        mDatabase.beginTransaction();
        mDatabase.beginTransaction();
        try {
        try {
            final long id = mDatabase.insert(TABLE_DOCUMENTS, null, values);
            final long id = mDatabase.insert(TABLE_DOCUMENTS, null, values);
@@ -344,7 +348,6 @@ class MtpDatabase {
        public void onCreate(SQLiteDatabase db) {
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(QUERY_CREATE_DOCUMENTS);
            db.execSQL(QUERY_CREATE_DOCUMENTS);
            db.execSQL(QUERY_CREATE_ROOT_EXTRA);
            db.execSQL(QUERY_CREATE_ROOT_EXTRA);
            db.execSQL(QUERY_CREATE_VIEW_ROOTS);
        }
        }


        @Override
        @Override
@@ -358,18 +361,41 @@ class MtpDatabase {
        context.deleteDatabase(DATABASE_NAME);
        context.deleteDatabase(DATABASE_NAME);
    }
    }


    static void getDeviceDocumentValues(
            ContentValues values, int deviceId, String name, MtpRoot[] roots) {
        values.clear();
        values.put(COLUMN_DEVICE_ID, deviceId);
        values.putNull(COLUMN_STORAGE_ID);
        values.putNull(COLUMN_OBJECT_HANDLE);
        values.putNull(COLUMN_PARENT_DOCUMENT_ID);
        values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
        values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_DEVICE);
        values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
        values.put(Document.COLUMN_DISPLAY_NAME, name);
        values.putNull(Document.COLUMN_SUMMARY);
        values.putNull(Document.COLUMN_LAST_MODIFIED);
        values.put(Document.COLUMN_ICON, R.drawable.ic_root_mtp);
        values.put(Document.COLUMN_FLAGS, 0);
        long size = 0;
        for (final MtpRoot root : roots) {
            size += root.mMaxCapacity - root.mFreeSpace;
        }
        values.put(Document.COLUMN_SIZE, size);
    }

    /**
    /**
     * Gets {@link ContentValues} for the given root.
     * Gets {@link ContentValues} for the given root.
     * @param values {@link ContentValues} that receives values.
     * @param values {@link ContentValues} that receives values.
     * @param resources Resources used to get localized root name.
     * @param resources Resources used to get localized root name.
     * @param root Root to be converted {@link ContentValues}.
     * @param root Root to be converted {@link ContentValues}.
     */
     */
    static void getRootDocumentValues(ContentValues values, Resources resources, MtpRoot root) {
    static void getStorageDocumentValues(
            ContentValues values, Resources resources, String parentDocumentId, MtpRoot root) {
        values.clear();
        values.clear();
        values.put(COLUMN_DEVICE_ID, root.mDeviceId);
        values.put(COLUMN_DEVICE_ID, root.mDeviceId);
        values.put(COLUMN_STORAGE_ID, root.mStorageId);
        values.put(COLUMN_STORAGE_ID, root.mStorageId);
        values.putNull(COLUMN_OBJECT_HANDLE);
        values.putNull(COLUMN_OBJECT_HANDLE);
        values.putNull(COLUMN_PARENT_DOCUMENT_ID);
        values.put(COLUMN_PARENT_DOCUMENT_ID, parentDocumentId);
        values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
        values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
        values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_STORAGE);
        values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_STORAGE);
        values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
        values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
@@ -389,7 +415,7 @@ class MtpDatabase {
     * @param parentId Parent document ID of the object.
     * @param parentId Parent document ID of the object.
     * @param info MTP object info.
     * @param info MTP object info.
     */
     */
    static void getChildDocumentValues(
    static void getObjectDocumentValues(
            ContentValues values, int deviceId, String parentId, MtpObjectInfo info) {
            ContentValues values, int deviceId, String parentId, MtpObjectInfo info) {
        values.clear();
        values.clear();
        final String mimeType = info.getFormat() == MtpConstants.FORMAT_ASSOCIATION ?
        final String mimeType = info.getFormat() == MtpConstants.FORMAT_ASSOCIATION ?
+36 −29
Original line number Original line Diff line number Diff line
@@ -16,9 +16,13 @@


package com.android.mtp;
package com.android.mtp;


import android.database.sqlite.SQLiteQueryBuilder;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract.Root;


import java.util.HashMap;
import java.util.Map;

/**
/**
 * Class containing MtpDatabase constants.
 * Class containing MtpDatabase constants.
 */
 */
@@ -41,9 +45,13 @@ class MtpDatabaseConstants {
    static final String TABLE_ROOT_EXTRA = "RootExtra";
    static final String TABLE_ROOT_EXTRA = "RootExtra";


    /**
    /**
     * View to join Documents and RootExtra tables to provide roots information.
     * 'FROM' closure of joining TABLE_DOCUMENTS and TABLE_ROOT_EXTRA.
     */
     */
    static final String VIEW_ROOTS = "Roots";
    static final String JOIN_ROOTS = createJoinFromClosure(
            TABLE_DOCUMENTS,
            TABLE_ROOT_EXTRA,
            Document.COLUMN_DOCUMENT_ID,
            Root.COLUMN_ROOT_ID);


    static final String COLUMN_DEVICE_ID = "device_id";
    static final String COLUMN_DEVICE_ID = "device_id";
    static final String COLUMN_STORAGE_ID = "storage_id";
    static final String COLUMN_STORAGE_ID = "storage_id";
@@ -86,8 +94,6 @@ class MtpDatabaseConstants {


    /**
    /**
     * Document that represents a MTP device.
     * Document that represents a MTP device.
     * Note we have "device" document only when the device has multiple storage volumes. Otherwise
     * we regard the single "storage" document as root.
     */
     */
    static final int DOCUMENT_TYPE_DEVICE = 0;
    static final int DOCUMENT_TYPE_DEVICE = 0;


@@ -131,30 +137,31 @@ class MtpDatabaseConstants {
            Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);";
            Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);";


    /**
    /**
     * Creates a view to join Documents table and RootExtra table on their primary keys to
     * Map for columns names to provide DocumentContract.Root compatible columns.
     * provide DocumentContract.Root equivalent information.
     * @see SQLiteQueryBuilder#setProjectionMap(Map)
     */
     */
    static final String QUERY_CREATE_VIEW_ROOTS =
    static final Map<String, String> COLUMN_MAP_ROOTS;
            "CREATE VIEW " + VIEW_ROOTS + " AS SELECT " +
    static {
                    TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " +
        COLUMN_MAP_ROOTS = new HashMap<>();
                            Root.COLUMN_ROOT_ID + "," +
        COLUMN_MAP_ROOTS.put(Root.COLUMN_ROOT_ID, TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID);
                    TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS + "," +
        COLUMN_MAP_ROOTS.put(Root.COLUMN_FLAGS, TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS);
                    TABLE_DOCUMENTS + "." + Document.COLUMN_ICON + " AS " +
        COLUMN_MAP_ROOTS.put(Root.COLUMN_ICON, TABLE_DOCUMENTS + "." + Document.COLUMN_ICON);
                            Root.COLUMN_ICON + "," +
        COLUMN_MAP_ROOTS.put(
                    TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME + " AS " +
                Root.COLUMN_TITLE, TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME);
                            Root.COLUMN_TITLE + "," +
        COLUMN_MAP_ROOTS.put(Root.COLUMN_SUMMARY, TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY);
                    TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " +
        COLUMN_MAP_ROOTS.put(
                            Root.COLUMN_SUMMARY + "," +
                Root.COLUMN_DOCUMENT_ID, TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID);
                    TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " +
        COLUMN_MAP_ROOTS.put(
                    Root.COLUMN_DOCUMENT_ID + "," +
                Root.COLUMN_AVAILABLE_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES);
                    TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES + "," +
        COLUMN_MAP_ROOTS.put(
                    TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES + "," +
                Root.COLUMN_CAPACITY_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES);
                    TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES + "," +
        COLUMN_MAP_ROOTS.put(
                    TABLE_DOCUMENTS + "." + COLUMN_ROW_STATE +
                Root.COLUMN_MIME_TYPES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES);
            " FROM " + TABLE_DOCUMENTS + " INNER JOIN " + TABLE_ROOT_EXTRA +
    }
            " ON " +

                    COLUMN_PARENT_DOCUMENT_ID + " IS NULL AND " +
    private static String createJoinFromClosure(
                    TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID +
            String table1, String table2, String column1, String column2) {
                    "=" +
        return table1 + " INNER JOIN " + table2 +
                    TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID;
                " ON " + table1 + "." + column1 + " = " + table2 + "." + column2;
    }
}
}
+0 −3
Original line number Original line Diff line number Diff line
@@ -190,9 +190,6 @@ public class MtpDocumentsProvider extends DocumentsProvider {
            getDocumentLoader(parentIdentifier).clearTask(parentIdentifier);
            getDocumentLoader(parentIdentifier).clearTask(parentIdentifier);
            notifyChildDocumentsChange(parentIdentifier.mDocumentId);
            notifyChildDocumentsChange(parentIdentifier.mDocumentId);
        } catch (IOException error) {
        } catch (IOException error) {
            for (final StackTraceElement element : error.getStackTrace()) {
                Log.e("hirono", element.toString());
            }
            throw new FileNotFoundException(error.getMessage());
            throw new FileNotFoundException(error.getMessage());
        }
        }
    }
    }
+27 −8
Original line number Original line Diff line number Diff line
@@ -7,8 +7,11 @@ import android.net.Uri;
import android.os.Process;
import android.os.Process;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract;
import android.util.Log;
import android.util.Log;
import android.util.SparseArray;


import java.io.IOException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.FutureTask;
@@ -106,26 +109,42 @@ final class RootScanner {
            int pollingCount = 0;
            int pollingCount = 0;
            while (!Thread.interrupted()) {
            while (!Thread.interrupted()) {
                final int[] deviceIds = mManager.getOpenedDeviceIds();
                final int[] deviceIds = mManager.getOpenedDeviceIds();
                if (deviceIds.length == 0) {
                final Map<String, MtpRoot[]> rootsMap = new HashMap<>();
                    return;
                }
                boolean changed = false;
                boolean changed = false;

                // Update devices.
                mDatabase.getMapper().startAddingDocuments(null /* parentDocumentId */);
                mDatabase.getMapper().startAddingDocuments(null /* parentDocumentId */);
                for (int deviceId : deviceIds) {
                for (final int deviceId : deviceIds) {
                    try {
                    try {
                        final MtpRoot[] roots = mManager.getRoots(deviceId);
                        final MtpRoot[] roots = mManager.getRoots(deviceId);
                        if (mDatabase.getMapper().putRootDocuments(deviceId, mResources, roots)) {
                        final String id = mDatabase.getMapper().putDeviceDocument(
                                deviceId,
                                mManager.getDeviceName(deviceId),
                                roots);
                        if (id != null) {
                            changed = true;
                            changed = true;
                            rootsMap.put(id, roots);
                        }
                        }
                    } catch (IOException | SQLiteException exception) {
                    } catch (IOException exception) {
                        // The error may happen on the device. We would like to continue getting
                        // The error may happen on the device. We would like to continue getting
                        // roots for other devices.
                        // roots for other devices.
                        Log.e(MtpDocumentsProvider.TAG, exception.getMessage());
                        Log.e(MtpDocumentsProvider.TAG, exception.getMessage());
                    }
                    }
                }
                }
                if (mDatabase.getMapper().stopAddingDocuments(null /* parentDocumentId */)) {
                mDatabase.getMapper().stopAddingDocuments(null /* parentDocumentId */);

                // Update roots.
                for (final String documentId : rootsMap.keySet()) {
                    mDatabase.getMapper().startAddingDocuments(documentId);
                    if (mDatabase.getMapper().putRootDocuments(
                            documentId, mResources, rootsMap.get(documentId))) {
                        changed = true;
                    }
                    if (mDatabase.getMapper().stopAddingDocuments(documentId)) {
                        changed = true;
                        changed = true;
                    }
                    }
                }

                if (changed) {
                if (changed) {
                    notifyChange();
                    notifyChange();
                }
                }
Loading