Merge "Improve EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT docs"
diff --git a/src/com/android/providers/media/scan/ModernMediaScanner.java b/src/com/android/providers/media/scan/ModernMediaScanner.java
index 46c3516..173eb26 100644
--- a/src/com/android/providers/media/scan/ModernMediaScanner.java
+++ b/src/com/android/providers/media/scan/ModernMediaScanner.java
@@ -690,11 +690,8 @@
actualMimeType = mDrmClient.getOriginalMimeType(realFile.getPath());
}
- int actualMediaType = FileColumns.MEDIA_TYPE_NONE;
- if (actualMimeType != null) {
- actualMediaType = resolveMediaTypeFromFilePath(realFile, actualMimeType,
- /*isHidden*/ mHiddenDirCount > 0);
- }
+ int actualMediaType = mediaTypeFromMimeType(
+ realFile, actualMimeType, FileColumns.MEDIA_TYPE_NONE);
Trace.beginSection("checkChanged");
@@ -718,13 +715,9 @@
try (Cursor c = mResolver.query(mFilesUri, projection, queryArgs, mSignal)) {
if (c.moveToFirst()) {
existingId = c.getLong(0);
- final long dateModified = c.getLong(1);
- final long size = c.getLong(2);
final String mimeType = c.getString(3);
final int mediaType = c.getInt(4);
isPendingFromFuse &= c.getInt(5) != 0;
- final boolean isScanned =
- c.getInt(6) == FileColumns._MODIFIER_MEDIA_SCAN;
// Remember visiting this existing item, even if we skipped
// due to it being unchanged; this is needed so we don't
@@ -736,18 +729,36 @@
mFirstId = existingId;
}
- final boolean sameTime = (lastModifiedTime(realFile, attrs) == dateModified);
- final boolean sameSize = (attrs.size() == size);
- final boolean sameMimeType = mimeType == null ? actualMimeType == null :
- mimeType.equalsIgnoreCase(actualMimeType);
- final boolean sameMediaType = (actualMediaType == mediaType);
- final boolean isSame = sameTime && sameSize && sameMediaType && sameMimeType
- && !isPendingFromFuse && isScanned;
- if (attrs.isDirectory() || isSame) {
+ if (attrs.isDirectory()) {
+ if (LOGV) Log.v(TAG, "Skipping directory " + file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ final boolean sameMetadata =
+ hasSameMetadata(attrs, realFile, isPendingFromFuse, c);
+ if (isSame(
+ sameMetadata, actualMimeType, actualMediaType, mimeType, mediaType)) {
if (LOGV) Log.v(TAG, "Skipping unchanged " + file);
return FileVisitResult.CONTINUE;
}
+
+ // For this special case we may have changed mime type from the file's metadata.
+ // This is safe because mime_type cannot be changed outside of scanning.
+ if (sameMetadata
+ && "video/mp4".equalsIgnoreCase(actualMimeType)
+ && "audio/mp4".equalsIgnoreCase(mimeType)) {
+ if (LOGV) Log.v(TAG, "Skipping unchanged video/audio " + file);
+ return FileVisitResult.CONTINUE;
+ }
}
+
+ // Since we allow top-level mime type to be customised, we need to do this early
+ // on, so the file is later scanned as the appropriate type (otherwise, this
+ // audio filed would be scanned as video and it would be missing the correct
+ // metadata).
+ actualMimeType = updateM4aMimeType(realFile, actualMimeType);
+ actualMediaType =
+ mediaTypeFromMimeType(realFile, actualMimeType, actualMediaType);
} finally {
Trace.endSection();
}
@@ -777,6 +788,65 @@
return FileVisitResult.CONTINUE;
}
+ private boolean isSame(
+ boolean hasSameMetadata,
+ String actualMimeType,
+ int actualMediaType,
+ String mimeType,
+ int mediaType) {
+ boolean sameMimeType =
+ mimeType == null
+ ? actualMimeType == null
+ : mimeType.equalsIgnoreCase(actualMimeType);
+ boolean sameMediaType = (actualMediaType == mediaType);
+ return hasSameMetadata && sameMediaType && sameMimeType;
+ }
+
+ private int mediaTypeFromMimeType(
+ File file, String mimeType, int defaultMediaType) {
+ if (mimeType != null) {
+ return resolveMediaTypeFromFilePath(
+ file, mimeType, /*isHidden*/ mHiddenDirCount > 0);
+ }
+ return defaultMediaType;
+ }
+
+ private boolean hasSameMetadata(
+ BasicFileAttributes attrs, File realFile, boolean isPendingFromFuse, Cursor c) {
+ final long dateModified = c.getLong(1);
+ final boolean sameTime = (lastModifiedTime(realFile, attrs) == dateModified);
+
+ final long size = c.getLong(2);
+ final boolean sameSize = (attrs.size() == size);
+
+ final boolean isScanned =
+ c.getInt(6) == FileColumns._MODIFIER_MEDIA_SCAN;
+
+ return sameTime && sameSize && !isPendingFromFuse && isScanned;
+ }
+
+ /**
+ * For this one very narrow case, we allow mime types to be customised when the top levels
+ * differ. This opens the given file, so avoid calling unless really necessary. This
+ * returns the defaultMimeType for non-m4a files or if opening the file throws an exception.
+ */
+ private String updateM4aMimeType(File file, String defaultMimeType) {
+ if ("video/mp4".equalsIgnoreCase(defaultMimeType)) {
+ try (
+ FileInputStream is = new FileInputStream(file);
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
+ mmr.setDataSource(is.getFD());
+ String refinedMimeType = mmr.extractMetadata(METADATA_KEY_MIMETYPE);
+ if ("audio/mp4".equalsIgnoreCase(refinedMimeType)) {
+ return refinedMimeType;
+ }
+ } catch (Exception e) {
+ return defaultMimeType;
+ }
+ }
+ return defaultMimeType;
+ }
+
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc)
throws IOException {
@@ -1537,12 +1607,6 @@
if (fileMimeType.regionMatches(true, 0, refinedMimeType, 0, refinedSplit + 1)) {
return Optional.of(refinedMimeType);
- } else if ("video/mp4".equalsIgnoreCase(fileMimeType)
- && "audio/mp4".equalsIgnoreCase(refinedMimeType)) {
- // We normally only allow MIME types to be customized when the
- // top-level type agrees, but this one very narrow case is added to
- // support a music service that was writing "m4a" files as "mp4".
- return Optional.of(refinedMimeType);
} else {
return Optional.empty();
}
diff --git a/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java b/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
index 6102464..753547b 100644
--- a/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
@@ -146,21 +146,10 @@
assertTrue(parseOptionalMimeType("image/png", "image/x-shiny").isPresent());
assertEquals("image/x-shiny",
parseOptionalMimeType("image/png", "image/x-shiny").get());
- }
- @Test
- public void testOverrideMimeType_148316354() throws Exception {
// Radical file type shifting isn't allowed
assertEquals(Optional.empty(),
parseOptionalMimeType("video/mp4", "audio/mpeg"));
-
- // One specific narrow type of shift (mp4 -> m4a) is allowed
- assertEquals(Optional.of("audio/mp4"),
- parseOptionalMimeType("video/mp4", "audio/mp4"));
-
- // The other direction isn't allowed
- assertEquals(Optional.empty(),
- parseOptionalMimeType("audio/mp4", "video/mp4"));
}
@Test
@@ -900,8 +889,11 @@
.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, null)) {
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
- assertEquals("audio/mp4",
- cursor.getString(cursor.getColumnIndex(MediaColumns.MIME_TYPE)));
+ assertThat(cursor.getString(cursor.getColumnIndex(MediaColumns.MIME_TYPE)))
+ .isEqualTo("audio/mp4");
+ assertThat(cursor.getString(cursor.getColumnIndex(AudioColumns.IS_MUSIC)))
+ .isEqualTo("1");
+
}
}