diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 92fe98ca1044e..c014ef682a24e 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -83,6 +83,7 @@ import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; +import java.util.Locale; import java.util.Objects; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @@ -115,6 +116,9 @@ public final class FileUtils { private FileUtils() { } + private static final String CAMERA_DIR_LOWER_CASE = "/storage/emulated/" + UserHandle.myUserId() + + "/dcim/camera"; + /** Regular expression for safe filenames: no spaces or metacharacters. * * Use a preload holder so that FileUtils can be compile-time initialized. @@ -1432,18 +1436,27 @@ public final class FileUtils { } } + // TODO(b/170488060): Consider better approach /** {@hide} */ + @VisibleForTesting public static FileDescriptor convertToModernFd(FileDescriptor fd) { try { Context context = AppGlobals.getInitialApplication(); + File realFile = ParcelFileDescriptor.getFile(fd); + String fileName = realFile.getName(); + boolean isCameraVideo = !fileName.startsWith(".") && fileName.endsWith(".mp4") + && contains(CAMERA_DIR_LOWER_CASE, realFile.getAbsolutePath().toLowerCase( + Locale.ROOT)); + if (!SystemProperties.getBoolean("sys.fuse.transcode_enabled", false) - || UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) { - // If transcode is enabled we optimize by default, unless explicitly disabled. - // Never convert modern fd for MediaProvider, because this requires + || UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context) + || !isCameraVideo) { + // 1. If transcode is enabled we optimize by default, unless explicitly disabled. + // 2. Never convert modern fd for MediaProvider, because this requires // MediaStore#scanFile and can cause infinite loops when MediaProvider scans + // 3. Only convert published mp4 videos in the DCIM/Camera dir return null; } - File realFile = ParcelFileDescriptor.getFile(fd); Log.i(TAG, "Changing to modern format dataSource for: " + realFile); ContentResolver resolver = context.getContentResolver(); diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index 5bce227cdf487..1a86678a30b4c 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -16,6 +16,7 @@ package android.os; +import static android.os.FileUtils.convertToModernFd; import static android.os.FileUtils.roundStorageSize; import static android.os.FileUtils.translateModeAccessToPosix; import static android.os.FileUtils.translateModePfdToPosix; @@ -45,6 +46,8 @@ import static android.text.format.DateUtils.WEEK_IN_MILLIS; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -67,6 +70,7 @@ import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.Arrays; @@ -562,6 +566,55 @@ public class FileUtilsTest { assertEquals(O_RDWR, translateModeAccessToPosix(R_OK | W_OK | X_OK)); } + @Test + public void testConvertToModernFd() throws Exception { + final String nonce = String.valueOf(System.nanoTime()); + + final File cameraDir = new File("/storage/emulated/0/DCIM/Camera"); + final File nonCameraDir = new File("/storage/emulated/0/Pictures"); + cameraDir.mkdirs(); + nonCameraDir.mkdirs(); + + final File validVideoCameraDir = new File(cameraDir, "validVideo-" + nonce + ".mp4"); + final File validImageCameraDir = new File(cameraDir, "validImage-" + nonce + ".jpg"); + final File invalidVideoCameraDir = new File(cameraDir, ".invalidVideo-" + nonce + ".mp4"); + + final File validVideoNonCameraDir = new File(nonCameraDir, "validVideo-" + nonce + ".mp4"); + final File validImageNonCameraDir = new File(nonCameraDir, "validImage-" + nonce + ".jpg"); + + try { + FileDescriptor pfdValidVideoCameraDir = + ParcelFileDescriptor.open(validVideoCameraDir, + MODE_CREATE | MODE_READ_WRITE).getFileDescriptor(); + FileDescriptor pfdValidImageCameraDir = + ParcelFileDescriptor.open(validImageCameraDir, + MODE_CREATE | MODE_READ_WRITE).getFileDescriptor(); + FileDescriptor pfdInvalidVideoCameraDir = + ParcelFileDescriptor.open(invalidVideoCameraDir, + MODE_CREATE | MODE_READ_WRITE).getFileDescriptor(); + + FileDescriptor pfdValidVideoNonCameraDir = + ParcelFileDescriptor.open(validVideoNonCameraDir, + MODE_CREATE | MODE_READ_WRITE).getFileDescriptor(); + FileDescriptor pfdValidImageNonCameraDir = + ParcelFileDescriptor.open(validImageNonCameraDir, + MODE_CREATE | MODE_READ_WRITE).getFileDescriptor(); + + assertNotNull(convertToModernFd(pfdValidVideoCameraDir)); + + assertNull(convertToModernFd(pfdValidImageCameraDir)); + assertNull(convertToModernFd(pfdInvalidVideoCameraDir)); + assertNull(convertToModernFd(pfdValidVideoNonCameraDir)); + assertNull(convertToModernFd(pfdValidImageNonCameraDir)); + } finally { + validVideoCameraDir.delete(); + validImageCameraDir.delete(); + invalidVideoCameraDir.delete(); + validVideoNonCameraDir.delete(); + validImageNonCameraDir.delete(); + } + } + private static void assertTranslate(String string, int posix, int pfd) { assertEquals(posix, translateModeStringToPosix(string)); assertEquals(string, translateModePosixToString(posix));