Don't read more than 4096 chars in FileUtils#readString

FileUtils#readString() is used to read directory path from .nomedia
file. As most of the filesystems have upper limit of 4096 chars for path
of the file, it's safe to read only 4096 bytes from the file. Adding
upper limit for readString will help in avoiding OutOfMemoryError while
reading a large renamed .nomedia file.

FileUtils#readString() is used for reading the directory path from
.nomedia file to decide if it was already scanned before.
When .nomedia file size is greater than 4096 bytes, we will not read
directory path from the .nomedia file. Instead, we will treat this file
as empty .nomedia file, and continue scanning the parent directory as
hidden directory.

Bug: 174333283
Test: atest
com.android.providers.media.util.FileUtilsTest#testString

Change-Id: Iebcb8ee0be65867dd98f3edffe3c0a0effdc9c4c
Merged-In: Iebcb8ee0be65867dd98f3edffe3c0a0effdc9c4c
(cherry picked from commit e64f5bbb85ed413e111061e8e80722989bafed4e)
diff --git a/src/com/android/providers/media/util/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java
index e0e3dca..e535bfc 100644
--- a/src/com/android/providers/media/util/FileUtils.java
+++ b/src/com/android/providers/media/util/FileUtils.java
@@ -389,18 +389,31 @@
         }
     }
 
+    private static final int MAX_READ_STRING_SIZE = 4096;
+
     /**
      * Read given {@link File} as a single {@link String}. Returns
-     * {@link Optional#empty()} when the file doesn't exist.
+     * {@link Optional#empty()} when
+     * <ul>
+     * <li> the file doesn't exist or
+     * <li> the size of the file exceeds {@code MAX_READ_STRING_SIZE}
+     * </ul>
      */
     public static @NonNull Optional<String> readString(@NonNull File file) throws IOException {
         try {
-            final String value = new String(Files.readAllBytes(file.toPath()),
-                    StandardCharsets.UTF_8);
-            return Optional.of(value);
-        } catch (NoSuchFileException e) {
-            return Optional.empty();
+            if (file.length() <= MAX_READ_STRING_SIZE) {
+                final String value = new String(Files.readAllBytes(file.toPath()),
+                        StandardCharsets.UTF_8);
+                return Optional.of(value);
+            }
+            // When file size exceeds MAX_READ_STRING_SIZE, file is either
+            // corrupted or doesn't the contain expected data. Hence we return
+            // Optional.empty() which will be interpreted as empty file.
+            Logging.logPersistent(String.format("Ignored reading %s, file size exceeds %d", file,
+                    MAX_READ_STRING_SIZE));
+        } catch (NoSuchFileException ignored) {
         }
+        return Optional.empty();
     }
 
     /**
diff --git a/tests/src/com/android/providers/media/util/FileUtilsTest.java b/tests/src/com/android/providers/media/util/FileUtilsTest.java
index 03ab070..7f2b4a8 100644
--- a/tests/src/com/android/providers/media/util/FileUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/FileUtilsTest.java
@@ -75,6 +75,7 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Locale;
@@ -90,6 +91,7 @@
     private static final String NONCE = String.valueOf(System.nanoTime());
 
     private static final String TEST_DIRECTORY_NAME = "FileUtilsTestDirectory" + NONCE;
+    private static final String TEST_FILE_NAME = "FileUtilsTestFile" + NONCE;
 
     private File mTarget;
     private File mDcimTarget;
@@ -140,6 +142,17 @@
         // Verify empty writing deletes file
         FileUtils.writeString(file, Optional.empty());
         assertFalse(FileUtils.readString(file).isPresent());
+
+        // Verify reading from a file with more than 4096 chars
+        try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
+            raf.setLength(4097);
+        }
+        assertEquals(Optional.empty(), FileUtils.readString(file));
+
+        // Verify reading from non existing file.
+        file.delete();
+        assertEquals(Optional.empty(), FileUtils.readString(file));
+
     }
 
     @Test