diff options
| author | Mike Lockwood <lockwood@android.com> | 2010-09-26 12:35:51 -0400 |
|---|---|---|
| committer | Mike Lockwood <lockwood@android.com> | 2010-09-27 13:16:53 -0400 |
| commit | ae078f7dacdc719d045c2d19bbce019599fec64e (patch) | |
| tree | b3dd3061c7e3822621c92cbec055ec03678e0e61 /media | |
| parent | bdb05df757847ebf343ad332f319a97f7482957c (diff) | |
| download | frameworks_base-ae078f7dacdc719d045c2d19bbce019599fec64e.zip frameworks_base-ae078f7dacdc719d045c2d19bbce019599fec64e.tar.gz frameworks_base-ae078f7dacdc719d045c2d19bbce019599fec64e.tar.bz2 | |
MTP: Implement extra object properties for audio, video and image files
Read-only support at this point.
BUG: 2869730
Change-Id: I424ba760c8f5f4af394bd65276f19438fa6da6cb
Signed-off-by: Mike Lockwood <lockwood@android.com>
Diffstat (limited to 'media')
| -rw-r--r-- | media/java/android/media/MtpDatabase.java | 303 | ||||
| -rw-r--r-- | media/jni/android_media_MtpDatabase.cpp | 33 |
2 files changed, 290 insertions, 46 deletions
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java index 403ed58..630d7112e 100644 --- a/media/java/android/media/MtpDatabase.java +++ b/media/java/android/media/MtpDatabase.java @@ -25,8 +25,9 @@ import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.os.RemoteException; import android.provider.MediaStore.Audio; -import android.provider.MediaStore.MediaColumns; import android.provider.MediaStore.Files; +import android.provider.MediaStore.Images; +import android.provider.MediaStore.MediaColumns; import android.provider.Mtp; import android.util.Log; @@ -278,8 +279,9 @@ public class MtpDatabase { return null; } - private int[] getSupportedObjectProperties(int handle) { - return new int[] { + static final int[] FILE_PROPERTIES = { + // NOTE must match beginning of AUDIO_PROPERTIES, VIDEO_PROPERTIES + // and IMAGE_PROPERTIES below MtpConstants.PROPERTY_STORAGE_ID, MtpConstants.PROPERTY_OBJECT_FORMAT, MtpConstants.PROPERTY_PROTECTION_STATUS, @@ -289,7 +291,93 @@ public class MtpDatabase { MtpConstants.PROPERTY_PARENT_OBJECT, MtpConstants.PROPERTY_PERSISTENT_UID, MtpConstants.PROPERTY_NAME, - }; + MtpConstants.PROPERTY_DATE_ADDED, + }; + + static final int[] AUDIO_PROPERTIES = { + // NOTE must match FILE_PROPERTIES above + MtpConstants.PROPERTY_STORAGE_ID, + MtpConstants.PROPERTY_OBJECT_FORMAT, + MtpConstants.PROPERTY_PROTECTION_STATUS, + MtpConstants.PROPERTY_OBJECT_SIZE, + MtpConstants.PROPERTY_OBJECT_FILE_NAME, + MtpConstants.PROPERTY_DATE_MODIFIED, + MtpConstants.PROPERTY_PARENT_OBJECT, + MtpConstants.PROPERTY_PERSISTENT_UID, + MtpConstants.PROPERTY_NAME, + MtpConstants.PROPERTY_DISPLAY_NAME, + MtpConstants.PROPERTY_DATE_ADDED, + + // audio specific properties + MtpConstants.PROPERTY_ARTIST, + MtpConstants.PROPERTY_ALBUM_NAME, + MtpConstants.PROPERTY_ALBUM_ARTIST, + MtpConstants.PROPERTY_TRACK, + MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE, + MtpConstants.PROPERTY_DURATION, + MtpConstants.PROPERTY_GENRE, + MtpConstants.PROPERTY_COMPOSER, + }; + + static final int[] VIDEO_PROPERTIES = { + // NOTE must match FILE_PROPERTIES above + MtpConstants.PROPERTY_STORAGE_ID, + MtpConstants.PROPERTY_OBJECT_FORMAT, + MtpConstants.PROPERTY_PROTECTION_STATUS, + MtpConstants.PROPERTY_OBJECT_SIZE, + MtpConstants.PROPERTY_OBJECT_FILE_NAME, + MtpConstants.PROPERTY_DATE_MODIFIED, + MtpConstants.PROPERTY_PARENT_OBJECT, + MtpConstants.PROPERTY_PERSISTENT_UID, + MtpConstants.PROPERTY_NAME, + MtpConstants.PROPERTY_DISPLAY_NAME, + MtpConstants.PROPERTY_DATE_ADDED, + + // video specific properties + MtpConstants.PROPERTY_ARTIST, + MtpConstants.PROPERTY_ALBUM_NAME, + MtpConstants.PROPERTY_DURATION, + MtpConstants.PROPERTY_DESCRIPTION, + }; + + static final int[] IMAGE_PROPERTIES = { + // NOTE must match FILE_PROPERTIES above + MtpConstants.PROPERTY_STORAGE_ID, + MtpConstants.PROPERTY_OBJECT_FORMAT, + MtpConstants.PROPERTY_PROTECTION_STATUS, + MtpConstants.PROPERTY_OBJECT_SIZE, + MtpConstants.PROPERTY_OBJECT_FILE_NAME, + MtpConstants.PROPERTY_DATE_MODIFIED, + MtpConstants.PROPERTY_PARENT_OBJECT, + MtpConstants.PROPERTY_PERSISTENT_UID, + MtpConstants.PROPERTY_NAME, + MtpConstants.PROPERTY_DISPLAY_NAME, + MtpConstants.PROPERTY_DATE_ADDED, + + // image specific properties + MtpConstants.PROPERTY_DESCRIPTION, + }; + + private int[] getSupportedObjectProperties(int format) { + switch (format) { + case MtpConstants.FORMAT_MP3: + case MtpConstants.FORMAT_WAV: + case MtpConstants.FORMAT_WMA: + case MtpConstants.FORMAT_OGG: + case MtpConstants.FORMAT_AAC: + return AUDIO_PROPERTIES; + case MtpConstants.FORMAT_MPEG: + case MtpConstants.FORMAT_3GP_CONTAINER: + case MtpConstants.FORMAT_WMV: + return VIDEO_PROPERTIES; + case MtpConstants.FORMAT_EXIF_JPEG: + case MtpConstants.FORMAT_GIF: + case MtpConstants.FORMAT_PNG: + case MtpConstants.FORMAT_BMP: + return IMAGE_PROPERTIES; + default: + return FILE_PROPERTIES; + } } private int[] getSupportedDeviceProperties() { @@ -299,17 +387,90 @@ public class MtpDatabase { }; } + private String queryString(int id, String column) { + Cursor c = null; + try { + // for now we are only reading properties from the "objects" table + c = mMediaProvider.query(mObjectsUri, + new String [] { Files.FileColumns._ID, column }, + ID_WHERE, new String[] { Integer.toString(id) }, null); + if (c != null && c.moveToNext()) { + return c.getString(1); + } else { + return ""; + } + } catch (Exception e) { + return null; + } finally { + if (c != null) { + c.close(); + } + } + } + + private String queryGenre(int id) { + Cursor c = null; + try { + Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id); + c = mMediaProvider.query(uri, + new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME }, + null, null, null); + if (c != null && c.moveToNext()) { + return c.getString(1); + } else { + return ""; + } + } catch (Exception e) { + Log.e(TAG, "queryGenre exception", e); + return null; + } finally { + if (c != null) { + c.close(); + } + } + } + + private boolean queryInt(int id, String column, long[] outValue) { + Cursor c = null; + try { + // for now we are only reading properties from the "objects" table + c = mMediaProvider.query(mObjectsUri, + new String [] { Files.FileColumns._ID, column }, + ID_WHERE, new String[] { Integer.toString(id) }, null); + if (c != null && c.moveToNext()) { + outValue[0] = c.getLong(1); + return true; + } + return false; + } catch (Exception e) { + return false; + } finally { + if (c != null) { + c.close(); + } + } + } + + private String nameFromPath(String path) { + // extract name from full path + int start = 0; + int lastSlash = path.lastIndexOf('/'); + if (lastSlash >= 0) { + start = lastSlash + 1; + } + int end = path.length(); + if (end - start > 255) { + end = start + 255; + } + return path.substring(start, end); + } + private int getObjectProperty(int handle, int property, long[] outIntValue, char[] outStringValue) { Log.d(TAG, "getObjectProperty: " + property); String column = null; boolean isString = false; - // temporary hack - if (property == MtpConstants.PROPERTY_NAME) { - property = MtpConstants.PROPERTY_OBJECT_FILE_NAME; - } - switch (property) { case MtpConstants.PROPERTY_STORAGE_ID: outIntValue[0] = mStorageID; @@ -325,12 +486,46 @@ public class MtpDatabase { column = Files.FileColumns.SIZE; break; case MtpConstants.PROPERTY_OBJECT_FILE_NAME: - column = Files.FileColumns.DATA; - isString = true; - break; + // special case - need to extract file name from full path + String value = queryString(handle, Files.FileColumns.DATA); + if (value != null) { + value = nameFromPath(value); + value.getChars(0, value.length(), outStringValue, 0); + outStringValue[value.length()] = 0; + return MtpConstants.RESPONSE_OK; + } else { + return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; + } + case MtpConstants.PROPERTY_NAME: + // first try title + String name = queryString(handle, MediaColumns.TITLE); + // then try name + if (name == null) { + name = queryString(handle, Audio.PlaylistsColumns.NAME); + } + // if title and name fail, extract name from full path + if (name == null) { + name = queryString(handle, Files.FileColumns.DATA); + if (name != null) { + name = nameFromPath(name); + } + } + if (name != null) { + name.getChars(0, name.length(), outStringValue, 0); + outStringValue[name.length()] = 0; + return MtpConstants.RESPONSE_OK; + } else { + return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; + } case MtpConstants.PROPERTY_DATE_MODIFIED: column = Files.FileColumns.DATE_MODIFIED; break; + case MtpConstants.PROPERTY_DATE_ADDED: + column = Files.FileColumns.DATE_ADDED; + break; + case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE: + column = Audio.AudioColumns.YEAR; + break; case MtpConstants.PROPERTY_PARENT_OBJECT: column = Files.FileColumns.PARENT; break; @@ -341,44 +536,64 @@ public class MtpDatabase { puid += handle; outIntValue[0] = puid; return MtpConstants.RESPONSE_OK; + case MtpConstants.PROPERTY_DURATION: + column = Audio.AudioColumns.DURATION; + break; + case MtpConstants.PROPERTY_TRACK: + if (queryInt(handle, Audio.AudioColumns.TRACK, outIntValue)) { + // track is stored in lower 3 decimal digits + outIntValue[0] %= 1000; + return MtpConstants.RESPONSE_OK; + } else { + return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; + } + case MtpConstants.PROPERTY_DISPLAY_NAME: + column = MediaColumns.DISPLAY_NAME; + isString = true; + break; + case MtpConstants.PROPERTY_ARTIST: + column = Audio.AudioColumns.ARTIST; + isString = true; + break; + case MtpConstants.PROPERTY_ALBUM_NAME: + column = Audio.AudioColumns.ALBUM; + isString = true; + break; + case MtpConstants.PROPERTY_ALBUM_ARTIST: + column = Audio.AudioColumns.ALBUM_ARTIST; + isString = true; + break; + case MtpConstants.PROPERTY_GENRE: + String genre = queryGenre(handle); + if (genre != null) { + genre.getChars(0, genre.length(), outStringValue, 0); + outStringValue[genre.length()] = 0; + return MtpConstants.RESPONSE_OK; + } else { + return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; + } + case MtpConstants.PROPERTY_COMPOSER: + column = Audio.AudioColumns.COMPOSER; + isString = true; + break; + case MtpConstants.PROPERTY_DESCRIPTION: + column = Images.ImageColumns.DESCRIPTION; + isString = true; + break; default: return MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED; } - Cursor c = null; - try { - // for now we are only reading properties from the "objects" table - c = mMediaProvider.query(mObjectsUri, - new String [] { Files.FileColumns._ID, column }, - ID_WHERE, new String[] { Integer.toString(handle) }, null); - if (c != null && c.moveToNext()) { - if (isString) { - String value = c.getString(1); - int start = 0; - - if (property == MtpConstants.PROPERTY_OBJECT_FILE_NAME) { - // extract name from full path - int lastSlash = value.lastIndexOf('/'); - if (lastSlash >= 0) { - start = lastSlash + 1; - } - } - int end = value.length(); - if (end - start > 255) { - end = start + 255; - } - value.getChars(start, end, outStringValue, 0); - outStringValue[end - start] = 0; - } else { - outIntValue[0] = c.getLong(1); - } + if (isString) { + String value = queryString(handle, column); + if (value != null) { + value.getChars(0, value.length(), outStringValue, 0); + outStringValue[value.length()] = 0; return MtpConstants.RESPONSE_OK; } - } catch (Exception e) { - return MtpConstants.RESPONSE_GENERAL_ERROR; - } finally { - if (c != null) { - c.close(); + } else { + if (queryInt(handle, column, outIntValue)) { + return MtpConstants.RESPONSE_OK; } } // query failed if we get here diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp index b5f4856..10ad29d 100644 --- a/media/jni/android_media_MtpDatabase.cpp +++ b/media/jni/android_media_MtpDatabase.cpp @@ -342,14 +342,21 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle, jlong longValue = longValues[0]; env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); - // special case MTP_PROPERTY_DATE_MODIFIED, which is a string to MTP + // special case date properties, which are strings to MTP // but stored internally as a uint64 - if (property == MTP_PROPERTY_DATE_MODIFIED) { + if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) { char date[20]; formatDateTime(longValue, date, sizeof(date)); packet.putString(date); return MTP_RESPONSE_OK; } + // release date is stored internally as just the year + if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) { + char date[20]; + snprintf(date, sizeof(date), "%04lld0101T000000", longValue); + packet.putString(date); + return MTP_RESPONSE_OK; + } switch (type) { case MTP_TYPE_INT8: @@ -680,6 +687,17 @@ static const PropertyTableEntry kObjectPropertyTable[] = { { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 }, { MTP_PROPERTY_PERSISTENT_UID, MTP_TYPE_UINT128 }, { MTP_PROPERTY_NAME, MTP_TYPE_STR }, + { MTP_PROPERTY_DISPLAY_NAME, MTP_TYPE_STR }, + { MTP_PROPERTY_DATE_ADDED, MTP_TYPE_STR }, + { MTP_PROPERTY_ARTIST, MTP_TYPE_STR }, + { MTP_PROPERTY_ALBUM_NAME, MTP_TYPE_STR }, + { MTP_PROPERTY_ALBUM_ARTIST, MTP_TYPE_STR }, + { MTP_PROPERTY_TRACK, MTP_TYPE_UINT16 }, + { MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR }, + { MTP_PROPERTY_GENRE, MTP_TYPE_STR }, + { MTP_PROPERTY_COMPOSER, MTP_TYPE_STR }, + { MTP_PROPERTY_DURATION, MTP_TYPE_UINT32 }, + { MTP_PROPERTY_DESCRIPTION, MTP_TYPE_STR }, }; static const PropertyTableEntry kDevicePropertyTable[] = { @@ -754,10 +772,12 @@ MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property, switch (property) { case MTP_PROPERTY_OBJECT_FORMAT: case MTP_PROPERTY_PROTECTION_STATUS: + case MTP_PROPERTY_TRACK: result = new MtpProperty(property, MTP_TYPE_UINT16); break; case MTP_PROPERTY_STORAGE_ID: case MTP_PROPERTY_PARENT_OBJECT: + case MTP_PROPERTY_DURATION: result = new MtpProperty(property, MTP_TYPE_UINT32); break; case MTP_PROPERTY_OBJECT_SIZE: @@ -769,6 +789,15 @@ MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property, case MTP_PROPERTY_NAME: case MTP_PROPERTY_OBJECT_FILE_NAME: case MTP_PROPERTY_DATE_MODIFIED: + case MTP_PROPERTY_DISPLAY_NAME: + case MTP_PROPERTY_DATE_ADDED: + case MTP_PROPERTY_ARTIST: + case MTP_PROPERTY_ALBUM_NAME: + case MTP_PROPERTY_ALBUM_ARTIST: + case MTP_PROPERTY_ORIGINAL_RELEASE_DATE: + case MTP_PROPERTY_GENRE: + case MTP_PROPERTY_COMPOSER: + case MTP_PROPERTY_DESCRIPTION: result = new MtpProperty(property, MTP_TYPE_STR); break; } |
