Merge "Make CTS more opinionated about the platform's default MimeMap." am: a8bfed9104 am: ac8a5625d2 am: 7e2ac2ea09
am: 98fdb1d810

Change-Id: I5eb0bd3cf5fd50d180c8da61a375f3faa0737164
diff --git a/mime/Android.bp b/mime/Android.bp
index 0ae94d4..51290f6 100644
--- a/mime/Android.bp
+++ b/mime/Android.bp
@@ -12,24 +12,84 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+
+java_defaults {
+    name: "mimemap-defaults",
+    srcs: [
+        "java/android/content/type/DefaultMimeMapFactory.java",
+    ],
+    sdk_version: "core_platform",
+}
+
 java_library {
     name: "mimemap",
+    defaults: ["mimemap-defaults"],
+    static_libs: ["mimemap-res.jar"],
+    visibility: [
+        "//frameworks/base:__subpackages__",
+    ],
+}
+
+java_library {
+    name: "mimemap-testing",
+    defaults: ["mimemap-defaults"],
+    static_libs: ["mimemap-testing-res.jar"],
+    jarjar_rules: "jarjar-rules.txt",
     visibility: [
         "//cts/tests/tests/mimemap:__subpackages__",
         "//frameworks/base:__subpackages__",
     ],
+}
 
-    srcs: [
-        "java/android/content/type/DefaultMimeMapFactory.java",
+// The mimemap-res.jar and mimemap-testing-res.jar genrules produce a .jar that
+// has the resource file in a subdirectory res/ and testres/, respectively.
+// Those paths need to They need to be in different paths because one of them
+// ends up on a bootclasspath jar whereas the other one ends up in a test jar.
+// Bootclasspath resources hide test or application resources under the same
+// path because ClassLoader.getResource(String) consults the parent ClassLoader
+// first.
+//
+// Further notes:
+//  - the "cp" command will flatten any directory paths that occur in $(in),
+//    but here they happen to already be in the root directory. If we needed
+//    to preserve sub paths then we might want to zip the files first and then
+//    unzip them below the new parent directory.
+//  - the path names "res/" and "testres/" and duplicated in .java source files
+//    (DefaultMimeMapFactory.java and MimeMapTest.java, as of October 2019).
+java_genrule {
+    name: "mimemap-res.jar",
+    tools: [
+        "soong_zip",
     ],
+    srcs: [":mime.types"],
+    out: ["mimemap-res.jar"],
+    cmd: "mkdir $(genDir)/res/ && cp $(in) $(genDir)/res/ && $(location soong_zip) -C $(genDir) -o $(out) -D $(genDir)/res/",
+}
 
-    java_resources: [
+// The same as mimemap-res.jar except that the resources are placed in a different directory.
+// They get bundled with CTS so that CTS can compare a device's MimeMap implementation vs.
+// the stock Android one from when CTS was built.
+java_genrule {
+    name: "mimemap-testing-res.jar",
+    tools: [
+        "soong_zip",
+    ],
+    srcs: [":mime.types"],
+    out: ["mimemap-testing-res.jar"],
+    cmd: "mkdir $(genDir)/testres/ && cp $(in) $(genDir)/testres/ && $(location soong_zip) -C $(genDir) -o $(out) -D $(genDir)/testres/",
+}
+
+// Combination of all *mime.types resources.
+filegroup {
+    name: "mime.types",
+    visibility: [
+        "//visibility:private",
+    ],
+    srcs: [
         ":debian.mime.types",
         ":android.mime.types",
         ":vendor.mime.types",
     ],
-
-    sdk_version: "core_platform",
 }
 
 filegroup {
diff --git a/mime/jarjar-rules.txt b/mime/jarjar-rules.txt
new file mode 100644
index 0000000..145d1db
--- /dev/null
+++ b/mime/jarjar-rules.txt
@@ -0,0 +1 @@
+rule android.content.type.DefaultMimeMapFactory android.content.type.cts.StockAndroidMimeMapFactory
\ No newline at end of file
diff --git a/mime/java/android/content/type/DefaultMimeMapFactory.java b/mime/java/android/content/type/DefaultMimeMapFactory.java
index 56b234f..13039a4 100644
--- a/mime/java/android/content/type/DefaultMimeMapFactory.java
+++ b/mime/java/android/content/type/DefaultMimeMapFactory.java
@@ -20,10 +20,13 @@
 
 import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
 import java.util.regex.Pattern;
 
 /**
@@ -45,21 +48,33 @@
      * Android's default mapping between MIME types and extensions.
      */
     public static MimeMap create() {
-        MimeMap.Builder builder = MimeMap.builder();
-        parseTypes(builder, true, "/mime.types");
-        parseTypes(builder, true, "/android.mime.types");
-        parseTypes(builder, false, "/vendor.mime.types");
-        return builder.build();
+        Class c = DefaultMimeMapFactory.class;
+        // The resources are placed into the res/ path by the "mimemap-res.jar" genrule.
+        return create(resourceName -> c.getResourceAsStream("/res/" + resourceName));
     }
 
     private static final Pattern SPLIT_PATTERN = Pattern.compile("\\s+");
 
+    /**
+     * Creates a {@link MimeMap} instance whose resources are loaded from the
+     * InputStreams looked up in {@code resourceSupplier}.
+     *
+     * @hide
+     */
+    public static MimeMap create(Function<String, InputStream> resourceSupplier) {
+        MimeMap.Builder builder = MimeMap.builder();
+        parseTypes(builder, true, resourceSupplier, "mime.types");
+        parseTypes(builder, true, resourceSupplier, "android.mime.types");
+        parseTypes(builder, false, resourceSupplier, "vendor.mime.types");
+        return builder.build();
+    }
+
     private static void parseTypes(MimeMap.Builder builder, boolean allowOverwrite,
-            String resource) {
-        try (BufferedReader r = new BufferedReader(
-                new InputStreamReader(DefaultMimeMapFactory.class.getResourceAsStream(resource)))) {
+            Function<String, InputStream> resourceSupplier, String resourceName) {
+        try (InputStream inputStream = Objects.requireNonNull(resourceSupplier.apply(resourceName));
+             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
             String line;
-            while ((line = r.readLine()) != null) {
+            while ((line = reader.readLine()) != null) {
                 int commentPos = line.indexOf('#');
                 if (commentPos >= 0) {
                     line = line.substring(0, commentPos);
@@ -78,7 +93,7 @@
                 builder.put(specs.get(0), specs.subList(1, specs.size()));
             }
         } catch (IOException | RuntimeException e) {
-            throw new RuntimeException("Failed to parse " + resource, e);
+            throw new RuntimeException("Failed to parse " + resourceName, e);
         }
     }