Store device document even if the device is not opened.

BUG=26197156

Change-Id: I2a80bb3e85310cf63253173b1110a155ee1391ba
This commit is contained in:
Daichi Hirono
2015-12-15 18:52:26 +09:00
parent d5f94468ba
commit 20754c5a11
10 changed files with 319 additions and 185 deletions

View File

@@ -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();
}
/**

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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)) {

View File

@@ -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));
}
}

View File

@@ -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)));
}

View File

@@ -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);
}
}

View File

@@ -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;