Adjust MTP to reference by specific volume name.
The MediaStore.VOLUME_EXTERNAL volume is a merged view of all storage
devices, and clients working on a specific volume need to focus on
the volume they're interested in.
Bug: 129840030
Test: atest --test-mapping packages/providers/MediaProvider
Change-Id: I91cee6a96d7f9360e6a93a9a3c389b097b6b9967
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 6361fd8..4ac6d35 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -36,6 +36,7 @@
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
+import android.util.SparseArray;
import android.view.Display;
import android.view.WindowManager;
@@ -69,8 +70,6 @@
private final Context mContext;
private final ContentProviderClient mMediaProvider;
- private final String mVolumeName;
- private final Uri mObjectsUri;
private final AtomicBoolean mClosed = new AtomicBoolean();
private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -78,10 +77,10 @@
private final HashMap<String, MtpStorage> mStorageMap = new HashMap<>();
// cached property groups for single properties
- private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty = new HashMap<>();
+ private final SparseArray<MtpPropertyGroup> mPropertyGroupsByProperty = new SparseArray<>();
// cached property groups for all properties for a given format
- private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByFormat = new HashMap<>();
+ private final SparseArray<MtpPropertyGroup> mPropertyGroupsByFormat = new SparseArray<>();
// SharedPreferences for writable MTP device properties
private SharedPreferences mDeviceProperties;
@@ -271,14 +270,11 @@
}
};
- public MtpDatabase(Context context, String volumeName,
- String[] subDirectories) {
+ public MtpDatabase(Context context, String[] subDirectories) {
native_setup();
mContext = Objects.requireNonNull(context);
mMediaProvider = context.getContentResolver()
.acquireContentProviderClient(MediaStore.AUTHORITY);
- mVolumeName = volumeName;
- mObjectsUri = Files.getMtpObjectsUri(volumeName);
mManager = new MtpStorageManager(new MtpStorageManager.MtpNotifier() {
@Override
public void sendObjectAdded(int id) {
@@ -526,8 +522,7 @@
propertyGroup = mPropertyGroupsByFormat.get(format);
if (propertyGroup == null) {
final int[] propertyList = getSupportedObjectProperties(format);
- propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName,
- propertyList);
+ propertyGroup = new MtpPropertyGroup(propertyList);
mPropertyGroupsByFormat.put(format, propertyGroup);
}
} else {
@@ -535,12 +530,11 @@
propertyGroup = mPropertyGroupsByProperty.get(property);
if (propertyGroup == null) {
final int[] propertyList = new int[]{property};
- propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName,
- propertyList);
+ propertyGroup = new MtpPropertyGroup(propertyList);
mPropertyGroupsByProperty.put(property, propertyGroup);
}
}
- int err = propertyGroup.getPropertyList(obj, ret);
+ int err = propertyGroup.getPropertyList(mMediaProvider, obj.getVolumeName(), obj, ret);
if (err != MtpConstants.RESPONSE_OK) {
return new MtpPropertyList(err);
}
@@ -581,7 +575,8 @@
try {
// note - we are relying on a special case in MediaProvider.update() to update
// the paths for all children in the case where this is a directory.
- mMediaProvider.update(mObjectsUri, values, PATH_WHERE, whereArgs);
+ final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
+ mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in mMediaProvider.update", e);
}
@@ -640,12 +635,12 @@
if (obj.getParent().isRoot()) {
values.put(Files.FileColumns.PARENT, 0);
} else {
- int parentId = findInMedia(path.getParent());
+ int parentId = findInMedia(newParentObj, path.getParent());
if (parentId != -1) {
values.put(Files.FileColumns.PARENT, parentId);
} else {
// The new parent isn't in MediaProvider, so delete the object instead
- deleteFromMedia(oldPath, obj.isDir());
+ deleteFromMedia(obj, oldPath, obj.isDir());
return;
}
}
@@ -655,13 +650,14 @@
try {
int parentId = -1;
if (!oldParentObj.isRoot()) {
- parentId = findInMedia(oldPath.getParent());
+ parentId = findInMedia(oldParentObj, oldPath.getParent());
}
if (oldParentObj.isRoot() || parentId != -1) {
// Old parent exists in MediaProvider - perform a move
// note - we are relying on a special case in MediaProvider.update() to update
// the paths for all children in the case where this is a directory.
- mMediaProvider.update(mObjectsUri, values, PATH_WHERE, whereArgs);
+ final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
+ mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
} else {
// Old parent doesn't exist - add the object
MediaStore.scanFile(mContext, path.toFile());
@@ -823,14 +819,16 @@
if (!mManager.endRemoveObject(obj, success))
Log.e(TAG, "Failed to end remove object");
if (success)
- deleteFromMedia(obj.getPath(), obj.isDir());
+ deleteFromMedia(obj, obj.getPath(), obj.isDir());
}
- private int findInMedia(Path path) {
+ private int findInMedia(MtpStorageManager.MtpObject obj, Path path) {
+ final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
+
int ret = -1;
Cursor c = null;
try {
- c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE,
+ c = mMediaProvider.query(objectsUri, ID_PROJECTION, PATH_WHERE,
new String[]{path.toString()}, null, null);
if (c != null && c.moveToNext()) {
ret = c.getInt(0);
@@ -844,12 +842,13 @@
return ret;
}
- private void deleteFromMedia(Path path, boolean isDir) {
+ private void deleteFromMedia(MtpStorageManager.MtpObject obj, Path path, boolean isDir) {
+ final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
try {
// Delete the object(s) from MediaProvider, but ignore errors.
if (isDir) {
// recursive case - delete all children first
- mMediaProvider.delete(mObjectsUri,
+ mMediaProvider.delete(objectsUri,
// the 'like' makes it use the index, the 'lower()' makes it correct
// when the path contains sqlite wildcard characters
"_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
@@ -858,7 +857,7 @@
}
String[] whereArgs = new String[]{path.toString()};
- if (mMediaProvider.delete(mObjectsUri, PATH_WHERE, whereArgs) > 0) {
+ if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) > 0) {
if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) {
MediaStore.scanFile(mContext, path.getParent().toFile());
}
@@ -876,10 +875,10 @@
if (obj == null)
return null;
// Translate this handle to the MediaProvider Handle
- handle = findInMedia(obj.getPath());
+ handle = findInMedia(obj, obj.getPath());
if (handle == -1)
return null;
- Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
+ Uri uri = Files.getMtpReferencesUri(obj.getVolumeName(), handle);
Cursor c = null;
try {
c = mMediaProvider.query(uri, PATH_PROJECTION, null, null, null, null);
@@ -912,17 +911,17 @@
if (obj == null)
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
// Translate this handle to the MediaProvider Handle
- handle = findInMedia(obj.getPath());
+ handle = findInMedia(obj, obj.getPath());
if (handle == -1)
return MtpConstants.RESPONSE_GENERAL_ERROR;
- Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
+ Uri uri = Files.getMtpReferencesUri(obj.getVolumeName(), handle);
ArrayList<ContentValues> valuesList = new ArrayList<>();
for (int id : references) {
// Translate each reference id to the MediaProvider Id
MtpStorageManager.MtpObject refObj = mManager.getObject(id);
if (refObj == null)
continue;
- int refHandle = findInMedia(refObj.getPath());
+ int refHandle = findInMedia(refObj, refObj.getPath());
if (refHandle == -1)
continue;
ContentValues values = new ContentValues();
diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java
index 6d5be8e..5bb0c1b 100644
--- a/media/java/android/mtp/MtpPropertyGroup.java
+++ b/media/java/android/mtp/MtpPropertyGroup.java
@@ -46,9 +46,6 @@
}
}
- private final ContentProviderClient mProvider;
- private final String mVolumeName;
-
// list of all properties in this group
private final Property[] mProperties;
@@ -58,10 +55,7 @@
private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
// constructs a property group for a list of properties
- public MtpPropertyGroup(ContentProviderClient provider, String volumeName, int[] properties) {
- mProvider = provider;
- mVolumeName = volumeName;
-
+ public MtpPropertyGroup(int[] properties) {
int count = properties.length;
ArrayList<String> columns = new ArrayList<>(count);
columns.add(Files.FileColumns._ID);
@@ -175,7 +169,8 @@
* object and adds them to the given property list.
* @return Response_OK if the operation succeeded.
*/
- public int getPropertyList(MtpStorageManager.MtpObject object, MtpPropertyList list) {
+ public int getPropertyList(ContentProviderClient provider, String volumeName,
+ MtpStorageManager.MtpObject object, MtpPropertyList list) {
Cursor c = null;
int id = object.getId();
String path = object.getPath().toString();
@@ -184,8 +179,8 @@
try {
// Look up the entry in MediaProvider only if one of those properties is needed.
final Uri uri = MtpDatabase.getObjectPropertiesUri(object.getFormat(),
- mVolumeName);
- c = mProvider.query(uri, mColumns,
+ volumeName);
+ c = provider.query(uri, mColumns,
PATH_WHERE, new String[] {path}, null, null);
if (c != null && !c.moveToNext()) {
c.close();
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index c714b3c..65d0fef 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -18,6 +18,7 @@
import android.annotation.UnsupportedAppUsage;
import android.os.storage.StorageVolume;
+import android.provider.MediaStore;
/**
* This class represents a storage unit on an MTP device.
@@ -27,12 +28,12 @@
* @hide
*/
public class MtpStorage {
-
private final int mStorageId;
private final String mPath;
private final String mDescription;
private final boolean mRemovable;
private final long mMaxFileSize;
+ private final String mVolumeName;
public MtpStorage(StorageVolume volume, int storageId) {
mStorageId = storageId;
@@ -40,6 +41,11 @@
mDescription = volume.getDescription(null);
mRemovable = volume.isRemovable();
mMaxFileSize = volume.getMaxFileSize();
+ if (volume.isPrimary()) {
+ mVolumeName = MediaStore.VOLUME_EXTERNAL_PRIMARY;
+ } else {
+ mVolumeName = volume.getNormalizedUuid();
+ }
}
/**
@@ -88,4 +94,8 @@
public long getMaxFileSize() {
return mMaxFileSize;
}
+
+ public String getVolumeName() {
+ return mVolumeName;
+ }
}
diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java
index f14e7d7..e783788 100644
--- a/media/java/android/mtp/MtpStorageManager.java
+++ b/media/java/android/mtp/MtpStorageManager.java
@@ -21,6 +21,8 @@
import android.os.storage.StorageVolume;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import java.io.IOException;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
@@ -131,6 +133,7 @@
/** MtpObject represents either a file or directory in an associated storage. **/
public static class MtpObject {
+ private MtpStorage mStorage;
// null for root objects
private MtpObject mParent;
@@ -147,9 +150,10 @@
// null if not both a directory and visited
private FileObserver mObserver;
- MtpObject(String name, int id, MtpObject parent, boolean isDir) {
+ MtpObject(String name, int id, MtpStorage storage, MtpObject parent, boolean isDir) {
mId = id;
mName = name;
+ mStorage = Preconditions.checkNotNull(storage);
mParent = parent;
mObserver = null;
mVisited = false;
@@ -206,6 +210,10 @@
return mParent == null;
}
+ public String getVolumeName() {
+ return mStorage.getVolumeName();
+ }
+
/** For MtpStorageManager only **/
private void setName(String name) {
@@ -278,7 +286,7 @@
}
private MtpObject copy(boolean recursive) {
- MtpObject copy = new MtpObject(mName, mId, mParent, mIsDir);
+ MtpObject copy = new MtpObject(mName, mId, mStorage, mParent, mIsDir);
copy.mIsDir = mIsDir;
copy.mVisited = mVisited;
copy.mState = mState;
@@ -408,7 +416,7 @@
public synchronized MtpStorage addMtpStorage(StorageVolume volume) {
int storageId = ((getNextStorageId() & 0x0000FFFF) << 16) + 1;
MtpStorage storage = new MtpStorage(volume, storageId);
- MtpObject root = new MtpObject(storage.getPath(), storageId, null, true);
+ MtpObject root = new MtpObject(storage.getPath(), storageId, storage, null, true);
mRoots.put(storageId, root);
return storage;
}
@@ -608,7 +616,7 @@
return null;
}
- MtpObject obj = new MtpObject(newName, getNextObjectId(), parent, isDir);
+ MtpObject obj = new MtpObject(newName, getNextObjectId(), parent.mStorage, parent, isDir);
mObjects.put(obj.getId(), obj);
parent.addChild(obj);
return obj;