1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media; 18 19 import static android.content.ContentResolver.MIME_TYPE_DEFAULT; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.mtp.MtpConstants; 25 26 import libcore.content.type.MimeMap; 27 28 import java.util.HashMap; 29 30 /** 31 * MediaScanner helper class. 32 * <p> 33 * This heavily relies upon extension to MIME type mappings which are maintained 34 * in {@link MimeMap}, to ensure consistency across the OS. 35 * <p> 36 * When adding a new file type, first add the MIME type mapping to 37 * {@link MimeMap}, and then add the MTP format mapping here. 38 * 39 * @hide 40 */ 41 public class MediaFile { 42 43 /** @deprecated file types no longer exist */ 44 @Deprecated 45 @UnsupportedAppUsage 46 private static final int FIRST_AUDIO_FILE_TYPE = 1; 47 /** @deprecated file types no longer exist */ 48 @Deprecated 49 @UnsupportedAppUsage 50 private static final int LAST_AUDIO_FILE_TYPE = 10; 51 52 /** @deprecated file types no longer exist */ 53 @Deprecated 54 public static class MediaFileType { 55 @UnsupportedAppUsage 56 public final int fileType; 57 @UnsupportedAppUsage 58 public final String mimeType; 59 MediaFileType(int fileType, String mimeType)60 MediaFileType(int fileType, String mimeType) { 61 this.fileType = fileType; 62 this.mimeType = mimeType; 63 } 64 } 65 66 /** @deprecated file types no longer exist */ 67 @Deprecated 68 @UnsupportedAppUsage 69 private static final HashMap<String, MediaFileType> sFileTypeMap = new HashMap<>(); 70 /** @deprecated file types no longer exist */ 71 @Deprecated 72 @UnsupportedAppUsage 73 private static final HashMap<String, Integer> sFileTypeToFormatMap = new HashMap<>(); 74 75 // maps mime type to MTP format code 76 @UnsupportedAppUsage 77 private static final HashMap<String, Integer> sMimeTypeToFormatMap = new HashMap<>(); 78 // maps MTP format code to mime type 79 @UnsupportedAppUsage 80 private static final HashMap<Integer, String> sFormatToMimeTypeMap = new HashMap<>(); 81 82 @UnsupportedAppUsage MediaFile()83 public MediaFile() { 84 } 85 86 /** @deprecated file types no longer exist */ 87 @Deprecated 88 @UnsupportedAppUsage addFileType(String extension, int fileType, String mimeType)89 static void addFileType(String extension, int fileType, String mimeType) { 90 } 91 addFileType(int mtpFormatCode, @NonNull String mimeType)92 private static void addFileType(int mtpFormatCode, @NonNull String mimeType) { 93 if (!sMimeTypeToFormatMap.containsKey(mimeType)) { 94 sMimeTypeToFormatMap.put(mimeType, Integer.valueOf(mtpFormatCode)); 95 } 96 if (!sFormatToMimeTypeMap.containsKey(mtpFormatCode)) { 97 sFormatToMimeTypeMap.put(mtpFormatCode, mimeType); 98 } 99 } 100 101 static { addFileType(MtpConstants.FORMAT_MP3, "audio/mpeg")102 addFileType(MtpConstants.FORMAT_MP3, "audio/mpeg"); addFileType(MtpConstants.FORMAT_WAV, "audio/x-wav")103 addFileType(MtpConstants.FORMAT_WAV, "audio/x-wav"); addFileType(MtpConstants.FORMAT_WMA, "audio/x-ms-wma")104 addFileType(MtpConstants.FORMAT_WMA, "audio/x-ms-wma"); addFileType(MtpConstants.FORMAT_OGG, "audio/ogg")105 addFileType(MtpConstants.FORMAT_OGG, "audio/ogg"); addFileType(MtpConstants.FORMAT_AAC, "audio/aac")106 addFileType(MtpConstants.FORMAT_AAC, "audio/aac"); addFileType(MtpConstants.FORMAT_FLAC, "audio/flac")107 addFileType(MtpConstants.FORMAT_FLAC, "audio/flac"); addFileType(MtpConstants.FORMAT_AIFF, "audio/x-aiff")108 addFileType(MtpConstants.FORMAT_AIFF, "audio/x-aiff"); addFileType(MtpConstants.FORMAT_MP2, "audio/mpeg")109 addFileType(MtpConstants.FORMAT_MP2, "audio/mpeg"); 110 addFileType(MtpConstants.FORMAT_MPEG, "video/mpeg")111 addFileType(MtpConstants.FORMAT_MPEG, "video/mpeg"); addFileType(MtpConstants.FORMAT_MP4_CONTAINER, "video/mp4")112 addFileType(MtpConstants.FORMAT_MP4_CONTAINER, "video/mp4"); addFileType(MtpConstants.FORMAT_3GP_CONTAINER, "video/3gpp")113 addFileType(MtpConstants.FORMAT_3GP_CONTAINER, "video/3gpp"); addFileType(MtpConstants.FORMAT_3GP_CONTAINER, "video/3gpp2")114 addFileType(MtpConstants.FORMAT_3GP_CONTAINER, "video/3gpp2"); addFileType(MtpConstants.FORMAT_AVI, "video/avi")115 addFileType(MtpConstants.FORMAT_AVI, "video/avi"); addFileType(MtpConstants.FORMAT_WMV, "video/x-ms-wmv")116 addFileType(MtpConstants.FORMAT_WMV, "video/x-ms-wmv"); addFileType(MtpConstants.FORMAT_ASF, "video/x-ms-asf")117 addFileType(MtpConstants.FORMAT_ASF, "video/x-ms-asf"); 118 addFileType(MtpConstants.FORMAT_EXIF_JPEG, "image/jpeg")119 addFileType(MtpConstants.FORMAT_EXIF_JPEG, "image/jpeg"); addFileType(MtpConstants.FORMAT_GIF, "image/gif")120 addFileType(MtpConstants.FORMAT_GIF, "image/gif"); addFileType(MtpConstants.FORMAT_PNG, "image/png")121 addFileType(MtpConstants.FORMAT_PNG, "image/png"); addFileType(MtpConstants.FORMAT_BMP, "image/x-ms-bmp")122 addFileType(MtpConstants.FORMAT_BMP, "image/x-ms-bmp"); addFileType(MtpConstants.FORMAT_HEIF, "image/heif")123 addFileType(MtpConstants.FORMAT_HEIF, "image/heif"); addFileType(MtpConstants.FORMAT_DNG, "image/x-adobe-dng")124 addFileType(MtpConstants.FORMAT_DNG, "image/x-adobe-dng"); addFileType(MtpConstants.FORMAT_TIFF, "image/tiff")125 addFileType(MtpConstants.FORMAT_TIFF, "image/tiff"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-canon-cr2")126 addFileType(MtpConstants.FORMAT_TIFF, "image/x-canon-cr2"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-nikon-nrw")127 addFileType(MtpConstants.FORMAT_TIFF, "image/x-nikon-nrw"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-sony-arw")128 addFileType(MtpConstants.FORMAT_TIFF, "image/x-sony-arw"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-panasonic-rw2")129 addFileType(MtpConstants.FORMAT_TIFF, "image/x-panasonic-rw2"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-olympus-orf")130 addFileType(MtpConstants.FORMAT_TIFF, "image/x-olympus-orf"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-pentax-pef")131 addFileType(MtpConstants.FORMAT_TIFF, "image/x-pentax-pef"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-samsung-srw")132 addFileType(MtpConstants.FORMAT_TIFF, "image/x-samsung-srw"); addFileType(MtpConstants.FORMAT_TIFF_EP, "image/tiff")133 addFileType(MtpConstants.FORMAT_TIFF_EP, "image/tiff"); addFileType(MtpConstants.FORMAT_TIFF_EP, "image/x-nikon-nef")134 addFileType(MtpConstants.FORMAT_TIFF_EP, "image/x-nikon-nef"); addFileType(MtpConstants.FORMAT_JP2, "image/jp2")135 addFileType(MtpConstants.FORMAT_JP2, "image/jp2"); addFileType(MtpConstants.FORMAT_JPX, "image/jpx")136 addFileType(MtpConstants.FORMAT_JPX, "image/jpx"); 137 addFileType(MtpConstants.FORMAT_M3U_PLAYLIST, "audio/x-mpegurl")138 addFileType(MtpConstants.FORMAT_M3U_PLAYLIST, "audio/x-mpegurl"); addFileType(MtpConstants.FORMAT_PLS_PLAYLIST, "audio/x-scpls")139 addFileType(MtpConstants.FORMAT_PLS_PLAYLIST, "audio/x-scpls"); addFileType(MtpConstants.FORMAT_WPL_PLAYLIST, "application/vnd.ms-wpl")140 addFileType(MtpConstants.FORMAT_WPL_PLAYLIST, "application/vnd.ms-wpl"); addFileType(MtpConstants.FORMAT_ASX_PLAYLIST, "video/x-ms-asf")141 addFileType(MtpConstants.FORMAT_ASX_PLAYLIST, "video/x-ms-asf"); 142 addFileType(MtpConstants.FORMAT_TEXT, "text/plain")143 addFileType(MtpConstants.FORMAT_TEXT, "text/plain"); addFileType(MtpConstants.FORMAT_HTML, "text/html")144 addFileType(MtpConstants.FORMAT_HTML, "text/html"); addFileType(MtpConstants.FORMAT_XML_DOCUMENT, "text/xml")145 addFileType(MtpConstants.FORMAT_XML_DOCUMENT, "text/xml"); 146 addFileType(MtpConstants.FORMAT_MS_WORD_DOCUMENT, "application/msword")147 addFileType(MtpConstants.FORMAT_MS_WORD_DOCUMENT, 148 "application/msword"); addFileType(MtpConstants.FORMAT_MS_WORD_DOCUMENT, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")149 addFileType(MtpConstants.FORMAT_MS_WORD_DOCUMENT, 150 "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); addFileType(MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, "application/vnd.ms-excel")151 addFileType(MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, 152 "application/vnd.ms-excel"); addFileType(MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")153 addFileType(MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, 154 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); addFileType(MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, "application/vnd.ms-powerpoint")155 addFileType(MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, 156 "application/vnd.ms-powerpoint"); addFileType(MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, "application/vnd.openxmlformats-officedocument.presentationml.presentation")157 addFileType(MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, 158 "application/vnd.openxmlformats-officedocument.presentationml.presentation"); 159 } 160 161 /** @deprecated file types no longer exist */ 162 @Deprecated 163 @UnsupportedAppUsage isAudioFileType(int fileType)164 public static boolean isAudioFileType(int fileType) { 165 return false; 166 } 167 168 /** @deprecated file types no longer exist */ 169 @Deprecated 170 @UnsupportedAppUsage isVideoFileType(int fileType)171 public static boolean isVideoFileType(int fileType) { 172 return false; 173 } 174 175 /** @deprecated file types no longer exist */ 176 @Deprecated 177 @UnsupportedAppUsage isImageFileType(int fileType)178 public static boolean isImageFileType(int fileType) { 179 return false; 180 } 181 182 /** @deprecated file types no longer exist */ 183 @Deprecated 184 @UnsupportedAppUsage isPlayListFileType(int fileType)185 public static boolean isPlayListFileType(int fileType) { 186 return false; 187 } 188 189 /** @deprecated file types no longer exist */ 190 @Deprecated 191 @UnsupportedAppUsage isDrmFileType(int fileType)192 public static boolean isDrmFileType(int fileType) { 193 return false; 194 } 195 196 /** @deprecated file types no longer exist */ 197 @Deprecated 198 @UnsupportedAppUsage getFileType(String path)199 public static MediaFileType getFileType(String path) { 200 return null; 201 } 202 isExifMimeType(@ullable String mimeType)203 public static boolean isExifMimeType(@Nullable String mimeType) { 204 // For simplicity, assume that all image files might have EXIF data 205 return isImageMimeType(mimeType); 206 } 207 isAudioMimeType(@ullable String mimeType)208 public static boolean isAudioMimeType(@Nullable String mimeType) { 209 return normalizeMimeType(mimeType).startsWith("audio/"); 210 } 211 isVideoMimeType(@ullable String mimeType)212 public static boolean isVideoMimeType(@Nullable String mimeType) { 213 return normalizeMimeType(mimeType).startsWith("video/"); 214 } 215 isImageMimeType(@ullable String mimeType)216 public static boolean isImageMimeType(@Nullable String mimeType) { 217 return normalizeMimeType(mimeType).startsWith("image/"); 218 } 219 isPlayListMimeType(@ullable String mimeType)220 public static boolean isPlayListMimeType(@Nullable String mimeType) { 221 switch (normalizeMimeType(mimeType)) { 222 case "application/vnd.ms-wpl": 223 case "audio/x-mpegurl": 224 case "audio/mpegurl": 225 case "application/x-mpegurl": 226 case "application/vnd.apple.mpegurl": 227 case "audio/x-scpls": 228 return true; 229 default: 230 return false; 231 } 232 } 233 isDrmMimeType(@ullable String mimeType)234 public static boolean isDrmMimeType(@Nullable String mimeType) { 235 return normalizeMimeType(mimeType).equals("application/x-android-drm-fl"); 236 } 237 238 // generates a title based on file name 239 @UnsupportedAppUsage getFileTitle(@onNull String path)240 public static @NonNull String getFileTitle(@NonNull String path) { 241 // extract file name after last slash 242 int lastSlash = path.lastIndexOf('/'); 243 if (lastSlash >= 0) { 244 lastSlash++; 245 if (lastSlash < path.length()) { 246 path = path.substring(lastSlash); 247 } 248 } 249 // truncate the file extension (if any) 250 int lastDot = path.lastIndexOf('.'); 251 if (lastDot > 0) { 252 path = path.substring(0, lastDot); 253 } 254 return path; 255 } 256 getFileExtension(@ullable String path)257 public static @Nullable String getFileExtension(@Nullable String path) { 258 if (path == null) { 259 return null; 260 } 261 int lastDot = path.lastIndexOf('.'); 262 if (lastDot >= 0) { 263 return path.substring(lastDot + 1); 264 } else { 265 return null; 266 } 267 } 268 269 /** @deprecated file types no longer exist */ 270 @Deprecated 271 @UnsupportedAppUsage getFileTypeForMimeType(String mimeType)272 public static int getFileTypeForMimeType(String mimeType) { 273 return 0; 274 } 275 276 /** 277 * Find the best MIME type for the given item. Prefers mappings from file 278 * extensions, since they're more accurate than format codes. 279 */ getMimeType(@ullable String path, int formatCode)280 public static @NonNull String getMimeType(@Nullable String path, int formatCode) { 281 // First look for extension mapping 282 String mimeType = getMimeTypeForFile(path); 283 if (!MIME_TYPE_DEFAULT.equals(mimeType)) { 284 return mimeType; 285 } 286 287 // Otherwise look for format mapping 288 return getMimeTypeForFormatCode(formatCode); 289 } 290 291 @UnsupportedAppUsage getMimeTypeForFile(@ullable String path)292 public static @NonNull String getMimeTypeForFile(@Nullable String path) { 293 String ext = getFileExtension(path); 294 final String mimeType = MimeMap.getDefault().guessMimeTypeFromExtension(ext); 295 return (mimeType != null) ? mimeType : MIME_TYPE_DEFAULT; 296 } 297 getMimeTypeForFormatCode(int formatCode)298 public static @NonNull String getMimeTypeForFormatCode(int formatCode) { 299 final String mimeType = sFormatToMimeTypeMap.get(formatCode); 300 return (mimeType != null) ? mimeType : MIME_TYPE_DEFAULT; 301 } 302 303 /** 304 * Find the best MTP format code mapping for the given item. Prefers 305 * mappings from MIME types, since they're more accurate than file 306 * extensions. 307 */ getFormatCode(@ullable String path, @Nullable String mimeType)308 public static int getFormatCode(@Nullable String path, @Nullable String mimeType) { 309 // First look for MIME type mapping 310 int formatCode = getFormatCodeForMimeType(mimeType); 311 if (formatCode != MtpConstants.FORMAT_UNDEFINED) { 312 return formatCode; 313 } 314 315 // Otherwise look for extension mapping 316 return getFormatCodeForFile(path); 317 } 318 getFormatCodeForFile(@ullable String path)319 public static int getFormatCodeForFile(@Nullable String path) { 320 return getFormatCodeForMimeType(getMimeTypeForFile(path)); 321 } 322 getFormatCodeForMimeType(@ullable String mimeType)323 public static int getFormatCodeForMimeType(@Nullable String mimeType) { 324 if (mimeType == null) { 325 return MtpConstants.FORMAT_UNDEFINED; 326 } 327 328 // First look for direct mapping 329 Integer value = sMimeTypeToFormatMap.get(mimeType); 330 if (value != null) { 331 return value.intValue(); 332 } 333 334 // Otherwise look for indirect mapping 335 mimeType = normalizeMimeType(mimeType); 336 value = sMimeTypeToFormatMap.get(mimeType); 337 if (value != null) { 338 return value.intValue(); 339 } else if (mimeType.startsWith("audio/")) { 340 return MtpConstants.FORMAT_UNDEFINED_AUDIO; 341 } else if (mimeType.startsWith("video/")) { 342 return MtpConstants.FORMAT_UNDEFINED_VIDEO; 343 } else if (mimeType.startsWith("image/")) { 344 return MtpConstants.FORMAT_DEFINED; 345 } else { 346 return MtpConstants.FORMAT_UNDEFINED; 347 } 348 } 349 350 /** 351 * Normalize the given MIME type by bouncing through a default file 352 * extension, if defined. This handles cases like "application/x-flac" to 353 * ".flac" to "audio/flac". 354 */ normalizeMimeType(@ullable String mimeType)355 private static @NonNull String normalizeMimeType(@Nullable String mimeType) { 356 MimeMap mimeMap = MimeMap.getDefault(); 357 final String extension = mimeMap.guessExtensionFromMimeType(mimeType); 358 if (extension != null) { 359 final String extensionMimeType = mimeMap.guessMimeTypeFromExtension(extension); 360 if (extensionMimeType != null) { 361 return extensionMimeType; 362 } 363 } 364 return (mimeType != null) ? mimeType : MIME_TYPE_DEFAULT; 365 } 366 } 367