Loading core/java/android/provider/MediaStore.java +7 −0 Original line number Diff line number Diff line Loading @@ -345,6 +345,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> Loading core/res/res/values/config.xml +17 −0 Original line number Diff line number Diff line Loading @@ -114,6 +114,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 Loading media/java/android/mtp/MtpDatabase.java +48 −30 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 }; Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -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; } Loading @@ -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(); } Loading Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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) { Loading media/java/android/mtp/MtpPropertyGroup.java +4 −7 Original line number Diff line number Diff line Loading @@ -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: Loading Loading @@ -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: Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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); Loading media/java/android/mtp/MtpServer.java +11 −9 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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
core/java/android/provider/MediaStore.java +7 −0 Original line number Diff line number Diff line Loading @@ -345,6 +345,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> Loading
core/res/res/values/config.xml +17 −0 Original line number Diff line number Diff line Loading @@ -114,6 +114,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 Loading
media/java/android/mtp/MtpDatabase.java +48 −30 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 }; Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -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; } Loading @@ -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(); } Loading Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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) { Loading
media/java/android/mtp/MtpPropertyGroup.java +4 −7 Original line number Diff line number Diff line Loading @@ -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: Loading Loading @@ -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: Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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); Loading
media/java/android/mtp/MtpServer.java +11 −9 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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); }