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 import java.util.Locale; 30 31 /** 32 * MediaScanner helper class. 33 * <p> 34 * This heavily relies upon extension to MIME type mappings which are maintained 35 * in {@link MimeMap}, to ensure consistency across the OS. 36 * <p> 37 * When adding a new file type, first add the MIME type mapping to 38 * {@link MimeMap}, and then add the MTP format mapping here. 39 * 40 * @hide 41 */ 42 public class MediaFile { 43 44 /** @deprecated file types no longer exist */ 45 @Deprecated 46 @UnsupportedAppUsage 47 private static final int FIRST_AUDIO_FILE_TYPE = 1; 48 /** @deprecated file types no longer exist */ 49 @Deprecated 50 @UnsupportedAppUsage 51 private static final int LAST_AUDIO_FILE_TYPE = 10; 52 53 /** @deprecated file types no longer exist */ 54 @Deprecated 55 public static class MediaFileType { 56 @UnsupportedAppUsage 57 public final int fileType; 58 @UnsupportedAppUsage 59 public final String mimeType; 60 MediaFileType(int fileType, String mimeType)61 MediaFileType(int fileType, String mimeType) { 62 this.fileType = fileType; 63 this.mimeType = mimeType; 64 } 65 } 66 67 /** @deprecated file types no longer exist */ 68 @Deprecated 69 @UnsupportedAppUsage 70 private static final HashMap<String, MediaFileType> sFileTypeMap = new HashMap<>(); 71 /** @deprecated file types no longer exist */ 72 @Deprecated 73 @UnsupportedAppUsage 74 private static final HashMap<String, Integer> sFileTypeToFormatMap = new HashMap<>(); 75 76 // maps mime type to MTP format code 77 @UnsupportedAppUsage 78 private static final HashMap<String, Integer> sMimeTypeToFormatMap = new HashMap<>(); 79 // maps MTP format code to mime type 80 @UnsupportedAppUsage 81 private static final HashMap<Integer, String> sFormatToMimeTypeMap = new HashMap<>(); 82 83 @UnsupportedAppUsage MediaFile()84 public MediaFile() { 85 } 86 87 /** @deprecated file types no longer exist */ 88 @Deprecated 89 @UnsupportedAppUsage addFileType(String extension, int fileType, String mimeType)90 static void addFileType(String extension, int fileType, String mimeType) { 91 } 92 addFileType(int mtpFormatCode, @NonNull String mimeType)93 private static void addFileType(int mtpFormatCode, @NonNull String mimeType) { 94 if (!sMimeTypeToFormatMap.containsKey(mimeType)) { 95 sMimeTypeToFormatMap.put(mimeType, Integer.valueOf(mtpFormatCode)); 96 } 97 if (!sFormatToMimeTypeMap.containsKey(mtpFormatCode)) { 98 sFormatToMimeTypeMap.put(mtpFormatCode, mimeType); 99 } 100 } 101 102 static { addFileType(MtpConstants.FORMAT_MP3, "audio/mpeg")103 addFileType(MtpConstants.FORMAT_MP3, "audio/mpeg"); addFileType(MtpConstants.FORMAT_WAV, "audio/x-wav")104 addFileType(MtpConstants.FORMAT_WAV, "audio/x-wav"); addFileType(MtpConstants.FORMAT_WMA, "audio/x-ms-wma")105 addFileType(MtpConstants.FORMAT_WMA, "audio/x-ms-wma"); addFileType(MtpConstants.FORMAT_OGG, "audio/ogg")106 addFileType(MtpConstants.FORMAT_OGG, "audio/ogg"); addFileType(MtpConstants.FORMAT_AAC, "audio/aac")107 addFileType(MtpConstants.FORMAT_AAC, "audio/aac"); addFileType(MtpConstants.FORMAT_FLAC, "audio/flac")108 addFileType(MtpConstants.FORMAT_FLAC, "audio/flac"); addFileType(MtpConstants.FORMAT_AIFF, "audio/x-aiff")109 addFileType(MtpConstants.FORMAT_AIFF, "audio/x-aiff"); addFileType(MtpConstants.FORMAT_MP2, "audio/mpeg")110 addFileType(MtpConstants.FORMAT_MP2, "audio/mpeg"); 111 addFileType(MtpConstants.FORMAT_MPEG, "video/mpeg")112 addFileType(MtpConstants.FORMAT_MPEG, "video/mpeg"); addFileType(MtpConstants.FORMAT_MP4_CONTAINER, "video/mp4")113 addFileType(MtpConstants.FORMAT_MP4_CONTAINER, "video/mp4"); addFileType(MtpConstants.FORMAT_3GP_CONTAINER, "video/3gpp")114 addFileType(MtpConstants.FORMAT_3GP_CONTAINER, "video/3gpp"); addFileType(MtpConstants.FORMAT_3GP_CONTAINER, "video/3gpp2")115 addFileType(MtpConstants.FORMAT_3GP_CONTAINER, "video/3gpp2"); addFileType(MtpConstants.FORMAT_AVI, "video/avi")116 addFileType(MtpConstants.FORMAT_AVI, "video/avi"); addFileType(MtpConstants.FORMAT_WMV, "video/x-ms-wmv")117 addFileType(MtpConstants.FORMAT_WMV, "video/x-ms-wmv"); addFileType(MtpConstants.FORMAT_ASF, "video/x-ms-asf")118 addFileType(MtpConstants.FORMAT_ASF, "video/x-ms-asf"); 119 addFileType(MtpConstants.FORMAT_EXIF_JPEG, "image/jpeg")120 addFileType(MtpConstants.FORMAT_EXIF_JPEG, "image/jpeg"); addFileType(MtpConstants.FORMAT_GIF, "image/gif")121 addFileType(MtpConstants.FORMAT_GIF, "image/gif"); addFileType(MtpConstants.FORMAT_PNG, "image/png")122 addFileType(MtpConstants.FORMAT_PNG, "image/png"); addFileType(MtpConstants.FORMAT_BMP, "image/x-ms-bmp")123 addFileType(MtpConstants.FORMAT_BMP, "image/x-ms-bmp"); addFileType(MtpConstants.FORMAT_HEIF, "image/heif")124 addFileType(MtpConstants.FORMAT_HEIF, "image/heif"); addFileType(MtpConstants.FORMAT_DNG, "image/x-adobe-dng")125 addFileType(MtpConstants.FORMAT_DNG, "image/x-adobe-dng"); addFileType(MtpConstants.FORMAT_TIFF, "image/tiff")126 addFileType(MtpConstants.FORMAT_TIFF, "image/tiff"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-canon-cr2")127 addFileType(MtpConstants.FORMAT_TIFF, "image/x-canon-cr2"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-nikon-nrw")128 addFileType(MtpConstants.FORMAT_TIFF, "image/x-nikon-nrw"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-sony-arw")129 addFileType(MtpConstants.FORMAT_TIFF, "image/x-sony-arw"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-panasonic-rw2")130 addFileType(MtpConstants.FORMAT_TIFF, "image/x-panasonic-rw2"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-olympus-orf")131 addFileType(MtpConstants.FORMAT_TIFF, "image/x-olympus-orf"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-pentax-pef")132 addFileType(MtpConstants.FORMAT_TIFF, "image/x-pentax-pef"); addFileType(MtpConstants.FORMAT_TIFF, "image/x-samsung-srw")133 addFileType(MtpConstants.FORMAT_TIFF, "image/x-samsung-srw"); addFileType(MtpConstants.FORMAT_TIFF_EP, "image/tiff")134 addFileType(MtpConstants.FORMAT_TIFF_EP, "image/tiff"); addFileType(MtpConstants.FORMAT_TIFF_EP, "image/x-nikon-nef")135 addFileType(MtpConstants.FORMAT_TIFF_EP, "image/x-nikon-nef"); addFileType(MtpConstants.FORMAT_JP2, "image/jp2")136 addFileType(MtpConstants.FORMAT_JP2, "image/jp2"); addFileType(MtpConstants.FORMAT_JPX, "image/jpx")137 addFileType(MtpConstants.FORMAT_JPX, "image/jpx"); 138 addFileType(MtpConstants.FORMAT_M3U_PLAYLIST, "audio/x-mpegurl")139 addFileType(MtpConstants.FORMAT_M3U_PLAYLIST, "audio/x-mpegurl"); addFileType(MtpConstants.FORMAT_PLS_PLAYLIST, "audio/x-scpls")140 addFileType(MtpConstants.FORMAT_PLS_PLAYLIST, "audio/x-scpls"); addFileType(MtpConstants.FORMAT_WPL_PLAYLIST, "application/vnd.ms-wpl")141 addFileType(MtpConstants.FORMAT_WPL_PLAYLIST, "application/vnd.ms-wpl"); addFileType(MtpConstants.FORMAT_ASX_PLAYLIST, "video/x-ms-asf")142 addFileType(MtpConstants.FORMAT_ASX_PLAYLIST, "video/x-ms-asf"); 143 addFileType(MtpConstants.FORMAT_TEXT, "text/plain")144 addFileType(MtpConstants.FORMAT_TEXT, "text/plain"); addFileType(MtpConstants.FORMAT_HTML, "text/html")145 addFileType(MtpConstants.FORMAT_HTML, "text/html"); addFileType(MtpConstants.FORMAT_XML_DOCUMENT, "text/xml")146 addFileType(MtpConstants.FORMAT_XML_DOCUMENT, "text/xml"); 147 addFileType(MtpConstants.FORMAT_MS_WORD_DOCUMENT, "application/msword")148 addFileType(MtpConstants.FORMAT_MS_WORD_DOCUMENT, 149 "application/msword"); addFileType(MtpConstants.FORMAT_MS_WORD_DOCUMENT, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")150 addFileType(MtpConstants.FORMAT_MS_WORD_DOCUMENT, 151 "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); addFileType(MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, "application/vnd.ms-excel")152 addFileType(MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, 153 "application/vnd.ms-excel"); addFileType(MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")154 addFileType(MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET, 155 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); addFileType(MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, "application/vnd.ms-powerpoint")156 addFileType(MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, 157 "application/vnd.ms-powerpoint"); addFileType(MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, "application/vnd.openxmlformats-officedocument.presentationml.presentation")158 addFileType(MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION, 159 "application/vnd.openxmlformats-officedocument.presentationml.presentation"); 160 } 161 162 /** @deprecated file types no longer exist */ 163 @Deprecated 164 @UnsupportedAppUsage isAudioFileType(int fileType)165 public static boolean isAudioFileType(int fileType) { 166 return false; 167 } 168 169 /** @deprecated file types no longer exist */ 170 @Deprecated 171 @UnsupportedAppUsage isVideoFileType(int fileType)172 public static boolean isVideoFileType(int fileType) { 173 return false; 174 } 175 176 /** @deprecated file types no longer exist */ 177 @Deprecated 178 @UnsupportedAppUsage isImageFileType(int fileType)179 public static boolean isImageFileType(int fileType) { 180 return false; 181 } 182 183 /** @deprecated file types no longer exist */ 184 @Deprecated 185 @UnsupportedAppUsage isPlayListFileType(int fileType)186 public static boolean isPlayListFileType(int fileType) { 187 return false; 188 } 189 190 /** @deprecated file types no longer exist */ 191 @Deprecated 192 @UnsupportedAppUsage isDrmFileType(int fileType)193 public static boolean isDrmFileType(int fileType) { 194 return false; 195 } 196 197 /** @deprecated file types no longer exist */ 198 @Deprecated 199 @UnsupportedAppUsage getFileType(String path)200 public static MediaFileType getFileType(String path) { 201 return null; 202 } 203 204 /** 205 * Check whether the mime type is document or not. 206 * @param mimeType the mime type to check 207 * @return true, if the mimeType is matched. Otherwise, false. 208 */ isDocumentMimeType(@ullable String mimeType)209 public static boolean isDocumentMimeType(@Nullable String mimeType) { 210 if (mimeType == null) { 211 return false; 212 } 213 214 final String normalizedMimeType = normalizeMimeType(mimeType); 215 if (normalizedMimeType.startsWith("text/")) { 216 return true; 217 } 218 219 switch (normalizedMimeType.toLowerCase(Locale.ROOT)) { 220 case "application/epub+zip": 221 case "application/msword": 222 case "application/pdf": 223 case "application/rtf": 224 case "application/vnd.ms-excel": 225 case "application/vnd.ms-excel.addin.macroenabled.12": 226 case "application/vnd.ms-excel.sheet.binary.macroenabled.12": 227 case "application/vnd.ms-excel.sheet.macroenabled.12": 228 case "application/vnd.ms-excel.template.macroenabled.12": 229 case "application/vnd.ms-powerpoint": 230 case "application/vnd.ms-powerpoint.addin.macroenabled.12": 231 case "application/vnd.ms-powerpoint.presentation.macroenabled.12": 232 case "application/vnd.ms-powerpoint.slideshow.macroenabled.12": 233 case "application/vnd.ms-powerpoint.template.macroenabled.12": 234 case "application/vnd.ms-word.document.macroenabled.12": 235 case "application/vnd.ms-word.template.macroenabled.12": 236 case "application/vnd.oasis.opendocument.chart": 237 case "application/vnd.oasis.opendocument.database": 238 case "application/vnd.oasis.opendocument.formula": 239 case "application/vnd.oasis.opendocument.graphics": 240 case "application/vnd.oasis.opendocument.graphics-template": 241 case "application/vnd.oasis.opendocument.presentation": 242 case "application/vnd.oasis.opendocument.presentation-template": 243 case "application/vnd.oasis.opendocument.spreadsheet": 244 case "application/vnd.oasis.opendocument.spreadsheet-template": 245 case "application/vnd.oasis.opendocument.text": 246 case "application/vnd.oasis.opendocument.text-master": 247 case "application/vnd.oasis.opendocument.text-template": 248 case "application/vnd.oasis.opendocument.text-web": 249 case "application/vnd.openxmlformats-officedocument.presentationml.presentation": 250 case "application/vnd.openxmlformats-officedocument.presentationml.slideshow": 251 case "application/vnd.openxmlformats-officedocument.presentationml.template": 252 case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": 253 case "application/vnd.openxmlformats-officedocument.spreadsheetml.template": 254 case "application/vnd.openxmlformats-officedocument.wordprocessingml.document": 255 case "application/vnd.openxmlformats-officedocument.wordprocessingml.template": 256 case "application/vnd.stardivision.calc": 257 case "application/vnd.stardivision.chart": 258 case "application/vnd.stardivision.draw": 259 case "application/vnd.stardivision.impress": 260 case "application/vnd.stardivision.impress-packed": 261 case "application/vnd.stardivision.mail": 262 case "application/vnd.stardivision.math": 263 case "application/vnd.stardivision.writer": 264 case "application/vnd.stardivision.writer-global": 265 case "application/vnd.sun.xml.calc": 266 case "application/vnd.sun.xml.calc.template": 267 case "application/vnd.sun.xml.draw": 268 case "application/vnd.sun.xml.draw.template": 269 case "application/vnd.sun.xml.impress": 270 case "application/vnd.sun.xml.impress.template": 271 case "application/vnd.sun.xml.math": 272 case "application/vnd.sun.xml.writer": 273 case "application/vnd.sun.xml.writer.global": 274 case "application/vnd.sun.xml.writer.template": 275 case "application/x-mspublisher": 276 return true; 277 default: 278 return false; 279 } 280 } 281 isExifMimeType(@ullable String mimeType)282 public static boolean isExifMimeType(@Nullable String mimeType) { 283 // For simplicity, assume that all image files might have EXIF data 284 return isImageMimeType(mimeType); 285 } 286 isAudioMimeType(@ullable String mimeType)287 public static boolean isAudioMimeType(@Nullable String mimeType) { 288 return normalizeMimeType(mimeType).startsWith("audio/"); 289 } 290 isVideoMimeType(@ullable String mimeType)291 public static boolean isVideoMimeType(@Nullable String mimeType) { 292 return normalizeMimeType(mimeType).startsWith("video/"); 293 } 294 isImageMimeType(@ullable String mimeType)295 public static boolean isImageMimeType(@Nullable String mimeType) { 296 return normalizeMimeType(mimeType).startsWith("image/"); 297 } 298 isPlayListMimeType(@ullable String mimeType)299 public static boolean isPlayListMimeType(@Nullable String mimeType) { 300 switch (normalizeMimeType(mimeType)) { 301 case "application/vnd.ms-wpl": 302 case "audio/x-mpegurl": 303 case "audio/mpegurl": 304 case "application/x-mpegurl": 305 case "application/vnd.apple.mpegurl": 306 case "audio/x-scpls": 307 return true; 308 default: 309 return false; 310 } 311 } 312 isDrmMimeType(@ullable String mimeType)313 public static boolean isDrmMimeType(@Nullable String mimeType) { 314 return normalizeMimeType(mimeType).equals("application/x-android-drm-fl"); 315 } 316 317 // generates a title based on file name 318 @UnsupportedAppUsage getFileTitle(@onNull String path)319 public static @NonNull String getFileTitle(@NonNull String path) { 320 // extract file name after last slash 321 int lastSlash = path.lastIndexOf('/'); 322 if (lastSlash >= 0) { 323 lastSlash++; 324 if (lastSlash < path.length()) { 325 path = path.substring(lastSlash); 326 } 327 } 328 // truncate the file extension (if any) 329 int lastDot = path.lastIndexOf('.'); 330 if (lastDot > 0) { 331 path = path.substring(0, lastDot); 332 } 333 return path; 334 } 335 getFileExtension(@ullable String path)336 public static @Nullable String getFileExtension(@Nullable String path) { 337 if (path == null) { 338 return null; 339 } 340 int lastDot = path.lastIndexOf('.'); 341 if (lastDot >= 0) { 342 return path.substring(lastDot + 1); 343 } else { 344 return null; 345 } 346 } 347 348 /** @deprecated file types no longer exist */ 349 @Deprecated 350 @UnsupportedAppUsage getFileTypeForMimeType(String mimeType)351 public static int getFileTypeForMimeType(String mimeType) { 352 return 0; 353 } 354 355 /** 356 * Find the best MIME type for the given item. Prefers mappings from file 357 * extensions, since they're more accurate than format codes. 358 */ getMimeType(@ullable String path, int formatCode)359 public static @NonNull String getMimeType(@Nullable String path, int formatCode) { 360 // First look for extension mapping 361 String mimeType = getMimeTypeForFile(path); 362 if (!MIME_TYPE_DEFAULT.equals(mimeType)) { 363 return mimeType; 364 } 365 366 // Otherwise look for format mapping 367 return getMimeTypeForFormatCode(formatCode); 368 } 369 370 @UnsupportedAppUsage getMimeTypeForFile(@ullable String path)371 public static @NonNull String getMimeTypeForFile(@Nullable String path) { 372 String ext = getFileExtension(path); 373 final String mimeType = MimeMap.getDefault().guessMimeTypeFromExtension(ext); 374 return (mimeType != null) ? mimeType : MIME_TYPE_DEFAULT; 375 } 376 getMimeTypeForFormatCode(int formatCode)377 public static @NonNull String getMimeTypeForFormatCode(int formatCode) { 378 final String mimeType = sFormatToMimeTypeMap.get(formatCode); 379 return (mimeType != null) ? mimeType : MIME_TYPE_DEFAULT; 380 } 381 382 /** 383 * Find the best MTP format code mapping for the given item. Prefers 384 * mappings from MIME types, since they're more accurate than file 385 * extensions. 386 */ getFormatCode(@ullable String path, @Nullable String mimeType)387 public static int getFormatCode(@Nullable String path, @Nullable String mimeType) { 388 // First look for MIME type mapping 389 int formatCode = getFormatCodeForMimeType(mimeType); 390 if (formatCode != MtpConstants.FORMAT_UNDEFINED) { 391 return formatCode; 392 } 393 394 // Otherwise look for extension mapping 395 return getFormatCodeForFile(path); 396 } 397 getFormatCodeForFile(@ullable String path)398 public static int getFormatCodeForFile(@Nullable String path) { 399 return getFormatCodeForMimeType(getMimeTypeForFile(path)); 400 } 401 getFormatCodeForMimeType(@ullable String mimeType)402 public static int getFormatCodeForMimeType(@Nullable String mimeType) { 403 if (mimeType == null) { 404 return MtpConstants.FORMAT_UNDEFINED; 405 } 406 407 // First look for direct mapping 408 Integer value = sMimeTypeToFormatMap.get(mimeType); 409 if (value != null) { 410 return value.intValue(); 411 } 412 413 // Otherwise look for indirect mapping 414 mimeType = normalizeMimeType(mimeType); 415 value = sMimeTypeToFormatMap.get(mimeType); 416 if (value != null) { 417 return value.intValue(); 418 } else if (mimeType.startsWith("audio/")) { 419 return MtpConstants.FORMAT_UNDEFINED_AUDIO; 420 } else if (mimeType.startsWith("video/")) { 421 return MtpConstants.FORMAT_UNDEFINED_VIDEO; 422 } else if (mimeType.startsWith("image/")) { 423 return MtpConstants.FORMAT_DEFINED; 424 } else { 425 return MtpConstants.FORMAT_UNDEFINED; 426 } 427 } 428 429 /** 430 * Normalize the given MIME type by bouncing through a default file 431 * extension, if defined. This handles cases like "application/x-flac" to 432 * ".flac" to "audio/flac". 433 */ normalizeMimeType(@ullable String mimeType)434 private static @NonNull String normalizeMimeType(@Nullable String mimeType) { 435 MimeMap mimeMap = MimeMap.getDefault(); 436 final String extension = mimeMap.guessExtensionFromMimeType(mimeType); 437 if (extension != null) { 438 final String extensionMimeType = mimeMap.guessMimeTypeFromExtension(extension); 439 if (extensionMimeType != null) { 440 return extensionMimeType; 441 } 442 } 443 return (mimeType != null) ? mimeType : MIME_TYPE_DEFAULT; 444 } 445 } 446