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

Commit 7ae938be authored by Mike Lockwood's avatar Mike Lockwood
Browse files

DO NOT MERGE MTP and media provider support for multiple storage devices:



- MTP support for multiple storage units

- Add storage_id column to media database for MTP storage ID

- Add framework resource for defining mount points and user visible descriptions
for multiple volumes

- Clean up locking in MtpServer JNI code

Change-Id: Ide6d47bd9aa1698ed2a13d695613e03f2a9b29e3
Signed-off-by: default avatarMike Lockwood <lockwood@android.com>
parent 91dd02c3
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -343,6 +343,13 @@ public final class MediaStore {
         * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
         */
        public interface FileColumns extends MediaColumns {
            /**
             * The MTP storage ID of the file
             * <P>Type: INTEGER</P>
             * @hide
             */
            public static final String STORAGE_ID = "storage_id";

            /**
             * The MTP format code of the file
             * <P>Type: INTEGER</P>
+17 −0
Original line number Diff line number Diff line
@@ -109,6 +109,23 @@
         removable. -->
    <bool name="config_externalStorageRemovable" product="default">true</bool>

    <!-- List of mount points for external storage devices.
         The first item on the list should be the primary external storage and should match the
         value returned by Environment.getExternalStorageDirectory (/mnt/sdcard).
         MTP storage IDs will be generated based on the position of the mountpoint in this list:
            0x00010001 - ID for primary external storage (/mnt/sdcard)
            0x00020001 - ID for first secondary external storage
            0x00030001 - ID for second secondary external storage
         etc. -->
    <string-array translatable="false" name="config_externalStoragePaths">
        <item>"/mnt/sdcard"</item>
    </string-array>

    <!-- User visible descriptions of the volumes in the config_externalStoragePaths array. -->
    <string-array translatable="true" name="config_externalStorageDescriptions">
        <item>"SD card"</item>
    </string-array>

    <!-- Number of megabytes of space to leave unallocated by MTP.
         MTP will subtract this value from the free space it reports back
         to the host via GetStorageInfo, and will not allow new files to
+48 −30
Original line number Diff line number Diff line
@@ -50,7 +50,8 @@ public class MtpDatabase {
    private final IContentProvider mMediaProvider;
    private final String mVolumeName;
    private final Uri mObjectsUri;
    private final String mMediaStoragePath;
    private final String mMediaStoragePath; // path to primary storage
    private final HashMap<String, MtpStorage> mStorageMap = new HashMap<String, MtpStorage>();

    // cached property groups for single properties
    private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty
@@ -67,9 +68,6 @@ public class MtpDatabase {
    private SharedPreferences mDeviceProperties;
    private static final int DEVICE_PROPERTIES_DATABASE_VERSION = 1;

    // FIXME - this should be passed in via the constructor
    private final int mStorageID = 0x00010001;

    private static final String[] ID_PROJECTION = new String[] {
            Files.FileColumns._ID, // 0
    };
@@ -85,17 +83,22 @@ public class MtpDatabase {
    };
    private static final String[] OBJECT_INFO_PROJECTION = new String[] {
            Files.FileColumns._ID, // 0
            Files.FileColumns.DATA, // 1
            Files.FileColumns.STORAGE_ID, // 1
            Files.FileColumns.FORMAT, // 2
            Files.FileColumns.PARENT, // 3
            Files.FileColumns.SIZE, // 4
            Files.FileColumns.DATE_MODIFIED, // 5
            Files.FileColumns.DATA, // 4
            Files.FileColumns.SIZE, // 5
            Files.FileColumns.DATE_MODIFIED, // 6
    };
    private static final String ID_WHERE = Files.FileColumns._ID + "=?";
    private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
    private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
    private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
                                            + Files.FileColumns.FORMAT + "=?";
    private static final String PARENT_STORAGE_WHERE = PARENT_WHERE + " AND "
                                            + Files.FileColumns.STORAGE_ID + "=?";
    private static final String PARENT_STORAGE_FORMAT_WHERE = PARENT_STORAGE_WHERE + " AND "
                                            + Files.FileColumns.FORMAT + "=?";

    private final MediaScanner mMediaScanner;

@@ -124,6 +127,14 @@ public class MtpDatabase {
        }
    }

    public void addStorage(MtpStorage storage) {
        mStorageMap.put(storage.getPath(), storage);
    }

    public void removeStorage(MtpStorage storage) {
        mStorageMap.remove(storage.getPath());
    }

    private void initDeviceProperties(Context context) {
        final String devicePropertiesName = "device-properties";
        mDeviceProperties = context.getSharedPreferences(devicePropertiesName, Context.MODE_PRIVATE);
@@ -160,7 +171,7 @@ public class MtpDatabase {
    }

    private int beginSendObject(String path, int format, int parent,
                         int storage, long size, long modified) {
                         int storageId, long size, long modified) {
        // first make sure the object does not exist
        if (path != null) {
            Cursor c = null;
@@ -185,7 +196,7 @@ public class MtpDatabase {
        values.put(Files.FileColumns.DATA, path);
        values.put(Files.FileColumns.FORMAT, format);
        values.put(Files.FileColumns.PARENT, parent);
        // storage is ignored for now
        values.put(Files.FileColumns.STORAGE_ID, storageId);
        values.put(Files.FileColumns.SIZE, size);
        values.put(Files.FileColumns.DATE_MODIFIED, modified);

@@ -237,19 +248,35 @@ public class MtpDatabase {
        }
    }

    private int[] getObjectList(int storageID, int format, int parent) {
        // we can ignore storageID until we support multiple storages
        Cursor c = null;
        try {
    private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
        if (storageID != 0) {
            if (format != 0) {
                return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                        PARENT_STORAGE_FORMAT_WHERE,
                        new String[] { Integer.toString(parent), Integer.toString(storageID),
                                Integer.toString(format) }, null);
            } else {
                return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                        PARENT_STORAGE_WHERE, new String[]
                                { Integer.toString(parent), Integer.toString(storageID) }, null);
            }
        } else {
            if (format != 0) {
                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                            PARENT_FORMAT_WHERE,
                            new String[] { Integer.toString(parent), Integer.toString(format) },
                             null);
            } else {
                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                            PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
            }
        }
    }

    private int[] getObjectList(int storageID, int format, int parent) {
        Cursor c = null;
        try {
            c = createObjectQuery(storageID, format, parent);
            if (c == null) {
                return null;
            }
@@ -273,18 +300,9 @@ public class MtpDatabase {
    }

    private int getNumObjects(int storageID, int format, int parent) {
        // we can ignore storageID until we support multiple storages
        Cursor c = null;
        try {
            if (format != 0) {
                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                            PARENT_FORMAT_WHERE,
                            new String[] { Integer.toString(parent), Integer.toString(format) },
                             null);
            } else {
                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                            PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
            }
            c = createObjectQuery(storageID, format, parent);
            if (c != null) {
                return c.getCount();
            }
@@ -508,7 +526,7 @@ public class MtpDatabase {
            }
        }

        return propertyGroup.getPropertyList((int)handle, format, depth, mStorageID);
        return propertyGroup.getPropertyList((int)handle, format, depth);
    }

    private int renameFile(int handle, String newName) {
@@ -631,12 +649,12 @@ public class MtpDatabase {
            c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
                            ID_WHERE, new String[] {  Integer.toString(handle) }, null);
            if (c != null && c.moveToNext()) {
                outStorageFormatParent[0] = mStorageID;
                outStorageFormatParent[0] = c.getInt(1);
                outStorageFormatParent[1] = c.getInt(2);
                outStorageFormatParent[2] = c.getInt(3);

                // extract name from path
                String path = c.getString(1);
                String path = c.getString(4);
                int lastSlash = path.lastIndexOf('/');
                int start = (lastSlash >= 0 ? lastSlash + 1 : 0);
                int end = path.length();
@@ -646,8 +664,8 @@ public class MtpDatabase {
                path.getChars(start, end, outName, 0);
                outName[end - start] = 0;

                outSizeModified[0] = c.getLong(4);
                outSizeModified[1] = c.getLong(5);
                outSizeModified[0] = c.getLong(5);
                outSizeModified[1] = c.getLong(6);
                return true;
            }
        } catch (RemoteException e) {
+4 −7
Original line number Diff line number Diff line
@@ -93,7 +93,7 @@ class MtpPropertyGroup {

         switch (code) {
            case MtpConstants.PROPERTY_STORAGE_ID:
                // no query needed until we support multiple storage units
                column = Files.FileColumns.STORAGE_ID;
                type = MtpConstants.TYPE_UINT32;
                break;
             case MtpConstants.PROPERTY_OBJECT_FORMAT:
@@ -134,6 +134,7 @@ class MtpPropertyGroup {
                break;
            case MtpConstants.PROPERTY_PERSISTENT_UID:
                // PUID is concatenation of storageID and object handle
                column = Files.FileColumns.STORAGE_ID;
                type = MtpConstants.TYPE_UINT128;
                break;
            case MtpConstants.PROPERTY_DURATION:
@@ -280,7 +281,7 @@ class MtpPropertyGroup {
        return path.substring(start, end);
    }

    MtpPropertyList getPropertyList(int handle, int format, int depth, int storageID) {
    MtpPropertyList getPropertyList(int handle, int format, int depth) {
        //Log.d(TAG, "getPropertyList handle: " + handle + " format: " + format + " depth: " + depth);
        if (depth > 1) {
            // we only support depth 0 and 1
@@ -348,10 +349,6 @@ class MtpPropertyGroup {

                    // handle some special cases
                    switch (propertyCode) {
                        case MtpConstants.PROPERTY_STORAGE_ID:
                            result.append(handle, propertyCode, MtpConstants.TYPE_UINT32,
                                    storageID);
                            break;
                        case MtpConstants.PROPERTY_PROTECTION_STATUS:
                            // protection status is always 0
                            result.append(handle, propertyCode, MtpConstants.TYPE_UINT16, 0);
@@ -398,7 +395,7 @@ class MtpPropertyGroup {
                            break;
                        case MtpConstants.PROPERTY_PERSISTENT_UID:
                            // PUID is concatenation of storageID and object handle
                            long puid = storageID;
                            long puid = c.getLong(column);
                            puid <<= 32;
                            puid += handle;
                            result.append(handle, propertyCode, MtpConstants.TYPE_UINT128, puid);
+11 −9
Original line number Diff line number Diff line
@@ -33,8 +33,8 @@ public class MtpServer {
        System.loadLibrary("media_jni");
    }

    public MtpServer(MtpDatabase database, String storagePath, long reserveSpace) {
        native_setup(database, storagePath, reserveSpace);
    public MtpServer(MtpDatabase database) {
        native_setup(database);
    }

    public void start() {
@@ -65,18 +65,20 @@ public class MtpServer {
        native_set_ptp_mode(usePtp);
    }

    // Used to disable MTP by removing all storage units.
    // This is done to disable access to file transfer when the device is locked.
    public void setLocked(boolean locked) {
        native_set_locked(locked);
    public void addStorage(MtpStorage storage) {
        native_add_storage(storage);
    }

    private native final void native_setup(MtpDatabase database, String storagePath,
            long reserveSpace);
    public void removeStorage(MtpStorage storage) {
        native_remove_storage(storage.getStorageId());
    }

    private native final void native_setup(MtpDatabase database);
    private native final void native_start();
    private native final void native_stop();
    private native final void native_send_object_added(int handle);
    private native final void native_send_object_removed(int handle);
    private native final void native_set_ptp_mode(boolean usePtp);
    private native final void native_set_locked(boolean locked);
    private native final void native_add_storage(MtpStorage storage);
    private native final void native_remove_storage(int storageId);
}
Loading