diff options
author | 2018-08-27 18:03:33 -0600 | |
---|---|---|
committer | 2018-08-28 13:02:35 -0600 | |
commit | 91e3cd4c6c1b00f606966a3af2abc0ef6f331bb0 (patch) | |
tree | e904980a97f58618b277256dde37f2d3e824657e | |
parent | bfbdd1c0b912e3c2df027e4030931e1e2c070270 (diff) |
More robust MTP-to-MIME-type mappings.
Recently in I830717428e72ac37c5ecd1f23d915aa878ef3744, we greatly
improved the underlying file-extension-to-MIME-type mappings defined
in libcore and used across the OS.
Instead of maintaining divergent mappings here in MediaFile, this
change delegates all file extension logic down to libcore, and
standardizes all MediaScanner internals on using MIME types. To
register new file types in the future:
1. Add the MIME-to-extension registration in libcore.
2. Add the MIME-to-MTP mapping here in MediaFile.
This change also ensures that unknown MIME types are surfaced
across MTP, using constants like FORMAT_UNDEFINED_AUDIO for audio/*
until an explicit format is defined.
We now surface WMA/WMV file formats, even if the device can't
natively play them back, since we still want to offer the ability
for users to copy them around, and the user may have a third-party
app capable of playing them.
Keeps @UnsupportedAppUsage intact for now.
Bug: 111268862, 112162449
Test: atest frameworks/base/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java
Test: atest cts/tests/tests/provider/src/android/provider/cts/MediaStore*
Change-Id: I2f6a5411bc215f776f00e0f9a4b7d825b10b377d
9 files changed, 472 insertions, 378 deletions
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 5867a9c8890e..3bfb1c55fed6 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -437,6 +437,12 @@ public abstract class ContentResolver { */ public static final String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*"; + /** + * Default MIME type for files whose type is otherwise unknown. + * @hide + */ + public static final String MIME_TYPE_DEFAULT = "application/octet-stream"; + /** @hide */ @UnsupportedAppUsage public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1; diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 3a3d9ea0b44f..1c8029caab70 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -23,6 +23,7 @@ import static android.system.OsConstants.S_ISREG; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.ContentResolver; import android.provider.DocumentsContract.Document; import android.system.ErrnoException; import android.system.Os; @@ -1057,7 +1058,7 @@ public class FileUtils { } if (mimeTypeFromExt == null) { - mimeTypeFromExt = "application/octet-stream"; + mimeTypeFromExt = ContentResolver.MIME_TYPE_DEFAULT; } final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType( diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index 0c6f832ff33a..b33a5c4ffb7d 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -74,10 +74,6 @@ public abstract class FileSystemProvider extends DocumentsProvider { private Handler mHandler; - private static final String MIMETYPE_JPEG = "image/jpeg"; - private static final String MIMETYPE_JPG = "image/jpg"; - private static final String MIMETYPE_OCTET_STREAM = "application/octet-stream"; - protected abstract File getFileForDocId(String docId, boolean visible) throws FileNotFoundException; @@ -433,7 +429,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { return mime; } } - return MIMETYPE_OCTET_STREAM; + return ContentResolver.MIME_TYPE_DEFAULT; } } diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 3b9b6dd6d889..710b503d97fd 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -16,115 +16,41 @@ package android.media; +import static android.content.ContentResolver.MIME_TYPE_DEFAULT; + +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; -import android.media.DecoderCapabilities; -import android.media.DecoderCapabilities.VideoDecoder; -import android.media.DecoderCapabilities.AudioDecoder; import android.mtp.MtpConstants; -import com.android.internal.util.Preconditions; + +import libcore.net.MimeUtils; + import java.util.HashMap; -import java.util.List; -import java.util.Locale; /** * MediaScanner helper class. + * <p> + * This heavily relies upon extension to MIME type mappings which are maintained + * in {@link MimeUtils}, to ensure consistency across the OS. + * <p> + * When adding a new file type, first add the MIME type mapping to + * {@link MimeUtils}, and then add the MTP format mapping here. * - * {@hide} + * @hide */ public class MediaFile { - // Audio file types - public static final int FILE_TYPE_MP3 = 1; - public static final int FILE_TYPE_M4A = 2; - public static final int FILE_TYPE_WAV = 3; - public static final int FILE_TYPE_AMR = 4; - public static final int FILE_TYPE_AWB = 5; - public static final int FILE_TYPE_WMA = 6; - public static final int FILE_TYPE_OGG = 7; - public static final int FILE_TYPE_AAC = 8; - public static final int FILE_TYPE_MKA = 9; - public static final int FILE_TYPE_FLAC = 10; + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage - private static final int FIRST_AUDIO_FILE_TYPE = FILE_TYPE_MP3; + private static final int FIRST_AUDIO_FILE_TYPE = 1; + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage - private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_FLAC; - - // MIDI file types - public static final int FILE_TYPE_MID = 11; - public static final int FILE_TYPE_SMF = 12; - public static final int FILE_TYPE_IMY = 13; - private static final int FIRST_MIDI_FILE_TYPE = FILE_TYPE_MID; - private static final int LAST_MIDI_FILE_TYPE = FILE_TYPE_IMY; - - // Video file types - public static final int FILE_TYPE_MP4 = 21; - public static final int FILE_TYPE_M4V = 22; - public static final int FILE_TYPE_3GPP = 23; - public static final int FILE_TYPE_3GPP2 = 24; - public static final int FILE_TYPE_WMV = 25; - public static final int FILE_TYPE_ASF = 26; - public static final int FILE_TYPE_MKV = 27; - public static final int FILE_TYPE_MP2TS = 28; - public static final int FILE_TYPE_AVI = 29; - public static final int FILE_TYPE_WEBM = 30; - private static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4; - private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_WEBM; - - // More video file types - public static final int FILE_TYPE_MP2PS = 200; - public static final int FILE_TYPE_QT = 201; - private static final int FIRST_VIDEO_FILE_TYPE2 = FILE_TYPE_MP2PS; - private static final int LAST_VIDEO_FILE_TYPE2 = FILE_TYPE_QT; - - // Image file types - public static final int FILE_TYPE_JPEG = 31; - public static final int FILE_TYPE_GIF = 32; - public static final int FILE_TYPE_PNG = 33; - public static final int FILE_TYPE_BMP = 34; - public static final int FILE_TYPE_WBMP = 35; - public static final int FILE_TYPE_WEBP = 36; - public static final int FILE_TYPE_HEIF = 37; - private static final int FIRST_IMAGE_FILE_TYPE = FILE_TYPE_JPEG; - private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_HEIF; - - // Raw image file types - public static final int FILE_TYPE_DNG = 300; - public static final int FILE_TYPE_CR2 = 301; - public static final int FILE_TYPE_NEF = 302; - public static final int FILE_TYPE_NRW = 303; - public static final int FILE_TYPE_ARW = 304; - public static final int FILE_TYPE_RW2 = 305; - public static final int FILE_TYPE_ORF = 306; - public static final int FILE_TYPE_RAF = 307; - public static final int FILE_TYPE_PEF = 308; - public static final int FILE_TYPE_SRW = 309; - private static final int FIRST_RAW_IMAGE_FILE_TYPE = FILE_TYPE_DNG; - private static final int LAST_RAW_IMAGE_FILE_TYPE = FILE_TYPE_SRW; - - // Playlist file types - public static final int FILE_TYPE_M3U = 41; - public static final int FILE_TYPE_PLS = 42; - public static final int FILE_TYPE_WPL = 43; - public static final int FILE_TYPE_HTTPLIVE = 44; - - private static final int FIRST_PLAYLIST_FILE_TYPE = FILE_TYPE_M3U; - private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_HTTPLIVE; - - // Drm file types - public static final int FILE_TYPE_FL = 51; - private static final int FIRST_DRM_FILE_TYPE = FILE_TYPE_FL; - private static final int LAST_DRM_FILE_TYPE = FILE_TYPE_FL; - - // Other popular file types - public static final int FILE_TYPE_TEXT = 100; - public static final int FILE_TYPE_HTML = 101; - public static final int FILE_TYPE_PDF = 102; - public static final int FILE_TYPE_XML = 103; - public static final int FILE_TYPE_MS_WORD = 104; - public static final int FILE_TYPE_MS_EXCEL = 105; - public static final int FILE_TYPE_MS_POWERPOINT = 106; - public static final int FILE_TYPE_ZIP = 107; + private static final int LAST_AUDIO_FILE_TYPE = 10; + /** @deprecated file types no longer exist */ + @Deprecated public static class MediaFileType { @UnsupportedAppUsage public final int fileType; @@ -137,214 +63,178 @@ public class MediaFile { } } + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage - private static final HashMap<String, MediaFileType> sFileTypeMap - = new HashMap<String, MediaFileType>(); - private static final HashMap<String, Integer> sMimeTypeMap - = new HashMap<String, Integer>(); - // maps file extension to MTP format code + private static final HashMap<String, MediaFileType> sFileTypeMap = new HashMap<>(); + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage - private static final HashMap<String, Integer> sFileTypeToFormatMap - = new HashMap<String, Integer>(); + private static final HashMap<String, Integer> sFileTypeToFormatMap = new HashMap<>(); + // maps mime type to MTP format code @UnsupportedAppUsage - private static final HashMap<String, Integer> sMimeTypeToFormatMap - = new HashMap<String, Integer>(); + private static final HashMap<String, Integer> sMimeTypeToFormatMap = new HashMap<>(); // maps MTP format code to mime type @UnsupportedAppUsage - private static final HashMap<Integer, String> sFormatToMimeTypeMap - = new HashMap<Integer, String>(); + private static final HashMap<Integer, String> sFormatToMimeTypeMap = new HashMap<>(); + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage static void addFileType(String extension, int fileType, String mimeType) { - sFileTypeMap.put(extension, new MediaFileType(fileType, mimeType)); - sMimeTypeMap.put(mimeType, Integer.valueOf(fileType)); } - private static void addFileType(String extension, int fileType, String mimeType, - int mtpFormatCode, boolean primaryType) { - addFileType(extension, fileType, mimeType); - sFileTypeToFormatMap.put(extension, Integer.valueOf(mtpFormatCode)); - sMimeTypeToFormatMap.put(mimeType, Integer.valueOf(mtpFormatCode)); - if (primaryType) { - Preconditions.checkArgument(!sFormatToMimeTypeMap.containsKey(mtpFormatCode)); - sFormatToMimeTypeMap.put(mtpFormatCode, mimeType); + private static void addFileType(int mtpFormatCode, @NonNull String mimeType) { + if (!sMimeTypeToFormatMap.containsKey(mimeType)) { + sMimeTypeToFormatMap.put(mimeType, Integer.valueOf(mtpFormatCode)); } - } - - private static boolean isWMAEnabled() { - List<AudioDecoder> decoders = DecoderCapabilities.getAudioDecoders(); - int count = decoders.size(); - for (int i = 0; i < count; i++) { - AudioDecoder decoder = decoders.get(i); - if (decoder == AudioDecoder.AUDIO_DECODER_WMA) { - return true; - } - } - return false; - } - - private static boolean isWMVEnabled() { - List<VideoDecoder> decoders = DecoderCapabilities.getVideoDecoders(); - int count = decoders.size(); - for (int i = 0; i < count; i++) { - VideoDecoder decoder = decoders.get(i); - if (decoder == VideoDecoder.VIDEO_DECODER_WMV) { - return true; - } + if (!sFormatToMimeTypeMap.containsKey(mtpFormatCode)) { + sFormatToMimeTypeMap.put(mtpFormatCode, mimeType); } - return false; } static { - addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg", MtpConstants.FORMAT_MP3, true); - addFileType("MPGA", FILE_TYPE_MP3, "audio/mpeg", MtpConstants.FORMAT_MP3, false); - addFileType("M4A", FILE_TYPE_M4A, "audio/mp4", MtpConstants.FORMAT_MPEG, false); - addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav", MtpConstants.FORMAT_WAV, true); - addFileType("AMR", FILE_TYPE_AMR, "audio/amr"); - addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb"); - if (isWMAEnabled()) { - addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma", MtpConstants.FORMAT_WMA, true); - } - addFileType("OGG", FILE_TYPE_OGG, "audio/ogg", MtpConstants.FORMAT_OGG, false); - addFileType("OGG", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG, true); - addFileType("OGA", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG, false); - addFileType("AAC", FILE_TYPE_AAC, "audio/aac", MtpConstants.FORMAT_AAC, true); - addFileType("AAC", FILE_TYPE_AAC, "audio/aac-adts", MtpConstants.FORMAT_AAC, false); - addFileType("MKA", FILE_TYPE_MKA, "audio/x-matroska"); - - addFileType("MID", FILE_TYPE_MID, "audio/midi"); - addFileType("MIDI", FILE_TYPE_MID, "audio/midi"); - addFileType("XMF", FILE_TYPE_MID, "audio/midi"); - addFileType("RTTTL", FILE_TYPE_MID, "audio/midi"); - addFileType("SMF", FILE_TYPE_SMF, "audio/sp-midi"); - addFileType("IMY", FILE_TYPE_IMY, "audio/imelody"); - addFileType("RTX", FILE_TYPE_MID, "audio/midi"); - addFileType("OTA", FILE_TYPE_MID, "audio/midi"); - addFileType("MXMF", FILE_TYPE_MID, "audio/midi"); - - addFileType("MPEG", FILE_TYPE_MP4, "video/mpeg", MtpConstants.FORMAT_MPEG, true); - addFileType("MPG", FILE_TYPE_MP4, "video/mpeg", MtpConstants.FORMAT_MPEG, false); - addFileType("MP4", FILE_TYPE_MP4, "video/mp4", MtpConstants.FORMAT_MPEG, false); - addFileType("M4V", FILE_TYPE_M4V, "video/mp4", MtpConstants.FORMAT_MPEG, false); - addFileType("MOV", FILE_TYPE_QT, "video/quicktime", MtpConstants.FORMAT_MPEG, false); - - addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp", MtpConstants.FORMAT_3GP_CONTAINER, true); - addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp", MtpConstants.FORMAT_3GP_CONTAINER, false); - addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2", MtpConstants.FORMAT_3GP_CONTAINER, false); - addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2", MtpConstants.FORMAT_3GP_CONTAINER, false); - addFileType("MKV", FILE_TYPE_MKV, "video/x-matroska"); - addFileType("WEBM", FILE_TYPE_WEBM, "video/webm"); - addFileType("TS", FILE_TYPE_MP2TS, "video/mp2ts"); - addFileType("AVI", FILE_TYPE_AVI, "video/avi"); - - if (isWMVEnabled()) { - addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv", MtpConstants.FORMAT_WMV, true); - addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf"); - } - - addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg", MtpConstants.FORMAT_EXIF_JPEG, true); - addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg", MtpConstants.FORMAT_EXIF_JPEG, false); - addFileType("GIF", FILE_TYPE_GIF, "image/gif", MtpConstants.FORMAT_GIF, true); - addFileType("PNG", FILE_TYPE_PNG, "image/png", MtpConstants.FORMAT_PNG, true); - addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp", MtpConstants.FORMAT_BMP, true); - addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp", MtpConstants.FORMAT_DEFINED, false); - addFileType("WEBP", FILE_TYPE_WEBP, "image/webp", MtpConstants.FORMAT_DEFINED, false); - addFileType("HEIC", FILE_TYPE_HEIF, "image/heif", MtpConstants.FORMAT_HEIF, true); - addFileType("HEIF", FILE_TYPE_HEIF, "image/heif", MtpConstants.FORMAT_HEIF, false); - - addFileType("DNG", FILE_TYPE_DNG, "image/x-adobe-dng", MtpConstants.FORMAT_DNG, true); - addFileType("CR2", FILE_TYPE_CR2, "image/x-canon-cr2", MtpConstants.FORMAT_TIFF, false); - addFileType("NEF", FILE_TYPE_NEF, "image/x-nikon-nef", MtpConstants.FORMAT_TIFF_EP, false); - addFileType("NRW", FILE_TYPE_NRW, "image/x-nikon-nrw", MtpConstants.FORMAT_TIFF, false); - addFileType("ARW", FILE_TYPE_ARW, "image/x-sony-arw", MtpConstants.FORMAT_TIFF, false); - addFileType("RW2", FILE_TYPE_RW2, "image/x-panasonic-rw2", MtpConstants.FORMAT_TIFF, false); - addFileType("ORF", FILE_TYPE_ORF, "image/x-olympus-orf", MtpConstants.FORMAT_TIFF, false); - addFileType("RAF", FILE_TYPE_RAF, "image/x-fuji-raf", MtpConstants.FORMAT_DEFINED, false); - addFileType("PEF", FILE_TYPE_PEF, "image/x-pentax-pef", MtpConstants.FORMAT_TIFF, false); - addFileType("SRW", FILE_TYPE_SRW, "image/x-samsung-srw", MtpConstants.FORMAT_TIFF, false); - - addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST, true); - addFileType("M3U", FILE_TYPE_M3U, "application/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST, false); - addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls", MtpConstants.FORMAT_PLS_PLAYLIST, true); - addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl", MtpConstants.FORMAT_WPL_PLAYLIST, true); - addFileType("M3U8", FILE_TYPE_HTTPLIVE, "application/vnd.apple.mpegurl"); - addFileType("M3U8", FILE_TYPE_HTTPLIVE, "audio/mpegurl"); - addFileType("M3U8", FILE_TYPE_HTTPLIVE, "audio/x-mpegurl"); - - addFileType("FL", FILE_TYPE_FL, "application/x-android-drm-fl"); - - addFileType("TXT", FILE_TYPE_TEXT, "text/plain", MtpConstants.FORMAT_TEXT, true); - addFileType("HTM", FILE_TYPE_HTML, "text/html", MtpConstants.FORMAT_HTML, true); - addFileType("HTML", FILE_TYPE_HTML, "text/html", MtpConstants.FORMAT_HTML, false); - addFileType("PDF", FILE_TYPE_PDF, "application/pdf"); - addFileType("DOC", FILE_TYPE_MS_WORD, "application/msword", MtpConstants.FORMAT_MS_WORD_DOCUMENT, true); - addFileType("XLS", FILE_TYPE_MS_EXCEL, "application/vnd.ms-excel", MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, true); - addFileType("PPT", FILE_TYPE_MS_POWERPOINT, "application/vnd.ms-powerpoint", MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, true); - addFileType("FLAC", FILE_TYPE_FLAC, "audio/flac", MtpConstants.FORMAT_FLAC, true); - addFileType("ZIP", FILE_TYPE_ZIP, "application/zip"); - addFileType("MPG", FILE_TYPE_MP2PS, "video/mp2p"); - addFileType("MPEG", FILE_TYPE_MP2PS, "video/mp2p"); + addFileType(MtpConstants.FORMAT_MP3, "audio/mpeg"); + addFileType(MtpConstants.FORMAT_WAV, "audio/x-wav"); + addFileType(MtpConstants.FORMAT_WMA, "audio/x-ms-wma"); + addFileType(MtpConstants.FORMAT_OGG, "audio/ogg"); + addFileType(MtpConstants.FORMAT_AAC, "audio/aac"); + addFileType(MtpConstants.FORMAT_FLAC, "audio/flac"); + addFileType(MtpConstants.FORMAT_AIFF, "audio/x-aiff"); + addFileType(MtpConstants.FORMAT_MP2, "audio/mpeg"); + + addFileType(MtpConstants.FORMAT_MPEG, "video/mpeg"); + addFileType(MtpConstants.FORMAT_MP4_CONTAINER, "video/mp4"); + addFileType(MtpConstants.FORMAT_3GP_CONTAINER, "video/3gpp"); + addFileType(MtpConstants.FORMAT_3GP_CONTAINER, "video/3gpp2"); + addFileType(MtpConstants.FORMAT_AVI, "video/avi"); + addFileType(MtpConstants.FORMAT_WMV, "video/x-ms-wmv"); + addFileType(MtpConstants.FORMAT_ASF, "video/x-ms-asf"); + + addFileType(MtpConstants.FORMAT_EXIF_JPEG, "image/jpeg"); + addFileType(MtpConstants.FORMAT_GIF, "image/gif"); + addFileType(MtpConstants.FORMAT_PNG, "image/png"); + addFileType(MtpConstants.FORMAT_BMP, "image/x-ms-bmp"); + addFileType(MtpConstants.FORMAT_HEIF, "image/heif"); + addFileType(MtpConstants.FORMAT_DNG, "image/x-adobe-dng"); + addFileType(MtpConstants.FORMAT_TIFF, "image/tiff"); + addFileType(MtpConstants.FORMAT_TIFF, "image/x-canon-cr2"); + addFileType(MtpConstants.FORMAT_TIFF, "image/x-nikon-nrw"); + addFileType(MtpConstants.FORMAT_TIFF, "image/x-sony-arw"); + addFileType(MtpConstants.FORMAT_TIFF, "image/x-panasonic-rw2"); + addFileType(MtpConstants.FORMAT_TIFF, "image/x-olympus-orf"); + addFileType(MtpConstants.FORMAT_TIFF, "image/x-pentax-pef"); + addFileType(MtpConstants.FORMAT_TIFF, "image/x-samsung-srw"); + addFileType(MtpConstants.FORMAT_TIFF_EP, "image/tiff"); + addFileType(MtpConstants.FORMAT_TIFF_EP, "image/x-nikon-nef"); + addFileType(MtpConstants.FORMAT_JP2, "image/jp2"); + addFileType(MtpConstants.FORMAT_JPX, "image/jpx"); + + addFileType(MtpConstants.FORMAT_M3U_PLAYLIST, "audio/x-mpegurl"); + addFileType(MtpConstants.FORMAT_PLS_PLAYLIST, "audio/x-scpls"); + addFileType(MtpConstants.FORMAT_WPL_PLAYLIST, "application/vnd.ms-wpl"); + addFileType(MtpConstants.FORMAT_ASX_PLAYLIST, "video/x-ms-asf"); + + addFileType(MtpConstants.FORMAT_TEXT, "text/plain"); + addFileType(MtpConstants.FORMAT_HTML, "text/html"); + addFileType(MtpConstants.FORMAT_XML_DOCUMENT, "text/xml"); + + addFileType(MtpConstants.FORMAT_MS_WORD_DOCUMENT, + "application/msword"); + addFileType(MtpConstants.FORMAT_MS_WORD_DOCUMENT, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + addFileType(MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, + "application/vnd.ms-excel"); + addFileType(MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + addFileType(MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, + "application/vnd.ms-powerpoint"); + addFileType(MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, + "application/vnd.openxmlformats-officedocument.presentationml.presentation"); } + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage public static boolean isAudioFileType(int fileType) { - return ((fileType >= FIRST_AUDIO_FILE_TYPE && - fileType <= LAST_AUDIO_FILE_TYPE) || - (fileType >= FIRST_MIDI_FILE_TYPE && - fileType <= LAST_MIDI_FILE_TYPE)); + return false; } + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage public static boolean isVideoFileType(int fileType) { - return (fileType >= FIRST_VIDEO_FILE_TYPE && - fileType <= LAST_VIDEO_FILE_TYPE) - || (fileType >= FIRST_VIDEO_FILE_TYPE2 && - fileType <= LAST_VIDEO_FILE_TYPE2); + return false; } + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage public static boolean isImageFileType(int fileType) { - return (fileType >= FIRST_IMAGE_FILE_TYPE && - fileType <= LAST_IMAGE_FILE_TYPE) - || (fileType >= FIRST_RAW_IMAGE_FILE_TYPE && - fileType <= LAST_RAW_IMAGE_FILE_TYPE); - } - - public static boolean isRawImageFileType(int fileType) { - return (fileType >= FIRST_RAW_IMAGE_FILE_TYPE && - fileType <= LAST_RAW_IMAGE_FILE_TYPE); + return false; } + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage public static boolean isPlayListFileType(int fileType) { - return (fileType >= FIRST_PLAYLIST_FILE_TYPE && - fileType <= LAST_PLAYLIST_FILE_TYPE); + return false; } + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage public static boolean isDrmFileType(int fileType) { - return (fileType >= FIRST_DRM_FILE_TYPE && - fileType <= LAST_DRM_FILE_TYPE); + return false; } + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage public static MediaFileType getFileType(String path) { - int lastDot = path.lastIndexOf('.'); - if (lastDot < 0) - return null; - return sFileTypeMap.get(path.substring(lastDot + 1).toUpperCase(Locale.ROOT)); + return null; + } + + public static boolean isExifMimeType(@Nullable String mimeType) { + // For simplicity, assume that all image files might have EXIF data + return isImageMimeType(mimeType); + } + + public static boolean isAudioMimeType(@Nullable String mimeType) { + return normalizeMimeType(mimeType).startsWith("audio/"); + } + + public static boolean isVideoMimeType(@Nullable String mimeType) { + return normalizeMimeType(mimeType).startsWith("video/"); + } + + public static boolean isImageMimeType(@Nullable String mimeType) { + return normalizeMimeType(mimeType).startsWith("image/"); } - public static boolean isMimeTypeMedia(String mimeType) { - int fileType = getFileTypeForMimeType(mimeType); - return isAudioFileType(fileType) || isVideoFileType(fileType) - || isImageFileType(fileType) || isPlayListFileType(fileType); + public static boolean isPlayListMimeType(@Nullable String mimeType) { + switch (normalizeMimeType(mimeType)) { + case "application/vnd.ms-wpl": + case "audio/x-mpegurl": + case "audio/mpegurl": + case "application/x-mpegurl": + case "application/vnd.apple.mpegurl": + case "video/x-ms-asf": + case "audio/x-scpls": + return true; + default: + return false; + } + } + + public static boolean isDrmMimeType(@Nullable String mimeType) { + return normalizeMimeType(mimeType).equals("application/x-android-drm-fl"); } // generates a title based on file name @UnsupportedAppUsage - public static String getFileTitle(String path) { + public static @NonNull String getFileTitle(@NonNull String path) { // extract file name after last slash int lastSlash = path.lastIndexOf('/'); if (lastSlash >= 0) { @@ -361,37 +251,111 @@ public class MediaFile { return path; } + public static @Nullable String getFileExtension(@Nullable String path) { + if (path == null) { + return null; + } + int lastDot = path.lastIndexOf('.'); + if (lastDot >= 0) { + return path.substring(lastDot + 1); + } else { + return null; + } + } + + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage public static int getFileTypeForMimeType(String mimeType) { - Integer value = sMimeTypeMap.get(mimeType); - return (value == null ? 0 : value.intValue()); + return 0; + } + + /** + * Find the best MIME type for the given item. Prefers mappings from file + * extensions, since they're more accurate than format codes. + */ + public static @NonNull String getMimeType(@Nullable String path, int formatCode) { + // First look for extension mapping + String mimeType = getMimeTypeForFile(path); + if (!MIME_TYPE_DEFAULT.equals(mimeType)) { + return mimeType; + } + + // Otherwise look for format mapping + return getMimeTypeForFormatCode(formatCode); } @UnsupportedAppUsage - public static String getMimeTypeForFile(String path) { - MediaFileType mediaFileType = getFileType(path); - return (mediaFileType == null ? null : mediaFileType.mimeType); + public static @NonNull String getMimeTypeForFile(@Nullable String path) { + final String mimeType = MimeUtils.guessMimeTypeFromExtension(getFileExtension(path)); + return (mimeType != null) ? mimeType : MIME_TYPE_DEFAULT; } - public static int getFormatCode(String fileName, String mimeType) { - if (mimeType != null) { - Integer value = sMimeTypeToFormatMap.get(mimeType); - if (value != null) { - return value.intValue(); - } + public static @NonNull String getMimeTypeForFormatCode(int formatCode) { + final String mimeType = sFormatToMimeTypeMap.get(formatCode); + return (mimeType != null) ? mimeType : MIME_TYPE_DEFAULT; + } + + /** + * Find the best MTP format code mapping for the given item. Prefers + * mappings from MIME types, since they're more accurate than file + * extensions. + */ + public static int getFormatCode(@Nullable String path, @Nullable String mimeType) { + // First look for MIME type mapping + int formatCode = getFormatCodeForMimeType(mimeType); + if (formatCode != MtpConstants.FORMAT_UNDEFINED) { + return formatCode; } - int lastDot = fileName.lastIndexOf('.'); - if (lastDot > 0) { - String extension = fileName.substring(lastDot + 1).toUpperCase(Locale.ROOT); - Integer value = sFileTypeToFormatMap.get(extension); - if (value != null) { - return value.intValue(); - } + + // Otherwise look for extension mapping + return getFormatCodeForFile(path); + } + + public static int getFormatCodeForFile(@Nullable String path) { + return getFormatCodeForMimeType(getMimeTypeForFile(path)); + } + + public static int getFormatCodeForMimeType(@Nullable String mimeType) { + if (mimeType == null) { + return MtpConstants.FORMAT_UNDEFINED; + } + + // First look for direct mapping + Integer value = sMimeTypeToFormatMap.get(mimeType); + if (value != null) { + return value.intValue(); + } + + // Otherwise look for indirect mapping + mimeType = normalizeMimeType(mimeType); + value = sMimeTypeToFormatMap.get(mimeType); + if (value != null) { + return value.intValue(); + } else if (mimeType.startsWith("audio/")) { + return MtpConstants.FORMAT_UNDEFINED_AUDIO; + } else if (mimeType.startsWith("video/")) { + return MtpConstants.FORMAT_UNDEFINED_VIDEO; + } else if (mimeType.startsWith("image/")) { + return MtpConstants.FORMAT_DEFINED; + } else { + return MtpConstants.FORMAT_UNDEFINED; } - return MtpConstants.FORMAT_UNDEFINED; } - public static String getMimeTypeForFormatCode(int formatCode) { - return sFormatToMimeTypeMap.get(formatCode); + /** + * Normalize the given MIME type by bouncing through a default file + * extension, if defined. This handles cases like "application/x-flac" to + * ".flac" to "audio/flac". + */ + private static @NonNull String normalizeMimeType(@Nullable String mimeType) { + final String extension = MimeUtils.guessExtensionFromMimeType(mimeType); + if (extension != null) { + final String extensionMimeType = MimeUtils.guessMimeTypeFromExtension(extension); + if ( extensionMimeType != null) { + return extensionMimeType; + } + } + return (mimeType != null) ? mimeType : MIME_TYPE_DEFAULT; } } diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index dcacb925b25b..0ff2d8f6b6ec 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -502,6 +502,8 @@ public class MediaScanner implements AutoCloseable { private String mGenre; @UnsupportedAppUsage private String mMimeType; + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage private int mFileType; private int mTrack; @@ -531,7 +533,6 @@ public class MediaScanner implements AutoCloseable { public FileEntry beginFile(String path, String mimeType, long lastModified, long fileSize, boolean isDirectory, boolean noMedia) { mMimeType = mimeType; - mFileType = 0; mFileSize = fileSize; mIsDrm = false; mScanSuccess = true; @@ -542,24 +543,13 @@ public class MediaScanner implements AutoCloseable { } mNoMedia = noMedia; - // try mimeType first, if it is specified - if (mimeType != null) { - mFileType = MediaFile.getFileTypeForMimeType(mimeType); - } - // if mimeType was not specified, compute file type based on file extension. - if (mFileType == 0) { - MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); - if (mediaFileType != null) { - mFileType = mediaFileType.fileType; - if (mMimeType == null) { - mMimeType = mediaFileType.mimeType; - } - } + if (mMimeType == null) { + mMimeType = MediaFile.getMimeTypeForFile(path); } - if (isDrmEnabled() && MediaFile.isDrmFileType(mFileType)) { - mFileType = getFileTypeFromDrm(path); + if (isDrmEnabled() && MediaFile.isDrmMimeType(mMimeType)) { + getMimeTypeFromDrm(path); } } @@ -578,7 +568,7 @@ public class MediaScanner implements AutoCloseable { entry.mLastModifiedChanged = true; } - if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) { + if (mProcessPlaylists && MediaFile.isPlayListMimeType(mMimeType)) { mPlayLists.add(entry); // we don't process playlists in the main scan, so return null return null; @@ -659,9 +649,9 @@ public class MediaScanner implements AutoCloseable { if (noMedia) { result = endFile(entry, false, false, false, false, false); } else { - boolean isaudio = MediaFile.isAudioFileType(mFileType); - boolean isvideo = MediaFile.isVideoFileType(mFileType); - boolean isimage = MediaFile.isImageFileType(mFileType); + boolean isaudio = MediaFile.isAudioMimeType(mMimeType); + boolean isvideo = MediaFile.isVideoMimeType(mMimeType); + boolean isimage = MediaFile.isImageMimeType(mMimeType); if (isaudio || isvideo || isimage) { path = Environment.maybeTranslateEmulatedPathToInternal(new File(path)) @@ -878,7 +868,6 @@ public class MediaScanner implements AutoCloseable { return; } mMimeType = mimeType; - mFileType = MediaFile.getFileTypeForMimeType(mimeType); } /** @@ -906,7 +895,7 @@ public class MediaScanner implements AutoCloseable { } if (!mNoMedia) { - if (MediaFile.isVideoFileType(mFileType)) { + if (MediaFile.isVideoMimeType(mMimeType)) { map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING)); map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 @@ -918,9 +907,9 @@ public class MediaScanner implements AutoCloseable { if (mDate > 0) { map.put(Video.Media.DATE_TAKEN, mDate); } - } else if (MediaFile.isImageFileType(mFileType)) { + } else if (MediaFile.isImageMimeType(mMimeType)) { // FIXME - add DESCRIPTION - } else if (mScanSuccess && MediaFile.isAudioFileType(mFileType)) { + } else if (mScanSuccess && MediaFile.isAudioMimeType(mMimeType)) { map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ? mArtist : MediaStore.UNKNOWN_STRING); map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null && @@ -982,7 +971,7 @@ public class MediaScanner implements AutoCloseable { } } long rowId = entry.mRowId; - if (MediaFile.isAudioFileType(mFileType) && (rowId == 0 || mMtpObjectHandle != 0)) { + if (MediaFile.isAudioMimeType(mMimeType) && (rowId == 0 || mMtpObjectHandle != 0)) { // Only set these for new entries. For existing entries, they // may have been modified later, and we want to keep the current // values so that custom ringtones still show up in the ringtone @@ -992,9 +981,7 @@ public class MediaScanner implements AutoCloseable { values.put(Audio.Media.IS_ALARM, alarms); values.put(Audio.Media.IS_MUSIC, music); values.put(Audio.Media.IS_PODCAST, podcasts); - } else if ((mFileType == MediaFile.FILE_TYPE_JPEG - || mFileType == MediaFile.FILE_TYPE_HEIF - || MediaFile.isRawImageFileType(mFileType)) && !mNoMedia) { + } else if (MediaFile.isExifMimeType(mMimeType) && !mNoMedia) { ExifInterface exif = null; try { exif = new ExifInterface(entry.mPath); @@ -1050,16 +1037,16 @@ public class MediaScanner implements AutoCloseable { int mediaType = FileColumns.MEDIA_TYPE_NONE; MediaInserter inserter = mMediaInserter; if (mScanSuccess && !mNoMedia) { - if (MediaFile.isVideoFileType(mFileType)) { + if (MediaFile.isVideoMimeType(mMimeType)) { tableUri = mVideoUri; mediaType = FileColumns.MEDIA_TYPE_VIDEO; - } else if (MediaFile.isImageFileType(mFileType)) { + } else if (MediaFile.isImageMimeType(mMimeType)) { tableUri = mImagesUri; mediaType = FileColumns.MEDIA_TYPE_IMAGE; - } else if (MediaFile.isAudioFileType(mFileType)) { + } else if (MediaFile.isAudioMimeType(mMimeType)) { tableUri = mAudioUri; mediaType = FileColumns.MEDIA_TYPE_AUDIO; - } else if (MediaFile.isPlayListFileType(mFileType)) { + } else if (MediaFile.isPlayListMimeType(mMimeType)) { tableUri = mPlaylistsUri; mediaType = FileColumns.MEDIA_TYPE_PLAYLIST; } @@ -1176,13 +1163,15 @@ public class MediaScanner implements AutoCloseable { Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1); } + /** @deprecated file types no longer exist */ + @Deprecated @UnsupportedAppUsage private int getFileTypeFromDrm(String path) { - if (!isDrmEnabled()) { - return 0; - } + return 0; + } - int resultFileType = 0; + private void getMimeTypeFromDrm(String path) { + mMimeType = null; if (mDrmManagerClient == null) { mDrmManagerClient = new DrmManagerClient(mContext); @@ -1190,13 +1179,12 @@ public class MediaScanner implements AutoCloseable { if (mDrmManagerClient.canHandle(path, null)) { mIsDrm = true; - String drmMimetype = mDrmManagerClient.getOriginalMimeType(path); - if (drmMimetype != null) { - mMimeType = drmMimetype; - resultFileType = MediaFile.getFileTypeForMimeType(drmMimetype); - } + mMimeType = mDrmManagerClient.getOriginalMimeType(path); + } + + if (mMimeType == null) { + mMimeType = ContentResolver.MIME_TYPE_DEFAULT; } - return resultFileType; } }; // end of anonymous MediaScannerClient instance @@ -1305,10 +1293,8 @@ public class MediaScanner implements AutoCloseable { // modified by the user. // The user can delete them in the media player instead. // instead, clear the path and lastModified fields in the row - MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); - int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType); - - if (!MediaFile.isPlayListFileType(fileType)) { + String mimeType = MediaFile.getMimeTypeForFile(path); + if (!MediaFile.isPlayListMimeType(mimeType)) { deleter.delete(rowId); if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) { deleter.flush(); @@ -1556,14 +1542,13 @@ public class MediaScanner implements AutoCloseable { } public void scanMtpFile(String path, int objectHandle, int format) { - MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); - int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType); + String mimeType = MediaFile.getMimeType(path, format); File file = new File(path); long lastModifiedSeconds = file.lastModified() / 1000; - if (!MediaFile.isAudioFileType(fileType) && !MediaFile.isVideoFileType(fileType) && - !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType) && - !MediaFile.isDrmFileType(fileType)) { + if (!MediaFile.isAudioMimeType(mimeType) && !MediaFile.isVideoMimeType(mimeType) && + !MediaFile.isImageMimeType(mimeType) && !MediaFile.isPlayListMimeType(mimeType) && + !MediaFile.isDrmMimeType(mimeType)) { // no need to use the media scanner, but we need to update last modified and file size ContentValues values = new ContentValues(); @@ -1582,7 +1567,7 @@ public class MediaScanner implements AutoCloseable { mMtpObjectHandle = objectHandle; Cursor fileList = null; try { - if (MediaFile.isPlayListFileType(fileType)) { + if (MediaFile.isPlayListMimeType(mimeType)) { // build file cache so we can look up tracks in the playlist prescan(null, true); @@ -1597,7 +1582,7 @@ public class MediaScanner implements AutoCloseable { prescan(path, false); // always scan the file, so we can return the content://media Uri for existing files - mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(), + mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), (format == MtpConstants.FORMAT_ASSOCIATION), true, isNoMediaPath(path)); } } catch (RemoteException e) { @@ -1911,15 +1896,17 @@ public class MediaScanner implements AutoCloseable { } String playListDirectory = path.substring(0, lastSlash + 1); - MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); - int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType); - - if (fileType == MediaFile.FILE_TYPE_M3U) { - processM3uPlayList(path, playListDirectory, membersUri, values, fileList); - } else if (fileType == MediaFile.FILE_TYPE_PLS) { - processPlsPlayList(path, playListDirectory, membersUri, values, fileList); - } else if (fileType == MediaFile.FILE_TYPE_WPL) { - processWplPlayList(path, playListDirectory, membersUri, values, fileList); + String mimeType = MediaFile.getMimeTypeForFile(path); + switch (mimeType) { + case "application/vnd.ms-wpl": + processWplPlayList(path, playListDirectory, membersUri, values, fileList); + break; + case "audio/x-mpegurl": + processM3uPlayList(path, playListDirectory, membersUri, values, fileList); + break; + case "audio/x-scpls": + processPlsPlayList(path, playListDirectory, membersUri, values, fileList); + break; } } diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java index 07ab06934eb3..4cc86fdce2f2 100644 --- a/media/java/android/media/ThumbnailUtils.java +++ b/media/java/android/media/ThumbnailUtils.java @@ -23,15 +23,13 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; -import android.media.MediaMetadataRetriever; -import android.media.MediaFile.MediaFileType; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.provider.MediaStore.Images; import android.util.Log; -import java.io.FileInputStream; import java.io.FileDescriptor; +import java.io.FileInputStream; import java.io.IOException; /** @@ -94,15 +92,15 @@ public class ThumbnailUtils { : MAX_NUM_PIXELS_MICRO_THUMBNAIL; SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap(); Bitmap bitmap = null; - MediaFileType fileType = MediaFile.getFileType(filePath); - if (fileType != null) { - if (fileType.fileType == MediaFile.FILE_TYPE_JPEG - || MediaFile.isRawImageFileType(fileType.fileType)) { - createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap); - bitmap = sizedThumbnailBitmap.mBitmap; - } else if (fileType.fileType == MediaFile.FILE_TYPE_HEIF) { - bitmap = createThumbnailFromMetadataRetriever(filePath, targetSize, maxPixels); - } + String mimeType = MediaFile.getMimeTypeForFile(filePath); + if (mimeType.equals("image/heif") + || mimeType.equals("image/heif-sequence") + || mimeType.equals("image/heic") + || mimeType.equals("image/heic-sequence")) { + bitmap = createThumbnailFromMetadataRetriever(filePath, targetSize, maxPixels); + } else if (MediaFile.isExifMimeType(mimeType)) { + createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap); + bitmap = sizedThumbnailBitmap.mBitmap; } if (bitmap == null) { diff --git a/media/tests/MediaFrameworkTest/AndroidTest.xml b/media/tests/MediaFrameworkTest/AndroidTest.xml new file mode 100644 index 000000000000..204959ff2749 --- /dev/null +++ b/media/tests/MediaFrameworkTest/AndroidTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs Media Framework Tests"> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="mediaframeworktest.apk" /> + </target_preparer> + + <option name="test-tag" value="MediaFrameworkTest" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.mediaframeworktest" /> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java new file mode 100644 index 000000000000..feac63d40710 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.unit; + +import static android.media.MediaFile.getFormatCode; +import static android.media.MediaFile.getMimeType; +import static android.media.MediaFile.isAudioMimeType; +import static android.media.MediaFile.isImageMimeType; +import static android.media.MediaFile.isPlayListMimeType; +import static android.media.MediaFile.isVideoMimeType; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.mtp.MtpConstants; +import android.support.test.runner.AndroidJUnit4; + +import libcore.net.MimeUtils; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class MediaFileTest { + @Test + public void testCommon() throws Exception { + assertConsistent("FOO.TXT", "text/plain", MtpConstants.FORMAT_TEXT); + assertConsistent("FOO.XML", "text/xml", MtpConstants.FORMAT_XML_DOCUMENT); + assertConsistent("FOO.HTML", "text/html", MtpConstants.FORMAT_HTML); + } + + @Test + public void testAudio() throws Exception { + assertTrue(isAudioMimeType("audio/flac")); + assertTrue(isAudioMimeType("application/x-flac")); + assertFalse(isAudioMimeType("video/mpeg")); + + assertConsistent("FOO.MP3", "audio/mpeg", MtpConstants.FORMAT_MP3); + assertConsistent("FOO.AAC", "audio/aac", MtpConstants.FORMAT_AAC); + assertConsistent("FOO.OGG", "audio/ogg", MtpConstants.FORMAT_OGG); + assertConsistent("FOO.FLAC", "audio/flac", MtpConstants.FORMAT_FLAC); + } + + @Test + public void testVideo() throws Exception { + assertTrue(isVideoMimeType("video/x-msvideo")); + assertFalse(isVideoMimeType("audio/mpeg")); + + assertConsistent("FOO.AVI", "video/avi", MtpConstants.FORMAT_AVI); + assertConsistent("FOO.MP4", "video/mp4", MtpConstants.FORMAT_MP4_CONTAINER); + assertConsistent("FOO.3GP", "video/3gpp", MtpConstants.FORMAT_3GP_CONTAINER); + } + + @Test + public void testImage() throws Exception { + assertTrue(isImageMimeType("image/jpeg")); + assertTrue(isImageMimeType("image/heif")); + assertTrue(isImageMimeType("image/webp")); + assertFalse(isImageMimeType("video/webm")); + + assertConsistent("FOO.JPG", "image/jpeg", MtpConstants.FORMAT_EXIF_JPEG); + assertConsistent("FOO.PNG", "image/png", MtpConstants.FORMAT_PNG); + assertConsistent("FOO.HEIF", "image/heif", MtpConstants.FORMAT_HEIF); + assertConsistent("FOO.DNG", "image/x-adobe-dng", MtpConstants.FORMAT_DNG); + assertConsistent("FOO.TIFF", "image/tiff", MtpConstants.FORMAT_TIFF); + } + + @Test + public void testPlayList() throws Exception { + assertTrue(isPlayListMimeType(MimeUtils.guessMimeTypeFromExtension("pls"))); + assertTrue(isPlayListMimeType(MimeUtils.guessMimeTypeFromExtension("wpl"))); + assertTrue(isPlayListMimeType(MimeUtils.guessMimeTypeFromExtension("m3u"))); + assertTrue(isPlayListMimeType(MimeUtils.guessMimeTypeFromExtension("m3u8"))); + assertTrue(isPlayListMimeType(MimeUtils.guessMimeTypeFromExtension("asf"))); + } + + @Test + public void testImageRaw() throws Exception { + // We trust MIME types before filenames + assertHexEquals(MtpConstants.FORMAT_TIFF, getFormatCode("FOO.CR2", "image/x-canon-cr2")); + // We trust filenames before format codes + assertEquals("image/x-canon-cr2", getMimeType("FOO.CR2", MtpConstants.FORMAT_TIFF)); + } + + @Test + public void testConfusing() throws Exception { + // We trust MIME types before filenames + assertHexEquals(MtpConstants.FORMAT_MP3, getFormatCode("foo.avi", "audio/mpeg")); + // We trust filenames before format codes + assertEquals("video/avi", getMimeType("foo.avi", MtpConstants.FORMAT_MP3)); + } + + @Test + public void testUnknown() throws Exception { + assertHexEquals(MtpConstants.FORMAT_UNDEFINED, + getFormatCode("foo.example", "application/x-example")); + assertHexEquals(MtpConstants.FORMAT_UNDEFINED_AUDIO, + getFormatCode("foo.example", "audio/x-example")); + assertHexEquals(MtpConstants.FORMAT_UNDEFINED_VIDEO, + getFormatCode("foo.example", "video/x-example")); + assertHexEquals(MtpConstants.FORMAT_DEFINED, + getFormatCode("foo.example", "image/x-example")); + } + + private static void assertConsistent(String path, String mimeType, int formatCode) { + assertHexEquals(formatCode, getFormatCode(path, null)); + assertHexEquals(formatCode, getFormatCode(null, mimeType)); + assertHexEquals(formatCode, getFormatCode(path, mimeType)); + + assertEquals(mimeType, getMimeType(path, MtpConstants.FORMAT_UNDEFINED)); + assertEquals(mimeType, getMimeType(null, formatCode)); + assertEquals(mimeType, getMimeType(path, formatCode)); + } + + private static void assertHexEquals(int expected, int actual) { + assertEquals(Integer.toHexString(expected), Integer.toHexString(actual)); + } +} diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java index f243b51d6769..59c205aebe18 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java @@ -851,25 +851,7 @@ class MtpDatabase { return DocumentsContract.Document.MIME_TYPE_DIR; } - final String formatCodeMimeType = MediaFile.getMimeTypeForFormatCode(info.getFormat()); - final String mediaFileMimeType = MediaFile.getMimeTypeForFile(info.getName()); - - // Format code can be mapped with multiple mime types, e.g. FORMAT_MPEG is mapped with - // audio/mp4 and video/mp4. - // As file extension contains more information than format code, returns mime type obtained - // from file extension if it is consistent with format code. - if (mediaFileMimeType != null && - MediaFile.getFormatCode("", mediaFileMimeType) == info.getFormat()) { - return mediaFileMimeType; - } - if (formatCodeMimeType != null) { - return formatCodeMimeType; - } - if (mediaFileMimeType != null) { - return mediaFileMimeType; - } - // We don't know the file type. - return "application/octet-stream"; + return MediaFile.getMimeType(info.getName(), info.getFormat()); } private static int getRootFlags(int[] operationsSupported) { |