@VisibleForTesting
class MtpDatabase {
private final MtpDatabaseInternal mDatabase;
+
+ /**
+ * Mapping mode for roots/documents where we start adding child documents.
+ * Methods operate the state needs to be synchronized.
+ */
private final Map<String, Integer> mMappingMode = new HashMap<>();
@VisibleForTesting
mDatabase = new MtpDatabaseInternal(context);
}
+ /**
+ * Closes the database.
+ */
@VisibleForTesting
+ void close() {
+ mDatabase.close();
+ }
+
+ /**
+ * {@link MtpDatabaseInternal#queryRoots}
+ */
Cursor queryRoots(String[] columnNames) {
return mDatabase.queryRoots(columnNames);
}
+ /**
+ * {@link MtpDatabaseInternal#queryRootDocuments}
+ */
@VisibleForTesting
Cursor queryRootDocuments(String[] columnNames) {
return mDatabase.queryRootDocuments(columnNames);
}
+ /**
+ * {@link MtpDatabaseInternal#queryChildDocuments}
+ */
@VisibleForTesting
Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) {
return mDatabase.queryChildDocuments(columnNames, parentDocumentId);
}
- @VisibleForTesting
- void startAddingRootDocuments(int deviceId) {
+ /**
+ * {@link MtpDatabaseInternal#removeDeviceRows}
+ */
+ void removeDeviceRows(int deviceId) {
+ mDatabase.removeDeviceRows(deviceId);
+ }
+
+ /**
+ * Invokes {@link MtpDatabaseInternal#startAddingDocuments} for root documents.
+ * @param deviceId Device ID.
+ */
+ synchronized void startAddingRootDocuments(int deviceId) {
final String mappingStateKey = getRootDocumentsMappingStateKey(deviceId);
if (mMappingMode.containsKey(mappingStateKey)) {
throw new Error("Mapping for the root has already started.");
SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId)));
}
+ /**
+ * Invokes {@link MtpDatabaseInternal#startAddingDocuments} for child of specific documents.
+ * @param parentDocumentId Document ID for parent document.
+ */
@VisibleForTesting
- void startAddingChildDocuments(String parentDocumentId) {
+ synchronized void startAddingChildDocuments(String parentDocumentId) {
final String mappingStateKey = getChildDocumentsMappingStateKey(parentDocumentId);
if (mMappingMode.containsKey(mappingStateKey)) {
throw new Error("Mapping for the root has already started.");
mDatabase.startAddingDocuments(SELECTION_CHILD_DOCUMENTS, parentDocumentId));
}
- @VisibleForTesting
- void putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) {
+ /**
+ * Puts root information to database.
+ * @param deviceId Device ID
+ * @param resources Resources required to localize root name.
+ * @param roots List of root information.
+ * @return If roots are added or removed from the database.
+ */
+ synchronized boolean putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) {
mDatabase.beginTransaction();
try {
- final ContentValues[] valuesList = new ContentValues[roots.length];
- for (int i = 0; i < roots.length; i++) {
- if (roots[i].mDeviceId != deviceId) {
- throw new IllegalArgumentException();
- }
- valuesList[i] = new ContentValues();
- getRootDocumentValues(valuesList[i], resources, roots[i]);
- }
- boolean heuristic;
- String mapColumn;
+ final boolean heuristic;
+ final String mapColumn;
switch (mMappingMode.get(getRootDocumentsMappingStateKey(deviceId))) {
case MAP_BY_MTP_IDENTIFIER:
heuristic = false;
default:
throw new Error("Unexpected map mode.");
}
- final long[] documentIds = mDatabase.putDocuments(
+ final ContentValues[] valuesList = new ContentValues[roots.length];
+ for (int i = 0; i < roots.length; i++) {
+ if (roots[i].mDeviceId != deviceId) {
+ throw new IllegalArgumentException();
+ }
+ valuesList[i] = new ContentValues();
+ getRootDocumentValues(valuesList[i], resources, roots[i]);
+ }
+ final boolean changed = mDatabase.putDocuments(
valuesList,
SELECTION_ROOT_DOCUMENTS,
Integer.toString(deviceId),
int i = 0;
for (final MtpRoot root : roots) {
// Use the same value for the root ID and the corresponding document ID.
- values.put(Root.COLUMN_ROOT_ID, documentIds[i++]);
values.put(
- Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
+ Root.COLUMN_ROOT_ID,
+ valuesList[i++].getAsString(Document.COLUMN_DOCUMENT_ID));
+ values.put(
+ Root.COLUMN_FLAGS,
+ Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
values.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace);
values.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity);
values.put(Root.COLUMN_MIME_TYPES, "");
mDatabase.putRootExtra(values);
}
mDatabase.setTransactionSuccessful();
+ return changed;
} finally {
mDatabase.endTransaction();
}
}
+ /**
+ * Puts document information to database.
+ * @param deviceId Device ID
+ * @param parentId Parent document ID.
+ * @param documents List of document information.
+ */
@VisibleForTesting
- void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
- final ContentValues[] valuesList = new ContentValues[documents.length];
- for (int i = 0; i < documents.length; i++) {
- valuesList[i] = new ContentValues();
- getChildDocumentValues(valuesList[i], deviceId, parentId, documents[i]);
- }
- boolean heuristic;
- String mapColumn;
+ synchronized void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
+ final boolean heuristic;
+ final String mapColumn;
switch (mMappingMode.get(getChildDocumentsMappingStateKey(parentId))) {
case MAP_BY_MTP_IDENTIFIER:
heuristic = false;
- mapColumn = COLUMN_STORAGE_ID;
+ mapColumn = COLUMN_OBJECT_HANDLE;
break;
case MAP_BY_NAME:
heuristic = true;
default:
throw new Error("Unexpected map mode.");
}
+ final ContentValues[] valuesList = new ContentValues[documents.length];
+ for (int i = 0; i < documents.length; i++) {
+ valuesList[i] = new ContentValues();
+ getChildDocumentValues(valuesList[i], deviceId, parentId, documents[i]);
+ }
mDatabase.putDocuments(
valuesList, SELECTION_CHILD_DOCUMENTS, parentId, heuristic, mapColumn);
}
+ /**
+ * Clears mapping between MTP identifier and document/root ID.
+ */
@VisibleForTesting
- void clearMapping() {
+ synchronized void clearMapping() {
mDatabase.clearMapping();
mMappingMode.clear();
}
- @VisibleForTesting
- void stopAddingRootDocuments(int deviceId) {
+ /**
+ * Stops adding root documents.
+ * @param deviceId Device ID.
+ * @return True if new rows are added/removed.
+ */
+ synchronized boolean stopAddingRootDocuments(int deviceId) {
final String mappingModeKey = getRootDocumentsMappingStateKey(deviceId);
switch (mMappingMode.get(mappingModeKey)) {
case MAP_BY_MTP_IDENTIFIER:
- mDatabase.stopAddingDocuments(
+ mMappingMode.remove(mappingModeKey);
+ return mDatabase.stopAddingDocuments(
SELECTION_ROOT_DOCUMENTS,
Integer.toString(deviceId),
COLUMN_STORAGE_ID);
- break;
case MAP_BY_NAME:
- mDatabase.stopAddingDocuments(
+ mMappingMode.remove(mappingModeKey);
+ return mDatabase.stopAddingDocuments(
SELECTION_ROOT_DOCUMENTS,
Integer.toString(deviceId),
Document.COLUMN_DISPLAY_NAME);
- break;
default:
throw new Error("Unexpected mapping state.");
}
- mMappingMode.remove(mappingModeKey);
}
+ /**
+ * Stops adding documents under the parent.
+ * @param parentId Document ID of the parent.
+ */
@VisibleForTesting
- void stopAddingChildDocuments(String parentId) {
+ synchronized void stopAddingChildDocuments(String parentId) {
final String mappingModeKey = getChildDocumentsMappingStateKey(parentId);
switch (mMappingMode.get(mappingModeKey)) {
case MAP_BY_MTP_IDENTIFIER:
values.put(Document.COLUMN_SIZE, info.getCompressedSize());
}
- private String getRootDocumentsMappingStateKey(int deviceId) {
+ /**
+ * @param deviceId Device ID.
+ * @return Key for {@link #mMappingMode}.
+ */
+ private static String getRootDocumentsMappingStateKey(int deviceId) {
return "RootDocuments/" + deviceId;
}
- private String getChildDocumentsMappingStateKey(String parentDocumentId) {
+ /**
+ * @param parentDocumentId Document ID for the parent document.
+ * @return Key for {@link #mMappingMode}.
+ */
+ private static String getChildDocumentsMappingStateKey(String parentDocumentId) {
return "ChildDocuments/" + parentDocumentId;
}
}
Root.COLUMN_TITLE + "," +
TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " +
Root.COLUMN_SUMMARY + "," +
+ // Temporary replace COLUMN_DOCUMENT_ID with old format.
TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " +
+ Root.COLUMN_DOCUMENT_ID + "_," +
+ TABLE_DOCUMENTS + "." + COLUMN_DEVICE_ID + "|| '_' ||" +
+ TABLE_DOCUMENTS + "." + COLUMN_STORAGE_ID + "||'_0' AS " +
Root.COLUMN_DOCUMENT_ID + "," +
TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES + "," +
TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES + "," +
mDatabase = helper.getWritableDatabase();
}
+ void close() {
+ mDatabase.close();
+ }
+
+ /**
+ * Queries roots information.
+ * @param columnNames Column names defined in {@link android.provider.DocumentsContract.Root}.
+ * @return Database cursor.
+ */
Cursor queryRoots(String[] columnNames) {
return mDatabase.query(
VIEW_ROOTS,
null);
}
+ /**
+ * Queries root documents information.
+ * @param columnNames Column names defined in
+ * {@link android.provider.DocumentsContract.Document}.
+ * @return Database cursor.
+ */
Cursor queryRootDocuments(String[] columnNames) {
return mDatabase.query(
TABLE_DOCUMENTS,
null);
}
+ /**
+ * Queries documents information.
+ * @param columnNames Column names defined in
+ * {@link android.provider.DocumentsContract.Document}.
+ * @return Database cursor.
+ */
Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) {
return mDatabase.query(
TABLE_DOCUMENTS,
}
/**
+ * Remove all rows belong to a device.
+ * @param deviceId Device ID.
+ */
+ void removeDeviceRows(int deviceId) {
+ deleteDocumentsAndRoots(COLUMN_DEVICE_ID + "=?", strings(deviceId));
+ }
+
+ /**
* Starts adding new documents.
* The methods decides mapping mode depends on if all documents under the given parent have MTP
* identifier or not. If all the documents have MTP identifier, it uses the identifier to find
* 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'
- * rows.
+ * rows. If the methods adds rows to database, it updates valueList with correct document ID.
*
* @param valuesList Values that are stored in the database.
* @param selection SQL where closure to select rows that shares the same parent.
* @param arg Argument for selection SQL.
* @param heuristic Whether the mapping mode is heuristic.
- * @return List of Document ID inserted to the table.
+ * @return Whether the method adds new rows.
*/
- long[] putDocuments(
+ boolean putDocuments(
ContentValues[] valuesList,
String selection,
String arg,
boolean heuristic,
String mappingKey) {
+ boolean added = false;
mDatabase.beginTransaction();
try {
- final long[] documentIds = new long[valuesList.length];
- int i = 0;
for (final ContentValues values : valuesList) {
final Cursor candidateCursor = mDatabase.query(
TABLE_DOCUMENTS,
final long rowId;
if (candidateCursor.getCount() == 0) {
rowId = mDatabase.insert(TABLE_DOCUMENTS, null, values);
+ added = true;
} else if (!heuristic) {
candidateCursor.moveToNext();
final String documentId = candidateCursor.getString(0);
}
// Document ID is a primary integer key of the table. So the returned row
// IDs should be same with the document ID.
- documentIds[i++] = rowId;
+ values.put(Document.COLUMN_DOCUMENT_ID, rowId);
candidateCursor.close();
}
mDatabase.setTransactionSuccessful();
- return documentIds;
+ return added;
} finally {
mDatabase.endTransaction();
}
}
+ /**
+ * Puts extra information for root documents.
+ * @param values Values containing extra information.
+ */
void putRootExtra(ContentValues values) {
mDatabase.replace(TABLE_ROOT_EXTRA, null, values);
}
* @param selection Query to select rows for resolving.
* @param arg Argument for selection SQL.
* @param groupKey Column name used to find corresponding rows.
+ * @return Whether the methods adds or removed visible rows.
*/
- void stopAddingDocuments(String selection, String arg, String groupKey) {
+ boolean stopAddingDocuments(String selection, String arg, String groupKey) {
mDatabase.beginTransaction();
try {
// Get 1-to-1 mapping of invalidated document and pending document.
}
mergingCursor.close();
+ boolean changed = false;
+
// Delete all invalidated rows that cannot be mapped.
- deleteDocumentsAndRoots(
+ if (deleteDocumentsAndRoots(
COLUMN_ROW_STATE + " = ? AND " + selection,
- strings(ROW_STATE_INVALIDATED, arg));
+ strings(ROW_STATE_INVALIDATED, arg))) {
+ changed = true;
+ }
// The database cannot find old document ID for the pending rows.
// Turn the all pending rows into valid state, which means the rows become to be
// valid with new document ID.
values.clear();
values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
- mDatabase.update(
+ if (mDatabase.update(
TABLE_DOCUMENTS,
values,
COLUMN_ROW_STATE + " = ? AND " + selection,
- strings(ROW_STATE_PENDING, arg));
+ strings(ROW_STATE_PENDING, arg)) != 0) {
+ changed = true;
+ }
mDatabase.setTransactionSuccessful();
+ return changed;
} finally {
mDatabase.endTransaction();
}
}
}
+ /**
+ * {@link android.database.sqlite.SQLiteDatabase#beginTransaction()}
+ */
void beginTransaction() {
mDatabase.beginTransaction();
}
+ /**
+ * {@link android.database.sqlite.SQLiteDatabase#setTransactionSuccessful()}
+ */
void setTransactionSuccessful() {
mDatabase.setTransactionSuccessful();
}
+ /**
+ * {@link android.database.sqlite.SQLiteDatabase#endTransaction()}
+ */
void endTransaction() {
mDatabase.endTransaction();
}
* Deletes a document, and its root information if the document is a root document.
* @param selection Query to select documents.
* @param args Arguments for selection.
+ * @return Whether the method deletes rows.
*/
- private void deleteDocumentsAndRoots(String selection, String[] args) {
+ private boolean deleteDocumentsAndRoots(String selection, String[] args) {
mDatabase.beginTransaction();
try {
- mDatabase.delete(
+ int deleted = 0;
+ deleted += mDatabase.delete(
TABLE_ROOT_EXTRA,
Root.COLUMN_ROOT_ID + " IN (" + SQLiteQueryBuilder.buildQueryString(
false,
null,
null) + ")",
args);
- mDatabase.delete(TABLE_DOCUMENTS, selection, args);
+ deleted += mDatabase.delete(TABLE_DOCUMENTS, selection, args);
mDatabase.setTransactionSuccessful();
+ return deleted != 0;
} finally {
mDatabase.endTransaction();
}
private Map<Integer, DeviceToolkit> mDeviceToolkits;
private RootScanner mRootScanner;
private Resources mResources;
+ private MtpDatabase mDatabase;
/**
* Provides singleton instance to MtpDocumentsService.
mMtpManager = new MtpManager(getContext());
mResolver = getContext().getContentResolver();
mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
- mRootScanner = new RootScanner(mResolver, mMtpManager);
+ mDatabase = new MtpDatabase(getContext());
+ mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
return true;
}
@VisibleForTesting
- void onCreateForTesting(Resources resources, MtpManager mtpManager, ContentResolver resolver) {
+ void onCreateForTesting(
+ Resources resources,
+ MtpManager mtpManager,
+ ContentResolver resolver,
+ MtpDatabase database) {
mResources = resources;
mMtpManager = mtpManager;
mResolver = resolver;
mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
- mRootScanner = new RootScanner(mResolver, mMtpManager);
+ mDatabase = database;
+ mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
}
@Override
if (projection == null) {
projection = MtpDocumentsProvider.DEFAULT_ROOT_PROJECTION;
}
- final MatrixCursor cursor = new MatrixCursor(projection);
- final MtpRoot[] roots = mRootScanner.getRoots();
- for (final MtpRoot root : roots) {
- final Identifier rootIdentifier = new Identifier(root.mDeviceId, root.mStorageId);
- final MatrixCursor.RowBuilder builder = cursor.newRow();
- builder.add(Root.COLUMN_ROOT_ID, rootIdentifier.toRootId());
- builder.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
- builder.add(Root.COLUMN_TITLE, root.getRootName(mResources));
- builder.add(Root.COLUMN_DOCUMENT_ID, rootIdentifier.toDocumentId());
- builder.add(Root.COLUMN_AVAILABLE_BYTES , root.mFreeSpace);
- }
+ final Cursor cursor = mDatabase.queryRoots(projection);
cursor.setNotificationUri(
mResolver, DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY));
return cursor;
// TODO: Flush the device before closing (if not closed externally).
getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
mDeviceToolkits.remove(deviceId);
+ mDatabase.removeDeviceRows(deviceId);
mMtpManager.closeDevice(deviceId);
- mRootScanner.scanNow();
+ mRootScanner.notifyChange();
}
- void closeAllDevices() {
+ void close() throws InterruptedException {
boolean closed = false;
for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
try {
+ mDatabase.removeDeviceRows(deviceId);
mMtpManager.closeDevice(deviceId);
getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
closed = true;
}
}
if (closed) {
- mRootScanner.scanNow();
+ mRootScanner.notifyChange();
}
+ mRootScanner.close();
+ mDatabase.close();
}
boolean hasOpenedDevices() {
@Override
public void onDestroy() {
final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
- provider.closeAllDevices();
unregisterReceiver(mReceiver);
mReceiver = null;
+ try {
+ provider.close();
+ } catch (InterruptedException e) {
+ Log.e(MtpDocumentsProvider.TAG, e.getMessage());
+ }
super.onDestroy();
}
package com.android.mtp;
import android.content.ContentResolver;
+import android.content.res.Resources;
import android.net.Uri;
import android.os.Process;
import android.provider.DocumentsContract;
import android.util.Log;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
final class RootScanner {
/**
private final static long SHORT_POLLING_TIMES = 10;
final ContentResolver mResolver;
+ final Resources mResources;
final MtpManager mManager;
- MtpRoot[] mLastRoots = new MtpRoot[0];
+ final MtpDatabase mDatabase;
+ boolean mClosed = false;
int mPollingCount;
- boolean mHasBackgroundTask = false;
+ Thread mBackgroundThread;
- RootScanner(ContentResolver resolver, MtpManager manager) {
+ RootScanner(
+ ContentResolver resolver,
+ Resources resources,
+ MtpManager manager,
+ MtpDatabase database) {
mResolver = resolver;
+ mResources = resources;
mManager = manager;
+ mDatabase = database;
}
- synchronized MtpRoot[] getRoots() {
- return mLastRoots;
+ void notifyChange() {
+ final Uri uri =
+ DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY);
+ mResolver.notifyChange(uri, null, false);
}
/**
* If the background thread has already gone, it restarts another background thread.
*/
synchronized void scanNow() {
+ if (mClosed) {
+ return;
+ }
mPollingCount = 0;
- if (!mHasBackgroundTask) {
- mHasBackgroundTask = true;
- new BackgroundLoaderThread().start();
+ if (mBackgroundThread == null) {
+ mBackgroundThread = new BackgroundLoaderThread();
+ mBackgroundThread.start();
} else {
notify();
}
}
- private MtpRoot[] createRoots(int[] deviceIds) {
- final ArrayList<MtpRoot> roots = new ArrayList<>();
- for (final int deviceId : deviceIds) {
- try {
- roots.addAll(Arrays.asList(mManager.getRoots(deviceId)));
- } catch (IOException error) {
- // Skip device that return error.
- Log.d(MtpDocumentsProvider.TAG, error.getMessage());
+ void close() throws InterruptedException {
+ Thread thread;
+ synchronized (this) {
+ mClosed = true;
+ thread = mBackgroundThread;
+ if (mBackgroundThread == null) {
+ return;
}
+ notify();
}
- return roots.toArray(new MtpRoot[roots.size()]);
+ thread.join();
}
private final class BackgroundLoaderThread extends Thread {
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
synchronized (RootScanner.this) {
- while (true) {
+ while (!mClosed) {
final int[] deviceIds = mManager.getOpenedDeviceIds();
- final MtpRoot[] newRoots = createRoots(deviceIds);
- if (!Arrays.equals(mLastRoots, newRoots)) {
- final Uri uri =
- DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY);
- mResolver.notifyChange(uri, null, false);
- mLastRoots = newRoots;
- }
if (deviceIds.length == 0) {
break;
}
+ boolean changed = false;
+ for (int deviceId : deviceIds) {
+ try {
+ mDatabase.startAddingRootDocuments(deviceId);
+ changed = mDatabase.putRootDocuments(
+ deviceId, mResources, mManager.getRoots(deviceId)) || changed;
+ changed = mDatabase.stopAddingRootDocuments(deviceId) || changed;
+ } catch (IOException exp) {
+ // The error may happen on the device. We would like to continue getting
+ // roots for other devices.
+ Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
+ continue;
+ }
+ }
+ if (changed) {
+ notifyChange();
+ }
mPollingCount++;
try {
// Use SHORT_POLLING_PERIOD for the first SHORT_POLLING_TIMES because it is
}
}
- mHasBackgroundTask = false;
+ mBackgroundThread = null;
}
}
}
Root.COLUMN_ICON,
Root.COLUMN_TITLE,
Root.COLUMN_SUMMARY,
- Root.COLUMN_DOCUMENT_ID,
+ Root.COLUMN_DOCUMENT_ID + "_",
Root.COLUMN_AVAILABLE_BYTES,
Root.COLUMN_CAPACITY_BYTES
});
private MtpDocumentsProvider mProvider;
private TestMtpManager mMtpManager;
private final TestResources mResources = new TestResources();
+ private MtpDatabase mDatabase;
@Override
public void setUp() throws IOException {
mResolver = new TestContentResolver();
mMtpManager = new TestMtpManager(getContext());
mProvider = new MtpDocumentsProvider();
- mProvider.onCreateForTesting(mResources, mMtpManager, mResolver);
+ mDatabase = new MtpDatabase(getContext());
+ mProvider.onCreateForTesting(mResources, mMtpManager, mResolver, mDatabase);
+ }
+
+ @Override
+ public void tearDown() {
+ try {
+ mProvider.close();
+ } catch (InterruptedException e) {
+ fail();
+ }
}
public void testOpenAndCloseDevice() throws Exception {
mResolver.waitForNotification(ROOTS_URI, 1);
}
- public void testCloseAllDevices() throws Exception {
- 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 */)
- });
- mProvider.closeAllDevices();
- mProvider.openDevice(0);
- mResolver.waitForNotification(ROOTS_URI, 1);
-
- mProvider.closeAllDevices();
- mResolver.waitForNotification(ROOTS_URI, 2);
- }
-
public void testQueryRoots() throws Exception {
mMtpManager.addValidDevice(0);
mMtpManager.addValidDevice(1);
final Cursor cursor = mProvider.queryRoots(null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("0_1", cursor.getString(0));
+ assertEquals("1", cursor.getString(0));
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
cursor.moveToNext();
- assertEquals("1_1", cursor.getString(0));
+ assertEquals("2", cursor.getString(0));
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);
assertEquals("1_1_0", cursor.getString(4));
assertEquals(2048, cursor.getInt(5));
}
-
- {
- mProvider.closeAllDevices();
- mResolver.waitForNotification(ROOTS_URI, 3);
- final Cursor cursor = mProvider.queryRoots(null);
- assertEquals(0, cursor.getCount());
- }
}
public void testQueryRoots_error() throws Exception {
final Cursor cursor = mProvider.queryRoots(null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("1_1", cursor.getString(0));
+ assertEquals("1", cursor.getString(0));
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);