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
This commit is contained in:
Jeff Sharkey
2018-08-27 18:03:33 -06:00
parent bfbdd1c0b9
commit 91e3cd4c6c
9 changed files with 469 additions and 375 deletions

View File

@@ -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;

View File

@@ -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(

View File

@@ -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;
}
}

View File

@@ -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));
private static void addFileType(int mtpFormatCode, @NonNull String mimeType) {
if (!sMimeTypeToFormatMap.containsKey(mimeType)) {
sMimeTypeToFormatMap.put(mimeType, Integer.valueOf(mtpFormatCode));
}
if (!sFormatToMimeTypeMap.containsKey(mtpFormatCode)) {
sFormatToMimeTypeMap.put(mtpFormatCode, mimeType);
}
}
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;
}
}
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(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("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(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("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(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("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");
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");
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(MtpConstants.FORMAT_TEXT, "text/plain");
addFileType(MtpConstants.FORMAT_HTML, "text/html");
addFileType(MtpConstants.FORMAT_XML_DOCUMENT, "text/xml");
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_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 isMimeTypeMedia(String mimeType) {
int fileType = getFileTypeForMimeType(mimeType);
return isAudioFileType(fileType) || isVideoFileType(fileType)
|| isImageFileType(fileType) || isPlayListFileType(fileType);
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 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();
}
}
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();
}
}
return MtpConstants.FORMAT_UNDEFINED;
public static @NonNull String getMimeTypeForFormatCode(int formatCode) {
final String mimeType = sFormatToMimeTypeMap.get(formatCode);
return (mimeType != null) ? mimeType : MIME_TYPE_DEFAULT;
}
public static String getMimeTypeForFormatCode(int formatCode) {
return sFormatToMimeTypeMap.get(formatCode);
/**
* 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;
}
// 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;
}
}
/**
* 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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) {

View File

@@ -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>

View File

@@ -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));
}
}

View File

@@ -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) {