Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 88ddd19f authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge changes from topic "nov29" into main

* changes:
  `LruCache` under Ravenwood.
  `FileUtils` and `AtomicFile` under Ravenwood.
parents aa41025a 7c1df35f
Loading
Loading
Loading
Loading
+46 −9
Original line number Diff line number Diff line
@@ -52,9 +52,11 @@ import android.provider.DocumentsContract.Document;
import android.provider.MediaStore;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
import android.text.TextUtils;
import android.util.DataUnit;
import android.util.EmptyArray;
import android.util.Log;
import android.util.Slog;
import android.webkit.MimeTypeMap;
@@ -64,7 +66,6 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.SizedInputStream;

import libcore.io.IoUtils;
import libcore.util.EmptyArray;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
@@ -94,6 +95,7 @@ import java.util.zip.CheckedInputStream;
/**
 * Utility methods useful for working with files.
 */
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FileUtils {
    private static final String TAG = "FileUtils";

@@ -116,9 +118,6 @@ 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.
@@ -133,6 +132,21 @@ public final class FileUtils {

    private static final long COPY_CHECKPOINT_BYTES = 524288;

    static {
        sEnableCopyOptimizations = shouldEnableCopyOptimizations();
    }

    @android.ravenwood.annotation.RavenwoodReplace
    private static boolean shouldEnableCopyOptimizations() {
        // Advanced copy operations enabled by default
        return true;
    }

    private static boolean shouldEnableCopyOptimizations$ravenwood() {
        // Disabled under Ravenwood due to missing kernel support
        return false;
    }

    /**
     * Listener that is called periodically as progress is made.
     */
@@ -150,6 +164,7 @@ public final class FileUtils {
     * @hide
     */
    @UnsupportedAppUsage
    @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
    public static int setPermissions(File path, int mode, int uid, int gid) {
        return setPermissions(path.getAbsolutePath(), mode, uid, gid);
    }
@@ -164,6 +179,7 @@ public final class FileUtils {
     * @hide
     */
    @UnsupportedAppUsage
    @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
    public static int setPermissions(String path, int mode, int uid, int gid) {
        try {
            Os.chmod(path, mode);
@@ -194,6 +210,7 @@ public final class FileUtils {
     * @hide
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
    public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) {
        try {
            Os.fchmod(fd, mode);
@@ -221,6 +238,7 @@ public final class FileUtils {
     * @param to File where attributes should be copied to.
     * @hide
     */
    @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
    public static void copyPermissions(@NonNull File from, @NonNull File to) throws IOException {
        try {
            final StructStat stat = Os.stat(from.getAbsolutePath());
@@ -236,6 +254,7 @@ public final class FileUtils {
     * @hide
     */
    @Deprecated
    @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
    public static int getUid(String path) {
        try {
            return Os.stat(path).st_uid;
@@ -314,11 +333,7 @@ public final class FileUtils {
        }
        try (FileOutputStream out = new FileOutputStream(destFile)) {
            copy(in, out);
            try {
                Os.fsync(out.getFD());
            } catch (ErrnoException e) {
                throw e.rethrowAsIOException();
            }
            sync(out);
        }
    }

@@ -475,6 +490,7 @@ public final class FileUtils {
     * @hide
     */
    @VisibleForTesting
    @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
    public static long copyInternalSplice(FileDescriptor in, FileDescriptor out, long count,
            CancellationSignal signal, Executor executor, ProgressListener listener)
            throws ErrnoException {
@@ -516,6 +532,7 @@ public final class FileUtils {
     * @hide
     */
    @VisibleForTesting
    @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
    public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out, long count,
            CancellationSignal signal, Executor executor, ProgressListener listener)
            throws ErrnoException {
@@ -699,6 +716,7 @@ public final class FileUtils {
     *
     * @hide
     */
    @android.ravenwood.annotation.RavenwoodReplace
    public static void bytesToFile(String filename, byte[] content) throws IOException {
        if (filename.startsWith("/proc/")) {
            final int oldMask = StrictMode.allowThreadDiskWritesMask();
@@ -714,6 +732,14 @@ public final class FileUtils {
        }
    }

    /** @hide */
    public static void bytesToFile$ravenwood(String filename, byte[] content) throws IOException {
        // No StrictMode support, so we can just directly write
        try (FileOutputStream fos = new FileOutputStream(filename)) {
            fos.write(content);
        }
    }

    /**
     * Writes string to file. Basically same as "echo -n $string > $filename"
     *
@@ -1176,6 +1202,7 @@ public final class FileUtils {
     *
     * @hide
     */
    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = MimeTypeMap.class)
    public static String[] splitFileName(String mimeType, String displayName) {
        String name;
        String ext;
@@ -1423,11 +1450,13 @@ public final class FileUtils {
     *   indicate a failure to flush bytes to the underlying resource.
     */
    @Deprecated
    @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires ART support")
    public static void closeQuietly(@Nullable FileDescriptor fd) {
        IoUtils.closeQuietly(fd);
    }

    /** {@hide} */
    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
    public static int translateModeStringToPosix(String mode) {
        // Quick check for invalid chars
        for (int i = 0; i < mode.length(); i++) {
@@ -1462,6 +1491,7 @@ public final class FileUtils {
    }

    /** {@hide} */
    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
    public static String translateModePosixToString(int mode) {
        String res = "";
        if ((mode & O_ACCMODE) == O_RDWR) {
@@ -1483,6 +1513,7 @@ public final class FileUtils {
    }

    /** {@hide} */
    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
    public static int translateModePosixToPfd(int mode) {
        int res = 0;
        if ((mode & O_ACCMODE) == O_RDWR) {
@@ -1507,6 +1538,7 @@ public final class FileUtils {
    }

    /** {@hide} */
    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
    public static int translateModePfdToPosix(int mode) {
        int res = 0;
        if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
@@ -1531,6 +1563,7 @@ public final class FileUtils {
    }

    /** {@hide} */
    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
    public static int translateModeAccessToPosix(int mode) {
        if (mode == F_OK) {
            // There's not an exact mapping, so we attempt a read-only open to
@@ -1549,6 +1582,7 @@ public final class FileUtils {

    /** {@hide} */
    @VisibleForTesting
    @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
    public static ParcelFileDescriptor convertToModernFd(FileDescriptor fd) {
        Context context = AppGlobals.getInitialApplication();
        if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
@@ -1565,6 +1599,7 @@ public final class FileUtils {
        }
    }

    @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
    private static int getMediaProviderAppId(Context context) {
        if (sMediaProviderAppId != -1) {
            return sMediaProviderAppId;
@@ -1605,10 +1640,12 @@ public final class FileUtils {
            return this;
        }

        @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
        public static MemoryPipe createSource(byte[] data) throws IOException {
            return new MemoryPipe(data, false).startInternal();
        }

        @android.ravenwood.annotation.RavenwoodThrow(reason = "Requires kernel support")
        public static MemoryPipe createSink(byte[] data) throws IOException {
            return new MemoryPipe(data, true).startInternal();
        }
+3 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import java.util.function.Consumer;
 * be accessed or modified concurrently by multiple threads or processes. The caller is responsible
 * for ensuring appropriate mutual exclusion invariants whenever it accesses the file.
 */
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AtomicFile {
    private static final String LOG_TAG = "AtomicFile";

@@ -68,6 +69,8 @@ public class AtomicFile {
     * @hide Internal constructor that also allows you to have the class
     * automatically log commit events.
     */
    @android.ravenwood.annotation.RavenwoodThrow(blockedBy =
            SystemConfigFileCommitEventLogger.class)
    public AtomicFile(File baseName, String commitTag) {
        this(baseName, new SystemConfigFileCommitEventLogger(commitTag));
    }
+13 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.util;

import android.compat.annotation.UnsupportedAppUsage;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

@@ -61,6 +62,7 @@ import java.util.Map;
 * of <a href="http://developer.android.com/sdk/compatibility-library.html">Android's
 * Support Package</a> for earlier releases.
 */
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LruCache<K, V> {
    @UnsupportedAppUsage
    private final LinkedHashMap<K, V> map;
@@ -208,7 +210,7 @@ public class LruCache<K, V> {
                    break;
                }

                Map.Entry<K, V> toEvict = map.eldest();
                Map.Entry<K, V> toEvict = eldest();
                if (toEvict == null) {
                    break;
                }
@@ -224,6 +226,16 @@ public class LruCache<K, V> {
        }
    }

    @android.ravenwood.annotation.RavenwoodReplace
    private Map.Entry<K, V> eldest() {
        return map.eldest();
    }

    private Map.Entry<K, V> eldest$ravenwood() {
        final Iterator<Map.Entry<K, V>> it = map.entrySet().iterator();
        return it.hasNext() ? it.next() : null;
    }

    /**
     * Removes the entry for {@code key} if it exists.
     *
+13 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ android_test {
        "testables",
        "com.android.text.flags-aconfig-java",
        "flag-junit",
        "ravenwood-junit",
    ],

    libs: [
@@ -163,3 +164,15 @@ android_library {
        "framework-res",
    ],
}

android_ravenwood_test {
    name: "FrameworksCoreTestsRavenwood",
    static_libs: [
        "androidx.annotation_annotation",
        "androidx.test.rules",
    ],
    srcs: [
        "src/android/os/FileUtilsTest.java",
    ],
    auto_gen_config: true,
}
+40 −14
Original line number Diff line number Diff line
@@ -51,20 +51,17 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import android.content.Context;
import android.os.FileUtils.MemoryPipe;
import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import android.provider.DocumentsContract.Document;
import android.util.DataUnit;

import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;

import com.google.android.collect.Sets;

import libcore.io.Streams;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@@ -74,12 +71,19 @@ import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;

@RunWith(AndroidJUnit4.class)
public class FileUtilsTest {
    @Rule
    public final RavenwoodRule mRavenwood = new RavenwoodRule();

    private static final String TEST_DATA =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

@@ -90,17 +94,13 @@ public class FileUtilsTest {

    private final int[] DATA_SIZES = { 32, 32_000, 32_000_000 };

    private Context getContext() {
        return InstrumentationRegistry.getContext();
    }

    @Before
    public void setUp() throws Exception {
        mDir = getContext().getDir("testing", Context.MODE_PRIVATE);
        mDir = Files.createTempDirectory("FileUtils").toFile();
        mTestFile = new File(mDir, "test.file");
        mCopyFile = new File(mDir, "copy.file");

        mTarget = getContext().getFilesDir();
        mTarget = mDir;
        FileUtils.deleteContents(mTarget);
    }

@@ -152,6 +152,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
    public void testCopy_FileToPipe() throws Exception {
        for (int size : DATA_SIZES) {
            final File src = new File(mTarget, "src");
@@ -172,6 +173,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
    public void testCopy_PipeToFile() throws Exception {
        for (int size : DATA_SIZES) {
            final File dest = new File(mTarget, "dest");
@@ -191,6 +193,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
    public void testCopy_PipeToPipe() throws Exception {
        for (int size : DATA_SIZES) {
            byte[] expected = new byte[size];
@@ -208,6 +211,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
    public void testCopy_ShortPipeToFile() throws Exception {
        byte[] source = new byte[33_000_000];
        new Random().nextBytes(source);
@@ -424,6 +428,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
    public void testBuildUniqueFile_normal() throws Exception {
        assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test"));
        assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
@@ -443,6 +448,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
    public void testBuildUniqueFile_unknown() throws Exception {
        assertNameEquals("test",
                FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test"));
@@ -456,6 +462,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
    public void testBuildUniqueFile_dir() throws Exception {
        assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
        new File(mTarget, "test").mkdir();
@@ -470,6 +477,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
    public void testBuildUniqueFile_increment() throws Exception {
        assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
        new File(mTarget, "test.jpg").createNewFile();
@@ -489,6 +497,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
    public void testBuildUniqueFile_mimeless() throws Exception {
        assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "test.jpg"));
        new File(mTarget, "test.jpg").createNewFile();
@@ -584,6 +593,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(reason = "Requires kernel support")
    public void testTranslateMode() throws Exception {
        assertTranslate("r", O_RDONLY, MODE_READ_ONLY);

@@ -603,6 +613,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(reason = "Requires kernel support")
    public void testMalformedTransate_int() throws Exception {
        try {
            // The non-standard Linux access mode 3 should throw
@@ -614,6 +625,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(reason = "Requires kernel support")
    public void testMalformedTransate_string() throws Exception {
        try {
            // The non-standard Linux access mode 3 should throw
@@ -625,6 +637,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(reason = "Requires kernel support")
    public void testTranslateMode_Invalid() throws Exception {
        try {
            translateModeStringToPosix("rwx");
@@ -639,6 +652,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(reason = "Requires kernel support")
    public void testTranslateMode_Access() throws Exception {
        assertEquals(O_RDONLY, translateModeAccessToPosix(F_OK));
        assertEquals(O_RDONLY, translateModeAccessToPosix(R_OK));
@@ -648,6 +662,7 @@ public class FileUtilsTest {
    }

    @Test
    @IgnoreUnderRavenwood(reason = "Requires kernel support")
    public void testConvertToModernFd() throws Exception {
        final String nonce = String.valueOf(System.nanoTime());

@@ -720,13 +735,24 @@ public class FileUtilsTest {
    private byte[] readFile(File file) throws Exception {
        try (FileInputStream in = new FileInputStream(file);
                ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            Streams.copy(in, out);
            copy(in, out);
            return out.toByteArray();
        }
    }

    private static int copy(InputStream in, OutputStream out) throws IOException {
        int total = 0;
        byte[] buffer = new byte[8192];
        int c;
        while ((c = in.read(buffer)) != -1) {
            total += c;
            out.write(buffer, 0, c);
        }
        return total;
    }

    private void assertDirContents(String... expected) {
        final HashSet<String> expectedSet = Sets.newHashSet(expected);
        final HashSet<String> expectedSet = new HashSet<>(Arrays.asList(expected));
        String[] actual = mDir.list();
        if (actual == null) actual = new String[0];

Loading