Change ID format of MtpDocumentsProvider.
BUG=25704562 Change-Id: I5d9fc167512eee06964650e07206e226173611b2
This commit is contained in:
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.database.MatrixCursor;
|
||||
import android.media.MediaFile;
|
||||
import android.mtp.MtpConstants;
|
||||
import android.mtp.MtpObjectInfo;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.DocumentsContract.Document;
|
||||
|
||||
/**
|
||||
* TODO Remove this class after we switch to use MtpDatabase.
|
||||
*/
|
||||
final class CursorHelper {
|
||||
static final int DUMMY_HANDLE_FOR_ROOT = 0;
|
||||
|
||||
private CursorHelper() {
|
||||
}
|
||||
|
||||
static void addToCursor(Resources resources, MtpRoot root, MatrixCursor.RowBuilder builder) {
|
||||
final Identifier identifier = new Identifier(
|
||||
root.mDeviceId, root.mStorageId, DUMMY_HANDLE_FOR_ROOT);
|
||||
builder.add(Document.COLUMN_DOCUMENT_ID, identifier.toDocumentId());
|
||||
builder.add(Document.COLUMN_DISPLAY_NAME, root.getRootName(resources));
|
||||
builder.add(Document.COLUMN_MIME_TYPE, DocumentsContract.Document.MIME_TYPE_DIR);
|
||||
builder.add(Document.COLUMN_LAST_MODIFIED, null);
|
||||
builder.add(Document.COLUMN_FLAGS, 0);
|
||||
builder.add(Document.COLUMN_SIZE,
|
||||
(int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
static void addToCursor(MtpObjectInfo objectInfo, Identifier rootIdentifier,
|
||||
MatrixCursor.RowBuilder builder) {
|
||||
final Identifier identifier = new Identifier(
|
||||
rootIdentifier.mDeviceId, rootIdentifier.mStorageId, objectInfo.getObjectHandle());
|
||||
final String mimeType = formatTypeToMimeType(objectInfo.getFormat());
|
||||
|
||||
int flag = 0;
|
||||
if (objectInfo.getProtectionStatus() == 0) {
|
||||
flag |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
|
||||
DocumentsContract.Document.FLAG_SUPPORTS_WRITE;
|
||||
if (mimeType == DocumentsContract.Document.MIME_TYPE_DIR) {
|
||||
flag |= DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE;
|
||||
}
|
||||
}
|
||||
if (objectInfo.getThumbCompressedSize() > 0) {
|
||||
flag |= DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL;
|
||||
}
|
||||
|
||||
builder.add(Document.COLUMN_DOCUMENT_ID, identifier.toDocumentId());
|
||||
builder.add(Document.COLUMN_DISPLAY_NAME, objectInfo.getName());
|
||||
builder.add(Document.COLUMN_MIME_TYPE, mimeType);
|
||||
builder.add(
|
||||
Document.COLUMN_LAST_MODIFIED,
|
||||
objectInfo.getDateModified() != 0 ? objectInfo.getDateModified() : null);
|
||||
builder.add(Document.COLUMN_FLAGS, flag);
|
||||
builder.add(Document.COLUMN_SIZE, objectInfo.getCompressedSize());
|
||||
}
|
||||
|
||||
static String formatTypeToMimeType(int format) {
|
||||
if (format == MtpConstants.FORMAT_ASSOCIATION) {
|
||||
return DocumentsContract.Document.MIME_TYPE_DIR;
|
||||
} else {
|
||||
return MediaFile.getMimeTypeForFormatCode(format);
|
||||
}
|
||||
}
|
||||
|
||||
static int mimeTypeToFormatType(String fileName, String mimeType) {
|
||||
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
|
||||
return MtpConstants.FORMAT_ASSOCIATION;
|
||||
} else {
|
||||
return MediaFile.getFormatCode(fileName, mimeType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@@ -75,7 +74,7 @@ class DocumentLoader {
|
||||
int parentHandle = parent.mObjectHandle;
|
||||
// Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to
|
||||
// getObjectHandles if we would like to obtain children under the root.
|
||||
if (parentHandle == CursorHelper.DUMMY_HANDLE_FOR_ROOT) {
|
||||
if (parentHandle == Identifier.DUMMY_HANDLE_FOR_ROOT) {
|
||||
parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN;
|
||||
}
|
||||
// TODO: Handle nit race around here.
|
||||
@@ -221,8 +220,8 @@ class DocumentLoader {
|
||||
throw new IOException(mError);
|
||||
}
|
||||
|
||||
final Cursor cursor = mDatabase.queryChildDocuments(
|
||||
columnNames, mIdentifier.mDocumentId, /* use old ID format */ true);
|
||||
final Cursor cursor =
|
||||
mDatabase.queryChildDocuments(columnNames, mIdentifier.mDocumentId);
|
||||
cursor.setNotificationUri(resolver, createUri());
|
||||
cursor.respond(extras);
|
||||
|
||||
@@ -282,7 +281,7 @@ class DocumentLoader {
|
||||
|
||||
private Uri createUri() {
|
||||
return DocumentsContract.buildChildDocumentsUri(
|
||||
MtpDocumentsProvider.AUTHORITY, mIdentifier.toDocumentId());
|
||||
MtpDocumentsProvider.AUTHORITY, mIdentifier.mDocumentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,39 +16,19 @@
|
||||
|
||||
package com.android.mtp;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Static utilities for ID.
|
||||
*/
|
||||
class Identifier {
|
||||
final static int DUMMY_HANDLE_FOR_ROOT = 0;
|
||||
|
||||
final int mDeviceId;
|
||||
final int mStorageId;
|
||||
final int mObjectHandle;
|
||||
final String mDocumentId;
|
||||
|
||||
static Identifier createFromRootId(String rootId) {
|
||||
final String[] components = rootId.split("_");
|
||||
return new Identifier(
|
||||
Integer.parseInt(components[0]),
|
||||
Integer.parseInt(components[1]));
|
||||
}
|
||||
|
||||
static Identifier createFromDocumentId(String documentId) {
|
||||
final String[] components = documentId.split("_");
|
||||
return new Identifier(
|
||||
Integer.parseInt(components[0]),
|
||||
Integer.parseInt(components[1]),
|
||||
Integer.parseInt(components[2]));
|
||||
}
|
||||
|
||||
|
||||
Identifier(int deviceId, int storageId) {
|
||||
this(deviceId, storageId, CursorHelper.DUMMY_HANDLE_FOR_ROOT);
|
||||
}
|
||||
|
||||
Identifier(int deviceId, int storageId, int objectHandle) {
|
||||
this(deviceId, storageId, objectHandle, null);
|
||||
}
|
||||
|
||||
Identifier(int deviceId, int storageId, int objectHandle, String documentId) {
|
||||
mDeviceId = deviceId;
|
||||
mStorageId = storageId;
|
||||
@@ -56,16 +36,6 @@ class Identifier {
|
||||
mDocumentId = documentId;
|
||||
}
|
||||
|
||||
// TODO: Make the ID persistent.
|
||||
String toRootId() {
|
||||
return String.format("%d_%d", mDeviceId, mStorageId);
|
||||
}
|
||||
|
||||
// TODO: Make the ID persistent.
|
||||
String toDocumentId() {
|
||||
return String.format("%d_%d_%d", mDeviceId, mStorageId, mObjectHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Identifier))
|
||||
@@ -77,6 +47,6 @@ class Identifier {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (mDeviceId << 16) ^ (mStorageId << 8) ^ mObjectHandle;
|
||||
return Objects.hash(mDeviceId, mStorageId, mObjectHandle, mDocumentId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.media.MediaFile;
|
||||
import android.mtp.MtpConstants;
|
||||
import android.mtp.MtpObjectInfo;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.DocumentsContract.Document;
|
||||
@@ -67,10 +69,8 @@ import java.util.Map;
|
||||
* the database tries to find corresponding rows by using document's name instead of MTP identifier
|
||||
* at the next update cycle.
|
||||
*
|
||||
* TODO: Remove @VisibleForTesting annotation when we start to use this class.
|
||||
* TODO: Improve performance by SQL optimization.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
class MtpDatabase {
|
||||
private final MtpDatabaseInternal mDatabase;
|
||||
|
||||
@@ -111,38 +111,21 @@ class MtpDatabase {
|
||||
/**
|
||||
* {@link MtpDatabaseInternal#queryChildDocuments}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) {
|
||||
return queryChildDocuments(columnNames, parentDocumentId, false);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Cursor queryChildDocuments(String[] columnNames, String parentDocumentId, boolean useOldId) {
|
||||
final String[] newColumnNames = new String[columnNames.length];
|
||||
|
||||
// TODO: Temporary replace document ID with old format.
|
||||
for (int i = 0; i < columnNames.length; i++) {
|
||||
if (useOldId && DocumentsContract.Document.COLUMN_DOCUMENT_ID.equals(columnNames[i])) {
|
||||
newColumnNames[i] = COLUMN_DEVICE_ID + " || '_' || " + COLUMN_STORAGE_ID +
|
||||
" || '_' || IFNULL(" + COLUMN_OBJECT_HANDLE + ",0) AS " +
|
||||
DocumentsContract.Document.COLUMN_DOCUMENT_ID;
|
||||
} else {
|
||||
newColumnNames[i] = columnNames[i];
|
||||
}
|
||||
}
|
||||
|
||||
return mDatabase.queryChildDocuments(newColumnNames, parentDocumentId);
|
||||
return mDatabase.queryChildDocuments(columnNames, parentDocumentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MtpDatabaseInternal#queryDocument}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
Cursor queryDocument(String documentId, String[] projection) {
|
||||
return mDatabase.queryDocument(documentId, projection);
|
||||
}
|
||||
|
||||
Identifier createIdentifier(String parentDocumentId) {
|
||||
/**
|
||||
* {@link MtpDatabaseInternal#createIdentifier}
|
||||
*/
|
||||
Identifier createIdentifier(String parentDocumentId) throws FileNotFoundException {
|
||||
return mDatabase.createIdentifier(parentDocumentId);
|
||||
}
|
||||
|
||||
@@ -157,7 +140,6 @@ class MtpDatabase {
|
||||
* {@link MtpDatabaseInternal#getParentId}
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String getParentId(String documentId) throws FileNotFoundException {
|
||||
return mDatabase.getParentId(documentId);
|
||||
}
|
||||
@@ -165,7 +147,6 @@ class MtpDatabase {
|
||||
/**
|
||||
* {@link MtpDatabaseInternal#deleteDocument}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void deleteDocument(String documentId) {
|
||||
mDatabase.deleteDocument(documentId);
|
||||
}
|
||||
@@ -174,7 +155,6 @@ class MtpDatabase {
|
||||
* {@link MtpDatabaseInternal#putNewDocument}
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String putNewDocument(int deviceId, String parentDocumentId, MtpObjectInfo info)
|
||||
throws FileNotFoundException {
|
||||
final ContentValues values = new ContentValues();
|
||||
@@ -405,7 +385,9 @@ class MtpDatabase {
|
||||
private void getChildDocumentValues(
|
||||
ContentValues values, int deviceId, String parentId, MtpObjectInfo info) {
|
||||
values.clear();
|
||||
final String mimeType = CursorHelper.formatTypeToMimeType(info.getFormat());
|
||||
final String mimeType = info.getFormat() == MtpConstants.FORMAT_ASSOCIATION ?
|
||||
DocumentsContract.Document.MIME_TYPE_DIR :
|
||||
MediaFile.getMimeTypeForFormatCode(info.getFormat());
|
||||
int flag = 0;
|
||||
if (info.getProtectionStatus() == 0) {
|
||||
flag |= Document.FLAG_SUPPORTS_DELETE |
|
||||
|
||||
@@ -129,11 +129,7 @@ class MtpDatabaseConstants {
|
||||
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 + "," +
|
||||
|
||||
@@ -191,6 +191,7 @@ class MtpDatabaseInternal {
|
||||
String putNewDocument(String parentDocumentId, ContentValues values) {
|
||||
mDatabase.beginTransaction();
|
||||
try {
|
||||
|
||||
final long id = mDatabase.insert(TABLE_DOCUMENTS, null, values);
|
||||
mDatabase.setTransactionSuccessful();
|
||||
return Long.toString(id);
|
||||
@@ -203,54 +204,28 @@ class MtpDatabaseInternal {
|
||||
* Gets identifier from document ID.
|
||||
* @param documentId Document ID.
|
||||
* @return Identifier.
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
Identifier createIdentifier(String documentId) {
|
||||
// Currently documentId is old format.
|
||||
final Identifier oldIdentifier = Identifier.createFromDocumentId(documentId);
|
||||
final String selection;
|
||||
final String[] args;
|
||||
if (oldIdentifier.mObjectHandle == CursorHelper.DUMMY_HANDLE_FOR_ROOT) {
|
||||
selection = COLUMN_DEVICE_ID + "= ? AND " +
|
||||
COLUMN_ROW_STATE + " IN (?, ?) AND " +
|
||||
COLUMN_STORAGE_ID + "= ? AND " +
|
||||
COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
|
||||
args = strings(
|
||||
oldIdentifier.mDeviceId,
|
||||
ROW_STATE_VALID,
|
||||
ROW_STATE_INVALIDATED,
|
||||
oldIdentifier.mStorageId);
|
||||
} else {
|
||||
selection = COLUMN_DEVICE_ID + "= ? AND " +
|
||||
COLUMN_ROW_STATE + " IN (?, ?) AND " +
|
||||
COLUMN_STORAGE_ID + "= ? AND " +
|
||||
COLUMN_OBJECT_HANDLE + " = ?";
|
||||
args = strings(
|
||||
oldIdentifier.mDeviceId,
|
||||
ROW_STATE_VALID,
|
||||
ROW_STATE_INVALIDATED,
|
||||
oldIdentifier.mStorageId,
|
||||
oldIdentifier.mObjectHandle);
|
||||
}
|
||||
|
||||
Identifier createIdentifier(String documentId) throws FileNotFoundException {
|
||||
final Cursor cursor = mDatabase.query(
|
||||
TABLE_DOCUMENTS,
|
||||
strings(Document.COLUMN_DOCUMENT_ID),
|
||||
selection,
|
||||
args,
|
||||
strings(COLUMN_DEVICE_ID, COLUMN_STORAGE_ID, COLUMN_OBJECT_HANDLE),
|
||||
SELECTION_DOCUMENT_ID,
|
||||
strings(documentId),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"1");
|
||||
try {
|
||||
if (cursor.getCount() == 0) {
|
||||
return oldIdentifier;
|
||||
throw new FileNotFoundException("ID is not found.");
|
||||
} else {
|
||||
cursor.moveToNext();
|
||||
return new Identifier(
|
||||
oldIdentifier.mDeviceId,
|
||||
oldIdentifier.mStorageId,
|
||||
oldIdentifier.mObjectHandle,
|
||||
cursor.getString(0));
|
||||
cursor.getInt(0),
|
||||
cursor.getInt(1),
|
||||
cursor.isNull(2) ? Identifier.DUMMY_HANDLE_FOR_ROOT : cursor.getInt(2),
|
||||
documentId);
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
@@ -481,7 +456,8 @@ class MtpDatabaseInternal {
|
||||
void clearMapping() {
|
||||
mDatabase.beginTransaction();
|
||||
try {
|
||||
deleteDocumentsAndRootsRecursively(COLUMN_ROW_STATE + " = ?", strings(ROW_STATE_PENDING));
|
||||
deleteDocumentsAndRootsRecursively(
|
||||
COLUMN_ROW_STATE + " = ?", strings(ROW_STATE_PENDING));
|
||||
final ContentValues values = new ContentValues();
|
||||
values.putNull(COLUMN_OBJECT_HANDLE);
|
||||
values.putNull(COLUMN_STORAGE_ID);
|
||||
@@ -571,7 +547,6 @@ class MtpDatabaseInternal {
|
||||
args);
|
||||
deleted += mDatabase.delete(TABLE_DOCUMENTS, selection, args);
|
||||
mDatabase.setTransactionSuccessful();
|
||||
// TODO Remove child.
|
||||
// TODO Remove mappingState.
|
||||
return deleted != 0;
|
||||
} finally {
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.graphics.Point;
|
||||
import android.media.MediaFile;
|
||||
import android.mtp.MtpConstants;
|
||||
import android.mtp.MtpObjectInfo;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
@@ -116,39 +118,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
if (projection == null) {
|
||||
projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION;
|
||||
}
|
||||
final Identifier identifier = Identifier.createFromDocumentId(documentId);
|
||||
|
||||
if (identifier.mObjectHandle != CursorHelper.DUMMY_HANDLE_FOR_ROOT) {
|
||||
MtpObjectInfo objectInfo;
|
||||
try {
|
||||
objectInfo = mMtpManager.getObjectInfo(
|
||||
identifier.mDeviceId, identifier.mObjectHandle);
|
||||
} catch (IOException e) {
|
||||
throw new FileNotFoundException(e.getMessage());
|
||||
}
|
||||
final MatrixCursor cursor = new MatrixCursor(projection);
|
||||
CursorHelper.addToCursor(
|
||||
objectInfo,
|
||||
new Identifier(identifier.mDeviceId, identifier.mStorageId),
|
||||
cursor.newRow());
|
||||
return cursor;
|
||||
} else {
|
||||
MtpRoot[] roots;
|
||||
try {
|
||||
roots = mMtpManager.getRoots(identifier.mDeviceId);
|
||||
} catch (IOException e) {
|
||||
throw new FileNotFoundException(e.getMessage());
|
||||
}
|
||||
for (final MtpRoot root : roots) {
|
||||
if (identifier.mStorageId != root.mStorageId)
|
||||
continue;
|
||||
final MatrixCursor cursor = new MatrixCursor(projection);
|
||||
CursorHelper.addToCursor(mResources, root, cursor.newRow());
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileNotFoundException();
|
||||
return mDatabase.queryDocument(documentId, projection);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -170,7 +140,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
public ParcelFileDescriptor openDocument(
|
||||
String documentId, String mode, CancellationSignal signal)
|
||||
throws FileNotFoundException {
|
||||
final Identifier identifier = Identifier.createFromDocumentId(documentId);
|
||||
final Identifier identifier = mDatabase.createIdentifier(documentId);
|
||||
try {
|
||||
switch (mode) {
|
||||
case "r":
|
||||
@@ -195,7 +165,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
String documentId,
|
||||
Point sizeHint,
|
||||
CancellationSignal signal) throws FileNotFoundException {
|
||||
final Identifier identifier = Identifier.createFromDocumentId(documentId);
|
||||
final Identifier identifier = mDatabase.createIdentifier(documentId);
|
||||
try {
|
||||
return new AssetFileDescriptor(
|
||||
getPipeManager(identifier).readThumbnail(mMtpManager, identifier),
|
||||
@@ -209,15 +179,17 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
@Override
|
||||
public void deleteDocument(String documentId) throws FileNotFoundException {
|
||||
try {
|
||||
final Identifier identifier = Identifier.createFromDocumentId(documentId);
|
||||
final int parentHandle =
|
||||
mMtpManager.getParent(identifier.mDeviceId, identifier.mObjectHandle);
|
||||
final Identifier identifier = mDatabase.createIdentifier(documentId);
|
||||
final Identifier parentIdentifier =
|
||||
mDatabase.createIdentifier(mDatabase.getParentId(documentId));
|
||||
mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);
|
||||
final Identifier parentIdentifier = new Identifier(
|
||||
identifier.mDeviceId, identifier.mStorageId, parentHandle);
|
||||
mDatabase.deleteDocument(documentId);
|
||||
getDocumentLoader(parentIdentifier).clearTask(parentIdentifier);
|
||||
notifyChildDocumentsChange(parentIdentifier.toDocumentId());
|
||||
notifyChildDocumentsChange(parentIdentifier.mDocumentId);
|
||||
} catch (IOException error) {
|
||||
for (final StackTraceElement element : error.getStackTrace()) {
|
||||
Log.e("hirono", element.toString());
|
||||
}
|
||||
throw new FileNotFoundException(error.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -235,19 +207,23 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
public String createDocument(String parentDocumentId, String mimeType, String displayName)
|
||||
throws FileNotFoundException {
|
||||
try {
|
||||
final Identifier parentId = Identifier.createFromDocumentId(parentDocumentId);
|
||||
final Identifier parentId = mDatabase.createIdentifier(parentDocumentId);
|
||||
final ParcelFileDescriptor pipe[] = ParcelFileDescriptor.createReliablePipe();
|
||||
pipe[0].close(); // 0 bytes for a new document.
|
||||
final int objectHandle = mMtpManager.createDocument(
|
||||
parentId.mDeviceId,
|
||||
new MtpObjectInfo.Builder()
|
||||
.setStorageId(parentId.mStorageId)
|
||||
.setParent(parentId.mObjectHandle)
|
||||
.setFormat(CursorHelper.mimeTypeToFormatType(displayName, mimeType))
|
||||
.setName(displayName)
|
||||
.build(), pipe[1]);
|
||||
final String documentId = new Identifier(parentId.mDeviceId, parentId.mStorageId,
|
||||
objectHandle).toDocumentId();
|
||||
final int formatCode = Document.MIME_TYPE_DIR.equals(mimeType) ?
|
||||
MtpConstants.FORMAT_ASSOCIATION :
|
||||
MediaFile.getFormatCode(displayName, mimeType);
|
||||
final MtpObjectInfo info = new MtpObjectInfo.Builder()
|
||||
.setStorageId(parentId.mStorageId)
|
||||
.setParent(parentId.mObjectHandle)
|
||||
.setFormat(formatCode)
|
||||
.setName(displayName)
|
||||
.build();
|
||||
final int objectHandle = mMtpManager.createDocument(parentId.mDeviceId, info, pipe[1]);
|
||||
final MtpObjectInfo infoWithHandle =
|
||||
new MtpObjectInfo.Builder(info).setObjectHandle(objectHandle).build();
|
||||
final String documentId = mDatabase.putNewDocument(
|
||||
parentId.mDeviceId, parentDocumentId, infoWithHandle);
|
||||
getDocumentLoader(parentId).clearTask(parentId);
|
||||
notifyChildDocumentsChange(parentDocumentId);
|
||||
return documentId;
|
||||
|
||||
@@ -57,7 +57,7 @@ public class DocumentLoaderTest extends AndroidTestCase {
|
||||
|
||||
public void testBasic() throws Exception {
|
||||
final Uri uri = DocumentsContract.buildChildDocumentsUri(
|
||||
MtpDocumentsProvider.AUTHORITY, mParentIdentifier.toDocumentId());
|
||||
MtpDocumentsProvider.AUTHORITY, mParentIdentifier.mDocumentId);
|
||||
setUpDocument(mManager, 40);
|
||||
mManager.blockDocument(0, 15);
|
||||
mManager.blockDocument(0, 35);
|
||||
|
||||
@@ -104,7 +104,7 @@ public class MtpDatabaseTest extends AndroidTestCase {
|
||||
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
|
||||
});
|
||||
|
||||
@@ -27,6 +27,9 @@ import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import static com.android.mtp.MtpDatabaseInternal.strings;
|
||||
|
||||
@SmallTest
|
||||
public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
@@ -138,7 +141,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
// TODO: Add storage icon for MTP devices.
|
||||
assertTrue(cursor.isNull(2) /* icon */);
|
||||
assertEquals("Device A Storage A", cursor.getString(3));
|
||||
assertEquals("0_1_0", cursor.getString(4));
|
||||
assertEquals("1", cursor.getString(4));
|
||||
assertEquals(1024, cursor.getInt(5));
|
||||
}
|
||||
|
||||
@@ -154,7 +157,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
// TODO: Add storage icon for MTP devices.
|
||||
assertTrue(cursor.isNull(2) /* icon */);
|
||||
assertEquals("Device B Storage B", cursor.getString(3));
|
||||
assertEquals("1_1_0", cursor.getString(4));
|
||||
assertEquals("2", cursor.getString(4));
|
||||
assertEquals(2048, cursor.getInt(5));
|
||||
}
|
||||
}
|
||||
@@ -186,29 +189,38 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
// TODO: Add storage icon for MTP devices.
|
||||
assertTrue(cursor.isNull(2) /* icon */);
|
||||
assertEquals("Device B Storage B", cursor.getString(3));
|
||||
assertEquals("1_1_0", cursor.getString(4));
|
||||
assertEquals("1", cursor.getString(4));
|
||||
assertEquals(2048, cursor.getInt(5));
|
||||
}
|
||||
}
|
||||
|
||||
public void testQueryDocument() throws IOException {
|
||||
public void testQueryDocument() throws IOException, InterruptedException, TimeoutException {
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
|
||||
.setObjectHandle(2)
|
||||
.setStorageId(1)
|
||||
.setFormat(MtpConstants.FORMAT_EXIF_JPEG)
|
||||
.setName("image.jpg")
|
||||
.setDateModified(1422716400000L)
|
||||
.setCompressedSize(1024 * 1024 * 5)
|
||||
.setThumbCompressedSize(1024 * 50)
|
||||
.build());
|
||||
final Cursor cursor = mProvider.queryDocument("0_1_2", null);
|
||||
|
||||
setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
|
||||
setupDocuments(
|
||||
0,
|
||||
0,
|
||||
MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
|
||||
"1",
|
||||
new MtpObjectInfo[] {
|
||||
new MtpObjectInfo.Builder()
|
||||
.setObjectHandle(100)
|
||||
.setFormat(MtpConstants.FORMAT_EXIF_JPEG)
|
||||
.setName("image.jpg")
|
||||
.setDateModified(1422716400000L)
|
||||
.setCompressedSize(1024 * 1024 * 5)
|
||||
.setThumbCompressedSize(50 * 1024)
|
||||
.build()
|
||||
});
|
||||
|
||||
final Cursor cursor = mProvider.queryDocument("2", null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
|
||||
cursor.moveToNext();
|
||||
|
||||
assertEquals("0_1_2", cursor.getString(0));
|
||||
assertEquals("2", cursor.getString(0));
|
||||
assertEquals("image/jpeg", cursor.getString(1));
|
||||
assertEquals("image.jpg", cursor.getString(2));
|
||||
assertEquals(1422716400000L, cursor.getLong(3));
|
||||
@@ -220,21 +232,32 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
assertEquals(1024 * 1024 * 5, cursor.getInt(5));
|
||||
}
|
||||
|
||||
public void testQueryDocument_directory() throws IOException {
|
||||
public void testQueryDocument_directory()
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
|
||||
.setObjectHandle(2)
|
||||
.setStorageId(1)
|
||||
.setFormat(MtpConstants.FORMAT_ASSOCIATION)
|
||||
.setName("directory")
|
||||
.setDateModified(1422716400000L)
|
||||
.build());
|
||||
final Cursor cursor = mProvider.queryDocument("0_1_2", null);
|
||||
|
||||
setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
|
||||
setupDocuments(
|
||||
0,
|
||||
0,
|
||||
MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
|
||||
"1",
|
||||
new MtpObjectInfo[] {
|
||||
new MtpObjectInfo.Builder()
|
||||
.setObjectHandle(2)
|
||||
.setStorageId(1)
|
||||
.setFormat(MtpConstants.FORMAT_ASSOCIATION)
|
||||
.setName("directory")
|
||||
.setDateModified(1422716400000L)
|
||||
.build()
|
||||
});
|
||||
|
||||
final Cursor cursor = mProvider.queryDocument("2", null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
|
||||
cursor.moveToNext();
|
||||
assertEquals("0_1_2", cursor.getString(0));
|
||||
assertEquals("2", cursor.getString(0));
|
||||
assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
|
||||
assertEquals("directory", cursor.getString(2));
|
||||
assertEquals(1422716400000L, cursor.getLong(3));
|
||||
@@ -246,10 +269,12 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
assertEquals(0, cursor.getInt(5));
|
||||
}
|
||||
|
||||
public void testQueryDocument_forRoot() throws IOException {
|
||||
public void testQueryDocument_forRoot()
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
mMtpManager.setRoots(0, new MtpRoot[] {
|
||||
|
||||
setupRoots(0, new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
0 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
@@ -259,11 +284,11 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
4096 /* total space */,
|
||||
"" /* no volume identifier */)
|
||||
});
|
||||
final Cursor cursor = mProvider.queryDocument("0_1_0", null);
|
||||
final Cursor cursor = mProvider.queryDocument("1", null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
|
||||
cursor.moveToNext();
|
||||
assertEquals("0_1_0", cursor.getString(0));
|
||||
assertEquals("1", cursor.getString(0));
|
||||
assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
|
||||
assertEquals("Device A Storage A", cursor.getString(2));
|
||||
assertTrue(cursor.isNull(3));
|
||||
@@ -274,42 +299,43 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
public void testQueryChildDocuments() throws Exception {
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
|
||||
|
||||
mDatabase.startAddingRootDocuments(0);
|
||||
mDatabase.putRootDocuments(0, mResources, new MtpRoot[] {
|
||||
new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "")
|
||||
});
|
||||
mDatabase.stopAddingRootDocuments(0);
|
||||
setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
|
||||
setupDocuments(
|
||||
0,
|
||||
0,
|
||||
MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
|
||||
"1",
|
||||
new MtpObjectInfo[] {
|
||||
new MtpObjectInfo.Builder()
|
||||
.setObjectHandle(100)
|
||||
.setFormat(MtpConstants.FORMAT_EXIF_JPEG)
|
||||
.setName("image.jpg")
|
||||
.setCompressedSize(1024 * 1024 * 5)
|
||||
.setThumbCompressedSize(5 * 1024)
|
||||
.setProtectionStatus(MtpConstants.PROTECTION_STATUS_READ_ONLY)
|
||||
.build()
|
||||
});
|
||||
|
||||
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
|
||||
.setObjectHandle(1)
|
||||
.setFormat(MtpConstants.FORMAT_EXIF_JPEG)
|
||||
.setName("image.jpg")
|
||||
.setCompressedSize(1024 * 1024 * 5)
|
||||
.setThumbCompressedSize(5 * 1024)
|
||||
.setProtectionStatus(MtpConstants.PROTECTION_STATUS_READ_ONLY)
|
||||
.build());
|
||||
|
||||
final Cursor cursor = mProvider.queryChildDocuments("0_0_0", null, null);
|
||||
final Cursor cursor = mProvider.queryChildDocuments("1", null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
|
||||
assertTrue(cursor.moveToNext());
|
||||
assertEquals("0_0_1", cursor.getString(0));
|
||||
assertEquals("2", cursor.getString(0));
|
||||
assertEquals("image/jpeg", cursor.getString(1));
|
||||
assertEquals("image.jpg", cursor.getString(2));
|
||||
assertEquals(0, cursor.getLong(3));
|
||||
assertEquals(DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL, cursor.getInt(4));
|
||||
assertEquals(1024 * 1024 * 5, cursor.getInt(5));
|
||||
|
||||
assertFalse(cursor.moveToNext());
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
public void testQueryChildDocuments_cursorError() throws Exception {
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
try {
|
||||
mProvider.queryChildDocuments("0_0_0", null, null);
|
||||
mProvider.queryChildDocuments("1", null, null);
|
||||
fail();
|
||||
} catch (Throwable error) {
|
||||
assertTrue(error instanceof FileNotFoundException);
|
||||
@@ -319,42 +345,95 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
public void testQueryChildDocuments_documentError() throws Exception {
|
||||
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 {
|
||||
mProvider.queryChildDocuments("0_0_0", null, null);
|
||||
mProvider.queryChildDocuments("1", null, null);
|
||||
fail();
|
||||
} catch (Throwable error) {
|
||||
assertTrue(error instanceof FileNotFoundException);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDeleteDocument() throws IOException {
|
||||
public void testDeleteDocument() throws IOException, InterruptedException, TimeoutException {
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
|
||||
.setObjectHandle(1)
|
||||
.setParent(2)
|
||||
.build());
|
||||
mProvider.deleteDocument("0_0_1");
|
||||
setupRoots(0, new MtpRoot[] {
|
||||
new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
|
||||
});
|
||||
setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
|
||||
new MtpObjectInfo.Builder()
|
||||
.setName("test.txt")
|
||||
.setObjectHandle(1)
|
||||
.setParent(-1)
|
||||
.build()
|
||||
});
|
||||
|
||||
mProvider.deleteDocument("2");
|
||||
assertEquals(1, mResolver.getChangeCount(
|
||||
DocumentsContract.buildChildDocumentsUri(
|
||||
MtpDocumentsProvider.AUTHORITY, "0_0_2")));
|
||||
MtpDocumentsProvider.AUTHORITY, "1")));
|
||||
}
|
||||
|
||||
public void testDeleteDocument_error() throws IOException {
|
||||
public void testDeleteDocument_error()
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
mMtpManager.addValidDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
|
||||
.setObjectHandle(2)
|
||||
.build());
|
||||
setupRoots(0, new MtpRoot[] {
|
||||
new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
|
||||
});
|
||||
setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
|
||||
new MtpObjectInfo.Builder()
|
||||
.setName("test.txt")
|
||||
.setObjectHandle(1)
|
||||
.setParent(-1)
|
||||
.build()
|
||||
});
|
||||
try {
|
||||
mProvider.deleteDocument("0_0_1");
|
||||
mProvider.deleteDocument("3");
|
||||
fail();
|
||||
} catch (Throwable e) {
|
||||
assertTrue(e instanceof IOException);
|
||||
}
|
||||
assertEquals(0, mResolver.getChangeCount(
|
||||
DocumentsContract.buildChildDocumentsUri(
|
||||
MtpDocumentsProvider.AUTHORITY, "0_0_2")));
|
||||
MtpDocumentsProvider.AUTHORITY, "1")));
|
||||
}
|
||||
|
||||
private String[] getStrings(Cursor cursor) {
|
||||
try {
|
||||
final String[] results = new String[cursor.getCount()];
|
||||
for (int i = 0; cursor.moveToNext(); i++) {
|
||||
results[i] = cursor.getString(0);
|
||||
}
|
||||
return results;
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
private String[] setupRoots(int deviceId, MtpRoot[] roots)
|
||||
throws FileNotFoundException, InterruptedException, TimeoutException {
|
||||
final int changeCount = mResolver.getChangeCount(ROOTS_URI);
|
||||
mMtpManager.setRoots(deviceId, roots);
|
||||
mResolver.waitForNotification(ROOTS_URI, changeCount + 1);
|
||||
return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID)));
|
||||
}
|
||||
|
||||
private String[] setupDocuments(
|
||||
int deviceId,
|
||||
int storageId,
|
||||
int parentHandle,
|
||||
String parentDocumentId,
|
||||
MtpObjectInfo[] objects) throws FileNotFoundException {
|
||||
final int[] handles = new int[objects.length];
|
||||
int i = 0;
|
||||
for (final MtpObjectInfo info : objects) {
|
||||
handles[i] = info.getObjectHandle();
|
||||
mMtpManager.setObjectInfo(deviceId, info);
|
||||
}
|
||||
mMtpManager.setObjectHandles(deviceId, storageId, parentHandle, handles);
|
||||
return getStrings(mProvider.queryChildDocuments(
|
||||
parentDocumentId, strings(DocumentsContract.Document.COLUMN_DOCUMENT_ID), null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,13 +44,13 @@ public class PipeManagerTest extends AndroidTestCase {
|
||||
public void testReadDocument_basic() throws Exception {
|
||||
mtpManager.setImportFileBytes(0, 1, HELLO_BYTES);
|
||||
final ParcelFileDescriptor descriptor = mPipeManager.readDocument(
|
||||
mtpManager, new Identifier(0, 0, 1));
|
||||
mtpManager, new Identifier(0, 0, 1, null));
|
||||
assertDescriptor(descriptor, HELLO_BYTES);
|
||||
}
|
||||
|
||||
public void testReadDocument_error() throws Exception {
|
||||
final ParcelFileDescriptor descriptor =
|
||||
mPipeManager.readDocument(mtpManager, new Identifier(0, 0, 1));
|
||||
mPipeManager.readDocument(mtpManager, new Identifier(0, 0, 1, null));
|
||||
assertDescriptorError(descriptor);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class PipeManagerTest extends AndroidTestCase {
|
||||
|
||||
// Upload testing bytes.
|
||||
final ParcelFileDescriptor descriptor = mPipeManager.writeDocument(
|
||||
getContext(), mtpManager, new Identifier(0, 0, 1));
|
||||
getContext(), mtpManager, new Identifier(0, 0, 1, null));
|
||||
final ParcelFileDescriptor.AutoCloseOutputStream outputStream =
|
||||
new ParcelFileDescriptor.AutoCloseOutputStream(descriptor);
|
||||
outputStream.write(HELLO_BYTES, 0, HELLO_BYTES.length);
|
||||
@@ -94,13 +94,13 @@ public class PipeManagerTest extends AndroidTestCase {
|
||||
public void testReadThumbnail_basic() throws Exception {
|
||||
mtpManager.setThumbnail(0, 1, HELLO_BYTES);
|
||||
final ParcelFileDescriptor descriptor = mPipeManager.readThumbnail(
|
||||
mtpManager, new Identifier(0, 0, 1));
|
||||
mtpManager, new Identifier(0, 0, 1, null));
|
||||
assertDescriptor(descriptor, HELLO_BYTES);
|
||||
}
|
||||
|
||||
public void testReadThumbnail_error() throws Exception {
|
||||
final ParcelFileDescriptor descriptor =
|
||||
mPipeManager.readThumbnail(mtpManager, new Identifier(0, 0, 1));
|
||||
mPipeManager.readThumbnail(mtpManager, new Identifier(0, 0, 1, null));
|
||||
assertDescriptorError(descriptor);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user