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

Commit 20754c5a authored by Daichi Hirono's avatar Daichi Hirono
Browse files

Store device document even if the device is not opened.

BUG=26197156

Change-Id: I2a80bb3e85310cf63253173b1110a155ee1391ba
parent d5f94468
Loading
Loading
Loading
Loading
+17 −10
Original line number Diff line number Diff line
@@ -56,22 +56,26 @@ class Mapper {
        mDatabase = database;
    }

    synchronized String putDeviceDocument(int deviceId, String name, MtpRoot[] roots) {
    /**
     * Puts device information to database.
     * @return If device is added to the database.
     */
    synchronized boolean putDeviceDocument(MtpDeviceRecord device) {
        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(
            MtpDatabase.getDeviceDocumentValues(valuesList[0], device);
            final boolean changed = 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);
            return changed;
        } finally {
            database.endTransaction();
        }
@@ -249,7 +253,7 @@ class Mapper {
     * If the mapping mode is not heuristic, it just adds the rows to the database or updates the
     * existing rows with the new values. If the mapping mode is heuristic, it adds some new rows as
     * 'pending' state when that rows may be corresponding to existing 'invalidated' rows. Then
     * {@link #stopAddingDocuments(String, String[], String)} turns the pending rows into 'valid'
     * {@link #stopAddingDocuments(String)} turns the pending rows into 'valid'
     * rows. If the methods adds rows to database, it updates valueList with correct document ID.
     *
     * @param valuesList Values for documents to be stored in the database.
@@ -452,13 +456,16 @@ class Mapper {
        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.
+26 −8
Original line number Diff line number Diff line
@@ -18,11 +18,11 @@ package com.android.mtp;

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

import android.annotation.Nullable;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
@@ -36,8 +36,6 @@ import android.provider.DocumentsContract.Root;
import com.android.internal.annotations.VisibleForTesting;

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

/**
@@ -183,6 +181,27 @@ class MtpDatabase {
        deleteDocumentsAndRoots(COLUMN_DEVICE_ID + "=?", strings(deviceId));
    }

    @Nullable String getDocumentIdForDevice(int deviceId) {
        final Cursor cursor = mDatabase.query(
                TABLE_DOCUMENTS,
                strings(Document.COLUMN_DOCUMENT_ID),
                COLUMN_DOCUMENT_TYPE + " = ? AND " + COLUMN_DEVICE_ID + " = ?",
                strings(DOCUMENT_TYPE_DEVICE, deviceId),
                null,
                null,
                null,
                "1");
        try {
            if (cursor.moveToNext()) {
                return cursor.getString(0);
            } else {
                return null;
            }
        } finally {
            cursor.close();
        }
    }

    /**
     * Obtains parent document ID.
     * @param documentId
@@ -361,23 +380,22 @@ class MtpDatabase {
        context.deleteDatabase(DATABASE_NAME);
    }

    static void getDeviceDocumentValues(
            ContentValues values, int deviceId, String name, MtpRoot[] roots) {
    static void getDeviceDocumentValues(ContentValues values, MtpDeviceRecord device) {
        values.clear();
        values.put(COLUMN_DEVICE_ID, deviceId);
        values.put(COLUMN_DEVICE_ID, device.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.put(Document.COLUMN_DISPLAY_NAME, device.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) {
        for (final MtpRoot root : device.roots) {
            size += root.mMaxCapacity - root.mFreeSpace;
        }
        values.put(Document.COLUMN_SIZE, size);
+31 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.mtp;

class MtpDeviceRecord {
    public final int deviceId;
    public final String name;
    public final boolean opened;
    public final MtpRoot[] roots;

    MtpDeviceRecord(int deviceId, String name, boolean opened, MtpRoot[] roots) {
        this.deviceId = deviceId;
        this.name = name;
        this.opened = opened;
        this.roots = roots;
    }
}
+7 −2
Original line number Diff line number Diff line
@@ -247,7 +247,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
            closeDeviceInternal(deviceId);
            mDatabase.removeDeviceRows(deviceId);
        }
        mRootScanner.notifyChange();
        mRootScanner.resume();
    }

    int[] getOpenedDeviceIds() {
@@ -258,7 +258,12 @@ public class MtpDocumentsProvider extends DocumentsProvider {

    String getDeviceName(int deviceId) throws IOException {
        synchronized (mDeviceListLock) {
            return mMtpManager.getDeviceName(deviceId);
            for (final MtpDeviceRecord device : mMtpManager.getDevices()) {
                if (device.deviceId == deviceId) {
                    return device.name;
                }
            }
            throw new IOException("Not found the device: " + Integer.toString(deviceId));
        }
    }

+88 −22
Original line number Diff line number Diff line
@@ -17,8 +17,10 @@
package com.android.mtp;

import android.content.Context;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.mtp.MtpConstants;
import android.mtp.MtpDevice;
@@ -26,12 +28,14 @@ import android.mtp.MtpEvent;
import android.mtp.MtpObjectInfo;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

/**
 * The model wrapping android.mtp API.
@@ -39,6 +43,27 @@ import java.io.IOException;
class MtpManager {
    final static int OBJECT_HANDLE_ROOT_CHILDREN = -1;

    /**
     * Subclass for PTP.
     */
    private static final int SUBCLASS_STILL_IMAGE_CAPTURE = 1;

    /**
     * Subclass for Android style MTP.
     */
    private static final int SUBCLASS_MTP = 0xff;

    /**
     * Protocol for Picture Transfer Protocol (PIMA 15470).
     */
    private static final int PROTOCOL_PICTURE_TRANSFER = 1;

    /**
     * Protocol for Android style MTP.
     */
    private static final int PROTOCOL_MTP = 0;


    private final UsbManager mManager;
    // TODO: Save and restore the set of opened device.
    private final SparseArray<MtpDevice> mDevices = new SparseArray<>();
@@ -92,6 +117,33 @@ class MtpManager {
        mDevices.remove(deviceId);
    }

    synchronized MtpDeviceRecord[] getDevices() {
        final ArrayList<MtpDeviceRecord> devices = new ArrayList<>();
        for (UsbDevice device : mManager.getDeviceList().values()) {
            if (!isMtpDevice(device)) {
                continue;
            }
            final boolean opened = mDevices.get(device.getDeviceId()) != null;
            final String name = device.getProductName();
            MtpRoot[] roots;
            if (opened) {
                try {
                    roots = getRoots(device.getDeviceId());
                } catch (IOException exp) {
                    Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
                    // If we failed to fetch roots for the device, we still returns device model
                    // with an empty set of roots so that the device is shown DocumentsUI as long as
                    // the device is physically connected.
                    roots = new MtpRoot[0];
                }
            } else {
                roots = new MtpRoot[0];
            }
            devices.add(new MtpDeviceRecord(device.getDeviceId(), name, opened, roots));
        }
        return devices.toArray(new MtpDeviceRecord[devices.size()]);
    }

    synchronized int[] getOpenedDeviceIds() {
        final int[] result = new int[mDevices.size()];
        for (int i = 0; i < result.length; i++) {
@@ -100,28 +152,6 @@ class MtpManager {
        return result;
    }

    String getDeviceName(int deviceId) throws IOException {
        return getDevice(deviceId).getDeviceInfo().getModel();
    }

    MtpRoot[] getRoots(int deviceId) throws IOException {
        final MtpDevice device = getDevice(deviceId);
        synchronized (device) {
            final int[] storageIds = device.getStorageIds();
            if (storageIds == null) {
                throw new IOException("Failed to obtain storage IDs.");
            }
            final MtpRoot[] results = new MtpRoot[storageIds.length];
            for (int i = 0; i < storageIds.length; i++) {
                results[i] = new MtpRoot(
                        device.getDeviceId(),
                        device.getDeviceInfo().getModel(),
                        device.getStorageInfo(storageIds[i]));
            }
            return results;
        }
    }

    MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
            throws IOException {
        final MtpDevice device = getDevice(deviceId);
@@ -212,4 +242,40 @@ class MtpManager {
        }
        return device;
    }

    private MtpRoot[] getRoots(int deviceId) throws IOException {
        final MtpDevice device = getDevice(deviceId);
        synchronized (device) {
            final int[] storageIds = device.getStorageIds();
            if (storageIds == null) {
                throw new IOException("Failed to obtain storage IDs.");
            }
            final MtpRoot[] results = new MtpRoot[storageIds.length];
            for (int i = 0; i < storageIds.length; i++) {
                results[i] = new MtpRoot(
                        device.getDeviceId(),
                        device.getDeviceInfo().getModel(),
                        device.getStorageInfo(storageIds[i]));
            }
            return results;
        }
    }

    static boolean isMtpDevice(UsbDevice device) {
        for (int i = 0; i < device.getInterfaceCount(); i++) {
            final UsbInterface usbInterface = device.getInterface(i);
            if ((usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE &&
                    usbInterface.getInterfaceSubclass() == SUBCLASS_STILL_IMAGE_CAPTURE &&
                    usbInterface.getInterfaceProtocol() == PROTOCOL_PICTURE_TRANSFER)) {
                return true;
            }
            if (usbInterface.getInterfaceClass() == UsbConstants.USB_SUBCLASS_VENDOR_SPEC &&
                    usbInterface.getInterfaceSubclass() == SUBCLASS_MTP &&
                    usbInterface.getInterfaceProtocol() == PROTOCOL_MTP &&
                    usbInterface.getName().equals("MTP")) {
                return true;
            }
        }
        return false;
    }
}
Loading