Merge "Adjust MTP to reference by specific volume name." into qt-dev
am: 383d316707
Change-Id: I0784ca10c6ea717b58f8ee6fc437dee0c24a7a9d
This commit is contained in:
@@ -36,6 +36,7 @@ import android.system.ErrnoException;
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
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 class MtpDatabase implements AutoCloseable {
|
||||
}
|
||||
};
|
||||
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
}
|
||||
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
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 @@ public class MtpDatabase implements AutoCloseable {
|
||||
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();
|
||||
|
||||
@@ -46,9 +46,6 @@ class MtpPropertyGroup {
|
||||
}
|
||||
}
|
||||
|
||||
private final ContentProviderClient mProvider;
|
||||
private final String mVolumeName;
|
||||
|
||||
// list of all properties in this group
|
||||
private final Property[] mProperties;
|
||||
|
||||
@@ -58,10 +55,7 @@ class MtpPropertyGroup {
|
||||
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 @@ class MtpPropertyGroup {
|
||||
* 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 @@ class MtpPropertyGroup {
|
||||
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();
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.mtp;
|
||||
|
||||
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 @@ import android.os.storage.StorageVolume;
|
||||
* @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 @@ public class MtpStorage {
|
||||
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 class MtpStorage {
|
||||
public long getMaxFileSize() {
|
||||
return mMaxFileSize;
|
||||
}
|
||||
|
||||
public String getVolumeName() {
|
||||
return mVolumeName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.os.FileObserver;
|
||||
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 @@ public class MtpStorageManager {
|
||||
|
||||
/** 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 @@ public class MtpStorageManager {
|
||||
// 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 @@ public class MtpStorageManager {
|
||||
return mParent == null;
|
||||
}
|
||||
|
||||
public String getVolumeName() {
|
||||
return mStorage.getVolumeName();
|
||||
}
|
||||
|
||||
/** For MtpStorageManager only **/
|
||||
|
||||
private void setName(String name) {
|
||||
@@ -278,7 +286,7 @@ public class MtpStorageManager {
|
||||
}
|
||||
|
||||
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 class MtpStorageManager {
|
||||
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 @@ public class MtpStorageManager {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user