Show MTP device as root when it has multiple/zero storages.
The CL updates MtpDocumentsProvider#queryRoots so that it fetches
devices from Database as well as storages when we don't have storages
under the device, or when we have multiple storages under the device.
BUG=26120019
Change-Id: Id2b140f00a1d49fa4da7e17d2564dbbaa1795e1e
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
index cb49535e..ac47067 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
@@ -66,10 +66,13 @@
database.beginTransaction();
try {
final ContentValues[] valuesList = new ContentValues[1];
+ final ContentValues[] extraValuesList = new ContentValues[1];
valuesList[0] = new ContentValues();
- MtpDatabase.getDeviceDocumentValues(valuesList[0], device);
+ extraValuesList[0] = new ContentValues();
+ MtpDatabase.getDeviceDocumentValues(valuesList[0], extraValuesList[0], device);
final boolean changed = putDocuments(
valuesList,
+ extraValuesList,
COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
EMPTY_ARGS,
/* heuristic */ false,
@@ -88,7 +91,7 @@
* @param roots List of root information.
* @return If roots are added or removed from the database.
*/
- synchronized boolean putRootDocuments(
+ synchronized boolean putStorageDocuments(
String parentDocumentId, Resources resources, MtpRoot[] roots) {
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
database.beginTransaction();
@@ -109,36 +112,21 @@
throw new Error("Unexpected map mode.");
}
final ContentValues[] valuesList = new ContentValues[roots.length];
+ final ContentValues[] extraValuesList = new ContentValues[roots.length];
for (int i = 0; i < roots.length; i++) {
valuesList[i] = new ContentValues();
+ extraValuesList[i] = new ContentValues();
MtpDatabase.getStorageDocumentValues(
- valuesList[i], resources, parentDocumentId, roots[i]);
+ valuesList[i], extraValuesList[i], resources, parentDocumentId, roots[i]);
}
final boolean changed = putDocuments(
valuesList,
+ extraValuesList,
COLUMN_PARENT_DOCUMENT_ID + "=?",
strings(parentDocumentId),
heuristic,
mapColumn);
- final ContentValues values = new ContentValues();
- int i = 0;
- for (final MtpRoot root : roots) {
- // Use the same value for the root ID and the corresponding document ID.
- final String documentId = valuesList[i++].getAsString(Document.COLUMN_DOCUMENT_ID);
- // If it fails to insert/update documents, the document ID will be set with -1.
- // In this case we don't insert/update root extra information neither.
- if (documentId == null) {
- continue;
- }
- values.put(Root.COLUMN_ROOT_ID, documentId);
- 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, "");
- database.replace(TABLE_ROOT_EXTRA, null, values);
- }
+
database.setTransactionSuccessful();
return changed;
} finally {
@@ -176,6 +164,7 @@
}
putDocuments(
valuesList,
+ null,
COLUMN_PARENT_DOCUMENT_ID + "=?",
strings(parentId),
heuristic,
@@ -257,6 +246,7 @@
* 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.
+ * @param rootExtraValuesList Values for root extra to be stored in the database.
* @param selection SQL where closure to select rows that shares the same parent.
* @param args Argument for selection SQL.
* @param heuristic Whether the mapping mode is heuristic.
@@ -264,6 +254,7 @@
*/
private boolean putDocuments(
ContentValues[] valuesList,
+ @Nullable ContentValues[] rootExtraValuesList,
String selection,
String[] args,
boolean heuristic,
@@ -272,7 +263,14 @@
boolean added = false;
database.beginTransaction();
try {
- for (final ContentValues values : valuesList) {
+ for (int i = 0; i < valuesList.length; i++) {
+ final ContentValues values = valuesList[i];
+ final ContentValues rootExtraValues;
+ if (rootExtraValuesList != null) {
+ rootExtraValues = rootExtraValuesList[i];
+ } else {
+ rootExtraValues = null;
+ }
final Cursor candidateCursor = database.query(
TABLE_DOCUMENTS,
strings(Document.COLUMN_DOCUMENT_ID),
@@ -290,25 +288,26 @@
final long rowId;
if (candidateCursor.getCount() == 0) {
rowId = database.insert(TABLE_DOCUMENTS, null, values);
- if (rowId == -1) {
- throw new SQLiteException("Failed to put a document into database.");
- }
added = true;
} else if (!heuristic) {
candidateCursor.moveToNext();
- final String documentId = candidateCursor.getString(0);
- rowId = database.update(
+ rowId = candidateCursor.getLong(0);
+ database.update(
TABLE_DOCUMENTS,
values,
SELECTION_DOCUMENT_ID,
- strings(documentId));
+ strings(rowId));
} else {
values.put(COLUMN_ROW_STATE, ROW_STATE_PENDING);
- rowId = database.insert(TABLE_DOCUMENTS, null, values);
+ rowId = database.insertOrThrow(TABLE_DOCUMENTS, null, values);
}
// Document ID is a primary integer key of the table. So the returned row
// IDs should be same with the document ID.
values.put(Document.COLUMN_DOCUMENT_ID, rowId);
+ if (rootExtraValues != null) {
+ rootExtraValues.put(Root.COLUMN_ROOT_ID, rowId);
+ database.replace(TABLE_ROOT_EXTRA, null, rootExtraValues);
+ }
} finally {
candidateCursor.close();
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 10941eb..112914e 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -23,6 +23,9 @@
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
@@ -106,17 +109,93 @@
* @return Database cursor.
*/
Cursor queryRoots(String[] columnNames) {
- final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
- builder.setTables(JOIN_ROOTS);
- builder.setProjectionMap(COLUMN_MAP_ROOTS);
- return builder.query(
- mDatabase,
- columnNames,
- COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + " = ?",
- strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_STORAGE),
+ final String selection =
+ COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + " = ?";
+ final Cursor deviceCursor = mDatabase.query(
+ TABLE_DOCUMENTS,
+ strings(COLUMN_DEVICE_ID),
+ selection,
+ strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_DEVICE),
+ COLUMN_DEVICE_ID,
null,
null,
null);
+
+ try {
+ final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
+ builder.setTables(JOIN_ROOTS);
+ builder.setProjectionMap(COLUMN_MAP_ROOTS);
+ final MatrixCursor result = new MatrixCursor(columnNames);
+ final ContentValues values = new ContentValues();
+
+ while (deviceCursor.moveToNext()) {
+ final int deviceId = deviceCursor.getInt(0);
+ final Cursor storageCursor = builder.query(
+ mDatabase,
+ columnNames,
+ selection + " AND " + COLUMN_DEVICE_ID + " = ?",
+ strings(ROW_STATE_VALID,
+ ROW_STATE_INVALIDATED,
+ DOCUMENT_TYPE_STORAGE,
+ deviceId),
+ null,
+ null,
+ null);
+ try {
+ values.clear();
+ if (storageCursor.getCount() == 1) {
+ storageCursor.moveToNext();
+ DatabaseUtils.cursorRowToContentValues(storageCursor, values);
+ } else {
+ final Cursor cursor = builder.query(
+ mDatabase,
+ columnNames,
+ selection + " AND " + COLUMN_DEVICE_ID + " = ?",
+ strings(ROW_STATE_VALID,
+ ROW_STATE_INVALIDATED,
+ DOCUMENT_TYPE_DEVICE,
+ deviceId),
+ null,
+ null,
+ null);
+ try {
+ cursor.moveToNext();
+ DatabaseUtils.cursorRowToContentValues(cursor, values);
+ } finally {
+ cursor.close();
+ }
+
+ long capacityBytes = 0;
+ long availableBytes = 0;
+ int capacityIndex = cursor.getColumnIndex(Root.COLUMN_CAPACITY_BYTES);
+ int availableIndex = cursor.getColumnIndex(Root.COLUMN_AVAILABLE_BYTES);
+ while (storageCursor.moveToNext()) {
+ // If requested columnNames does not include COLUMN_XXX_BYTES, we don't
+ // calculate corresponding values.
+ if (capacityIndex != -1) {
+ capacityBytes += cursor.getLong(capacityIndex);
+ }
+ if (availableIndex != -1) {
+ availableBytes += cursor.getLong(availableIndex);
+ }
+ }
+ values.put(Root.COLUMN_CAPACITY_BYTES, capacityBytes);
+ values.put(Root.COLUMN_AVAILABLE_BYTES, availableBytes);
+ }
+ } finally {
+ storageCursor.close();
+ }
+
+ final RowBuilder row = result.newRow();
+ for (final String key : values.keySet()) {
+ row.add(key, values.get(key));
+ }
+ }
+
+ return result;
+ } finally {
+ deviceCursor.close();
+ }
}
/**
@@ -380,7 +459,10 @@
context.deleteDatabase(DATABASE_NAME);
}
- static void getDeviceDocumentValues(ContentValues values, MtpDeviceRecord device) {
+ static void getDeviceDocumentValues(
+ ContentValues values,
+ ContentValues extraValues,
+ MtpDeviceRecord device) {
values.clear();
values.put(COLUMN_DEVICE_ID, device.deviceId);
values.putNull(COLUMN_STORAGE_ID);
@@ -394,11 +476,15 @@
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 : device.roots) {
- size += root.mMaxCapacity - root.mFreeSpace;
- }
- values.put(Document.COLUMN_SIZE, size);
+ values.putNull(Document.COLUMN_SIZE);
+
+ extraValues.clear();
+ extraValues.put(
+ Root.COLUMN_FLAGS,
+ Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
+ extraValues.putNull(Root.COLUMN_AVAILABLE_BYTES);
+ extraValues.putNull(Root.COLUMN_CAPACITY_BYTES);
+ extraValues.put(Root.COLUMN_MIME_TYPES, "");
}
/**
@@ -408,7 +494,11 @@
* @param root Root to be converted {@link ContentValues}.
*/
static void getStorageDocumentValues(
- ContentValues values, Resources resources, String parentDocumentId, MtpRoot root) {
+ ContentValues values,
+ ContentValues extraValues,
+ Resources resources,
+ String parentDocumentId,
+ MtpRoot root) {
values.clear();
values.put(COLUMN_DEVICE_ID, root.mDeviceId);
values.put(COLUMN_STORAGE_ID, root.mStorageId);
@@ -424,6 +514,13 @@
values.put(Document.COLUMN_FLAGS, 0);
values.put(Document.COLUMN_SIZE,
(int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE));
+
+ extraValues.put(
+ Root.COLUMN_FLAGS,
+ Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
+ extraValues.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace);
+ extraValues.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity);
+ extraValues.put(Root.COLUMN_MIME_TYPES, "");
}
/**
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
index a233589..f252e0f 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -126,14 +126,14 @@
Document.COLUMN_LAST_MODIFIED + " INTEGER," +
Document.COLUMN_ICON + " INTEGER," +
Document.COLUMN_FLAGS + " INTEGER NOT NULL," +
- Document.COLUMN_SIZE + " INTEGER NOT NULL);";
+ Document.COLUMN_SIZE + " INTEGER);";
static final String QUERY_CREATE_ROOT_EXTRA =
"CREATE TABLE " + TABLE_ROOT_EXTRA + " (" +
Root.COLUMN_ROOT_ID + " INTEGER PRIMARY KEY," +
Root.COLUMN_FLAGS + " INTEGER NOT NULL," +
- Root.COLUMN_AVAILABLE_BYTES + " INTEGER NOT NULL," +
- Root.COLUMN_CAPACITY_BYTES + " INTEGER NOT NULL," +
+ Root.COLUMN_AVAILABLE_BYTES + " INTEGER," +
+ Root.COLUMN_CAPACITY_BYTES + " INTEGER," +
Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);";
/**
@@ -145,18 +145,26 @@
COLUMN_MAP_ROOTS = new HashMap<>();
COLUMN_MAP_ROOTS.put(Root.COLUMN_ROOT_ID, TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID);
COLUMN_MAP_ROOTS.put(Root.COLUMN_FLAGS, TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS);
- COLUMN_MAP_ROOTS.put(Root.COLUMN_ICON, TABLE_DOCUMENTS + "." + Document.COLUMN_ICON);
COLUMN_MAP_ROOTS.put(
- Root.COLUMN_TITLE, TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME);
- COLUMN_MAP_ROOTS.put(Root.COLUMN_SUMMARY, TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY);
+ Root.COLUMN_ICON,
+ TABLE_DOCUMENTS + "." + Document.COLUMN_ICON + " AS " + Root.COLUMN_ICON);
COLUMN_MAP_ROOTS.put(
- Root.COLUMN_DOCUMENT_ID, TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID);
+ Root.COLUMN_TITLE,
+ TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME + " AS " + Root.COLUMN_TITLE);
+ COLUMN_MAP_ROOTS.put(
+ Root.COLUMN_SUMMARY,
+ TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " + Root.COLUMN_SUMMARY);
+ COLUMN_MAP_ROOTS.put(
+ Root.COLUMN_DOCUMENT_ID,
+ TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID +
+ " AS " + Root.COLUMN_DOCUMENT_ID);
COLUMN_MAP_ROOTS.put(
Root.COLUMN_AVAILABLE_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES);
COLUMN_MAP_ROOTS.put(
Root.COLUMN_CAPACITY_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES);
COLUMN_MAP_ROOTS.put(
Root.COLUMN_MIME_TYPES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES);
+ COLUMN_MAP_ROOTS.put(COLUMN_DEVICE_ID, COLUMN_DEVICE_ID);
}
private static String createJoinFromClosure(
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index e6c2726..619ef54 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -127,7 +127,7 @@
continue;
}
mDatabase.getMapper().startAddingDocuments(documentId);
- if (mDatabase.getMapper().putRootDocuments(
+ if (mDatabase.getMapper().putStorageDocuments(
documentId, mResources, device.roots)) {
changed = true;
}