Store device document even if the device is not opened.
BUG=26197156 Change-Id: I2a80bb3e85310cf63253173b1110a155ee1391ba
This commit is contained in:
@@ -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,12 +456,15 @@ class Mapper {
|
||||
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
|
||||
values.clear();
|
||||
final Cursor cursor = database.query(table, null, selection, args, null, null, null, "1");
|
||||
if (cursor.getCount() == 0) {
|
||||
return;
|
||||
try {
|
||||
if (cursor.getCount() == 0) {
|
||||
return;
|
||||
}
|
||||
cursor.moveToNext();
|
||||
DatabaseUtils.cursorRowToContentValues(cursor, values);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
cursor.moveToNext();
|
||||
DatabaseUtils.cursorRowToContentValues(cursor, values);
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,10 @@ package com.android.mtp;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.res.Resources;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.net.Uri;
|
||||
import android.os.Process;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
@@ -108,36 +106,29 @@ final class RootScanner {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
int pollingCount = 0;
|
||||
while (!Thread.interrupted()) {
|
||||
final int[] deviceIds = mManager.getOpenedDeviceIds();
|
||||
final Map<String, MtpRoot[]> rootsMap = new HashMap<>();
|
||||
boolean changed = false;
|
||||
|
||||
// Update devices.
|
||||
final MtpDeviceRecord[] devices = mManager.getDevices();
|
||||
mDatabase.getMapper().startAddingDocuments(null /* parentDocumentId */);
|
||||
for (final int deviceId : deviceIds) {
|
||||
try {
|
||||
final MtpRoot[] roots = mManager.getRoots(deviceId);
|
||||
final String id = mDatabase.getMapper().putDeviceDocument(
|
||||
deviceId,
|
||||
mManager.getDeviceName(deviceId),
|
||||
roots);
|
||||
if (id != null) {
|
||||
changed = true;
|
||||
rootsMap.put(id, roots);
|
||||
}
|
||||
} catch (IOException exception) {
|
||||
// The error may happen on the device. We would like to continue getting
|
||||
// roots for other devices.
|
||||
Log.e(MtpDocumentsProvider.TAG, exception.getMessage());
|
||||
for (final MtpDeviceRecord device : devices) {
|
||||
if (mDatabase.getMapper().putDeviceDocument(device)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
mDatabase.getMapper().stopAddingDocuments(null /* parentDocumentId */);
|
||||
if (mDatabase.getMapper().stopAddingDocuments(null /* parentDocumentId */)) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Update roots.
|
||||
for (final String documentId : rootsMap.keySet()) {
|
||||
for (final MtpDeviceRecord device : devices) {
|
||||
final String documentId = mDatabase.getDocumentIdForDevice(device.deviceId);
|
||||
if (documentId == null) {
|
||||
continue;
|
||||
}
|
||||
mDatabase.getMapper().startAddingDocuments(documentId);
|
||||
if (mDatabase.getMapper().putRootDocuments(
|
||||
documentId, mResources, rootsMap.get(documentId))) {
|
||||
documentId, mResources, device.roots)) {
|
||||
changed = true;
|
||||
}
|
||||
if (mDatabase.getMapper().stopAddingDocuments(documentId)) {
|
||||
|
||||
@@ -871,4 +871,12 @@ public class MtpDatabaseTest extends AndroidTestCase {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetDocumentIdForDevice() {
|
||||
mDatabase.getMapper().startAddingDocuments(null);
|
||||
mDatabase.getMapper().putDeviceDocument(
|
||||
new MtpDeviceRecord(100, "Device", true, new MtpRoot[0]));
|
||||
mDatabase.getMapper().stopAddingDocuments(null);
|
||||
assertEquals("1", mDatabase.getDocumentIdForDevice(100));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.net.Uri;
|
||||
import android.provider.DocumentsContract.Root;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -56,17 +55,20 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
|
||||
public void testOpenAndCloseDevice() throws Exception {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(0);
|
||||
mMtpManager.setRoots(0, new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
0 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
"Device A" /* device model name */,
|
||||
"Storage A" /* volume description */,
|
||||
1024 /* free space */,
|
||||
2048 /* total space */,
|
||||
"" /* no volume identifier */)
|
||||
});
|
||||
mMtpManager.addValidDevice(new MtpDeviceRecord(
|
||||
0,
|
||||
"Device",
|
||||
false /* unopened */,
|
||||
new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
0 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
"Device A" /* device model name */,
|
||||
"Storage A" /* volume description */,
|
||||
1024 /* free space */,
|
||||
2048 /* total space */,
|
||||
"" /* no volume identifier */)
|
||||
}));
|
||||
|
||||
mProvider.openDevice(0);
|
||||
mResolver.waitForNotification(ROOTS_URI, 1);
|
||||
@@ -92,45 +94,54 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
// Check if the following notification is the first one or not.
|
||||
mMtpManager.addValidDevice(0);
|
||||
mMtpManager.setRoots(0, new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
0 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
"Device A" /* device model name */,
|
||||
"Storage A" /* volume description */,
|
||||
1024 /* free space */,
|
||||
2048 /* total space */,
|
||||
"" /* no volume identifier */)
|
||||
});
|
||||
mMtpManager.addValidDevice(new MtpDeviceRecord(
|
||||
0,
|
||||
"Device",
|
||||
false /* unopened */,
|
||||
new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
0 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
"Device A" /* device model name */,
|
||||
"Storage A" /* volume description */,
|
||||
1024 /* free space */,
|
||||
2048 /* total space */,
|
||||
"" /* no volume identifier */)
|
||||
}));
|
||||
mProvider.openDevice(0);
|
||||
mResolver.waitForNotification(ROOTS_URI, 1);
|
||||
}
|
||||
|
||||
public void testQueryRoots() throws Exception {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(0);
|
||||
mMtpManager.addValidDevice(1);
|
||||
mMtpManager.setRoots(0, new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
0 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
"Device A" /* device model name */,
|
||||
"Storage A" /* volume description */,
|
||||
1024 /* free space */,
|
||||
2048 /* total space */,
|
||||
"" /* no volume identifier */)
|
||||
});
|
||||
mMtpManager.setRoots(1, new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
1 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
"Device B" /* device model name */,
|
||||
"Storage B" /* volume description */,
|
||||
2048 /* free space */,
|
||||
4096 /* total space */,
|
||||
"Identifier B" /* no volume identifier */)
|
||||
});
|
||||
mMtpManager.addValidDevice(new MtpDeviceRecord(
|
||||
0,
|
||||
"Device",
|
||||
false /* unopened */,
|
||||
new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
0 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
"Device A" /* device model name */,
|
||||
"Storage A" /* volume description */,
|
||||
1024 /* free space */,
|
||||
2048 /* total space */,
|
||||
"" /* no volume identifier */)
|
||||
}));
|
||||
mMtpManager.addValidDevice(new MtpDeviceRecord(
|
||||
1,
|
||||
"Device",
|
||||
false /* unopened */,
|
||||
new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
1 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
"Device B" /* device model name */,
|
||||
"Storage B" /* volume description */,
|
||||
2048 /* free space */,
|
||||
4096 /* total space */,
|
||||
"Identifier B" /* no volume identifier */)
|
||||
}));
|
||||
|
||||
{
|
||||
mProvider.openDevice(0);
|
||||
@@ -138,11 +149,11 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
final Cursor cursor = mProvider.queryRoots(null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.moveToNext();
|
||||
assertEquals("2", cursor.getString(0));
|
||||
assertEquals("3", cursor.getString(0));
|
||||
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
|
||||
assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
|
||||
assertEquals("Device A Storage A", cursor.getString(3));
|
||||
assertEquals("2", cursor.getString(4));
|
||||
assertEquals("3", cursor.getString(4));
|
||||
assertEquals(1024, cursor.getInt(5));
|
||||
}
|
||||
|
||||
@@ -164,19 +175,22 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
|
||||
public void testQueryRoots_error() throws Exception {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(0);
|
||||
mMtpManager.addValidDevice(1);
|
||||
// Not set roots for device 0 so that MtpManagerMock#getRoots throws IOException.
|
||||
mMtpManager.setRoots(1, new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
1 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
"Device B" /* device model name */,
|
||||
"Storage B" /* volume description */,
|
||||
2048 /* free space */,
|
||||
4096 /* total space */,
|
||||
"Identifier B" /* no volume identifier */)
|
||||
});
|
||||
mMtpManager.addValidDevice(
|
||||
new MtpDeviceRecord(0, "Device", false /* unopened */, new MtpRoot[0]));
|
||||
mMtpManager.addValidDevice(new MtpDeviceRecord(
|
||||
1,
|
||||
"Device",
|
||||
false /* unopened */,
|
||||
new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
1 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
"Device B" /* device model name */,
|
||||
"Storage B" /* volume description */,
|
||||
2048 /* free space */,
|
||||
4096 /* total space */,
|
||||
"Identifier B" /* no volume identifier */)
|
||||
}));
|
||||
{
|
||||
mProvider.openDevice(0);
|
||||
mProvider.openDevice(1);
|
||||
@@ -185,20 +199,17 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
final Cursor cursor = mProvider.queryRoots(null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.moveToNext();
|
||||
assertEquals("2", cursor.getString(0));
|
||||
assertEquals("3", cursor.getString(0));
|
||||
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
|
||||
assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
|
||||
assertEquals("Device B Storage B", cursor.getString(3));
|
||||
assertEquals("2", cursor.getString(4));
|
||||
assertEquals("3", cursor.getString(4));
|
||||
assertEquals(2048, cursor.getInt(5));
|
||||
}
|
||||
}
|
||||
|
||||
public void testQueryDocument() throws IOException, InterruptedException, TimeoutException {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
|
||||
setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
|
||||
setupDocuments(
|
||||
0,
|
||||
@@ -236,9 +247,6 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
public void testQueryDocument_directory()
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
|
||||
setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
|
||||
setupDocuments(
|
||||
0,
|
||||
@@ -274,9 +282,6 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
public void testQueryDocument_forRoot()
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
|
||||
setupRoots(0, new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
0 /* deviceId */,
|
||||
@@ -301,9 +306,6 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
|
||||
public void testQueryChildDocuments() throws Exception {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
|
||||
setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
|
||||
setupDocuments(
|
||||
0,
|
||||
@@ -337,8 +339,6 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
|
||||
public void testQueryChildDocuments_cursorError() throws Exception {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
try {
|
||||
mProvider.queryChildDocuments("1", null, null);
|
||||
fail();
|
||||
@@ -349,8 +349,6 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
|
||||
public void testQueryChildDocuments_documentError() throws Exception {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
|
||||
mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
|
||||
try {
|
||||
@@ -363,8 +361,6 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
|
||||
public void testDeleteDocument() throws IOException, InterruptedException, TimeoutException {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
setupRoots(0, new MtpRoot[] {
|
||||
new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
|
||||
});
|
||||
@@ -385,8 +381,6 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
public void testDeleteDocument_error()
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
setupRoots(0, new MtpRoot[] {
|
||||
new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
|
||||
});
|
||||
@@ -427,9 +421,11 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
private String[] setupRoots(int deviceId, MtpRoot[] roots)
|
||||
throws FileNotFoundException, InterruptedException, TimeoutException {
|
||||
throws InterruptedException, TimeoutException, IOException {
|
||||
final int changeCount = mResolver.getChangeCount(ROOTS_URI);
|
||||
mMtpManager.setRoots(deviceId, roots);
|
||||
mMtpManager.addValidDevice(
|
||||
new MtpDeviceRecord(deviceId, "Device", false /* unopened */, roots));
|
||||
mProvider.openDevice(deviceId);
|
||||
mResolver.waitForNotification(ROOTS_URI, changeCount + 1);
|
||||
return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID)));
|
||||
}
|
||||
|
||||
@@ -19,14 +19,12 @@ package com.android.mtp;
|
||||
import android.content.Context;
|
||||
import android.mtp.MtpObjectInfo;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class TestMtpManager extends MtpManager {
|
||||
public static final int CREATED_DOCUMENT_HANDLE = 1000;
|
||||
@@ -35,9 +33,7 @@ public class TestMtpManager extends MtpManager {
|
||||
return Arrays.toString(args);
|
||||
}
|
||||
|
||||
private final Set<Integer> mValidDevices = new HashSet<>();
|
||||
private final Set<Integer> mOpenedDevices = new TreeSet<>();
|
||||
private final Map<Integer, MtpRoot[]> mRoots = new HashMap<>();
|
||||
private final SparseArray<MtpDeviceRecord> mDevices = new SparseArray<>();
|
||||
private final Map<String, MtpObjectInfo> mObjectInfos = new HashMap<>();
|
||||
private final Map<String, int[]> mObjectHandles = new HashMap<>();
|
||||
private final Map<String, byte[]> mThumbnailBytes = new HashMap<>();
|
||||
@@ -47,18 +43,14 @@ public class TestMtpManager extends MtpManager {
|
||||
super(context);
|
||||
}
|
||||
|
||||
void addValidDevice(int deviceId) {
|
||||
mValidDevices.add(deviceId);
|
||||
void addValidDevice(MtpDeviceRecord device) {
|
||||
mDevices.put(device.deviceId, device);
|
||||
}
|
||||
|
||||
void setObjectHandles(int deviceId, int storageId, int parentHandle, int[] objectHandles) {
|
||||
mObjectHandles.put(pack(deviceId, storageId, parentHandle), objectHandles);
|
||||
}
|
||||
|
||||
void setRoots(int deviceId, MtpRoot[] roots) {
|
||||
mRoots.put(deviceId, roots);
|
||||
}
|
||||
|
||||
void setObjectInfo(int deviceId, MtpObjectInfo objectInfo) {
|
||||
mObjectInfos.put(pack(deviceId, objectInfo.getObjectHandle()), objectInfo);
|
||||
}
|
||||
@@ -75,29 +67,41 @@ public class TestMtpManager extends MtpManager {
|
||||
mThumbnailBytes.put(pack(deviceId, objectHandle), bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
MtpDeviceRecord[] getDevices() {
|
||||
final MtpDeviceRecord[] result = new MtpDeviceRecord[mDevices.size()];
|
||||
for (int i = 0; i < mDevices.size(); i++) {
|
||||
final MtpDeviceRecord device = mDevices.valueAt(i);
|
||||
if (device.opened) {
|
||||
result[i] = device;
|
||||
} else {
|
||||
result[i] = new MtpDeviceRecord(
|
||||
device.deviceId, device.name, device.opened, new MtpRoot[0]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
void openDevice(int deviceId) throws IOException {
|
||||
if (!mValidDevices.contains(deviceId) || mOpenedDevices.contains(deviceId)) {
|
||||
final MtpDeviceRecord device = mDevices.get(deviceId);
|
||||
if (device == null || device.opened) {
|
||||
throw new IOException();
|
||||
}
|
||||
mOpenedDevices.add(deviceId);
|
||||
mDevices.put(
|
||||
deviceId,
|
||||
new MtpDeviceRecord(device.deviceId, device.name, true, device.roots));
|
||||
}
|
||||
|
||||
@Override
|
||||
void closeDevice(int deviceId) throws IOException {
|
||||
if (!mValidDevices.contains(deviceId) || !mOpenedDevices.contains(deviceId)) {
|
||||
final MtpDeviceRecord device = mDevices.get(deviceId);
|
||||
if (device == null || !device.opened) {
|
||||
throw new IOException();
|
||||
}
|
||||
mOpenedDevices.remove(deviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
MtpRoot[] getRoots(int deviceId) throws IOException {
|
||||
if (mRoots.containsKey(deviceId)) {
|
||||
return mRoots.get(deviceId);
|
||||
} else {
|
||||
throw new IOException("getRoots error: " + Integer.toString(deviceId));
|
||||
}
|
||||
mDevices.put(
|
||||
deviceId,
|
||||
new MtpDeviceRecord(device.deviceId, device.name, false, device.roots));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -189,16 +193,14 @@ public class TestMtpManager extends MtpManager {
|
||||
|
||||
@Override
|
||||
int[] getOpenedDeviceIds() {
|
||||
int i = 0;
|
||||
final int[] result = new int[mOpenedDevices.size()];
|
||||
for (int deviceId : mOpenedDevices) {
|
||||
result[i++] = deviceId;
|
||||
final int[] result = new int[mDevices.size()];
|
||||
int count = 0;
|
||||
for (int i = 0; i < mDevices.size(); i++) {
|
||||
final MtpDeviceRecord device = mDevices.valueAt(i);
|
||||
if (device.opened) {
|
||||
result[count++] = device.deviceId;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getDeviceName(int deviceId) throws IOException {
|
||||
return "Device";
|
||||
return Arrays.copyOf(result, count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,9 +120,19 @@ final class TestUtil {
|
||||
private static void waitForStorages(
|
||||
TestResultInstrumentation instrumentation,
|
||||
MtpManager manager,
|
||||
int deviceId) throws IOException, InterruptedException {
|
||||
int deviceId) throws InterruptedException, IOException {
|
||||
while (true) {
|
||||
if (manager.getRoots(deviceId).length == 0) {
|
||||
MtpDeviceRecord device = null;
|
||||
for (final MtpDeviceRecord deviceCandidate : manager.getDevices()) {
|
||||
if (deviceCandidate.deviceId == deviceId) {
|
||||
device = deviceCandidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (device == null) {
|
||||
throw new IOException("Device was detached.");
|
||||
}
|
||||
if (device.roots.length == 0) {
|
||||
instrumentation.show("Wait for storages.");
|
||||
Thread.sleep(1000);
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user