Store device document even if the device is not opened.
BUG=26197156
Change-Id: I2a80bb3e85310cf63253173b1110a155ee1391ba
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
index 322246a..cb49535e 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
@@ -56,22 +56,26 @@
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 @@
* 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 @@
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();
}
/**
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 1ba3e31..10941eb 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -18,11 +18,11 @@
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 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 @@
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 @@
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);
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
new file mode 100644
index 0000000..71df5c1
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
@@ -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;
+ }
+}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index caa2024..57a68ba 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -247,7 +247,7 @@
closeDeviceInternal(deviceId);
mDatabase.removeDeviceRows(deviceId);
}
- mRootScanner.notifyChange();
+ mRootScanner.resume();
}
int[] getOpenedDeviceIds() {
@@ -258,7 +258,12 @@
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));
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index e7f94b7..88cab8b 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -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.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 @@
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 @@
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 @@
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 @@
}
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;
+ }
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index 197dec8..e6c2726 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -2,12 +2,10 @@
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 @@
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)) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 6d57c5b..b745175 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -871,4 +871,12 @@
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));
+ }
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index b0e9722..884b1e2 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -23,7 +23,6 @@
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 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 @@
}
// 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 @@
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 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 @@
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 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 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 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 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 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 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 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 @@
}
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)));
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index ddc18a4..bbd0a30 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -19,14 +19,12 @@
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 @@
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 @@
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);
}
@@ -76,28 +68,40 @@
}
@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 @@
@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);
}
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
index f910321..611e831 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
@@ -120,9 +120,19 @@
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;