Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 383d3167 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Adjust MTP to reference by specific volume name." into qt-dev

parents 29ddcbf1 42bf6d8a
Loading
Loading
Loading
Loading
+28 −29
Original line number Diff line number Diff line
@@ -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();
+5 −10
Original line number Diff line number Diff line
@@ -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();
+11 −1
Original line number Diff line number Diff line
@@ -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;
    }
}
+12 −4
Original line number Diff line number Diff line
@@ -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;