Add device side mainline module detection

Bug: 142286159
Test: make sts
Test: make cts/apps/MainlineModuleDetector
Test: install MainlineModule Detector and trigger intent
Test: make cts
Test: run cts with the detection logic temporarily applied
Change-Id: Ib0cd61b007274a7ab18ac0be035c1e9a332392bd
diff --git a/apps/MainlineModuleDetector/Android.mk b/apps/MainlineModuleDetector/Android.mk
index b99f8f7..5b8e316 100644
--- a/apps/MainlineModuleDetector/Android.mk
+++ b/apps/MainlineModuleDetector/Android.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2018 The Android Open Source Project
+# Copyright (C) 2019 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := MainlineModuleDetector
diff --git a/apps/MainlineModuleDetector/AndroidManifest.xml b/apps/MainlineModuleDetector/AndroidManifest.xml
index 9a36cc6..4cc8f8c 100644
--- a/apps/MainlineModuleDetector/AndroidManifest.xml
+++ b/apps/MainlineModuleDetector/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 
-<!-- Copyright (C) 2018 The Android Open Source Project
+<!-- Copyright (C) 2019 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
diff --git a/apps/MainlineModuleDetector/src/com/android/cts/mainlinemoduledetector/MainlineModuleDetector.java b/apps/MainlineModuleDetector/src/com/android/cts/mainlinemoduledetector/MainlineModuleDetector.java
index 5e473d6..01c02c7 100644
--- a/apps/MainlineModuleDetector/src/com/android/cts/mainlinemoduledetector/MainlineModuleDetector.java
+++ b/apps/MainlineModuleDetector/src/com/android/cts/mainlinemoduledetector/MainlineModuleDetector.java
@@ -1,14 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package com.android.cts.mainlinemoduledetector;
 
 import android.app.Activity;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.os.Build;
 import android.os.Bundle;
 import android.util.Log;
 
-import java.security.MessageDigest;
-import java.util.EnumSet;
+import com.android.compatibility.common.util.mainline.MainlineModule;
+import com.android.compatibility.common.util.mainline.ModuleDetector;
+
 import java.util.HashSet;
 import java.util.Set;
 
@@ -16,143 +30,20 @@
 
     private static final String LOG_TAG = "MainlineModuleDetector";
 
-    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
-
-    enum ModuleType {
-        APEX,
-        APK
-    }
-
-    enum MainlineModule {
-        // Security
-        MEDIA_SOFTWARE_CODEC("com.google.android.media.swcodec",
-                true, ModuleType.APEX,
-                "0C:2B:13:87:6D:E5:6A:E6:4E:D1:DE:93:42:2A:8A:3F:EA:6F:34:C0:FC:5D:7D:A1:BD:CF:EF"
-                        + ":C1:A7:B7:C9:1D"),
-        MEDIA("com.google.android.media",
-                true, ModuleType.APEX,
-                "16:C1:5C:FA:15:D0:FD:D0:7E:BE:CB:5A:76:6B:40:8B:05:DD:92:7E:1F:3A:DD:C5:AB:F6:8E"
-                        + ":E8:B9:98:F9:FD"),
-        DNS_RESOLVER("com.google.android.resolv",
-                true, ModuleType.APEX,
-                "EC:82:21:76:5E:4F:7E:2C:6D:8D:0F:0C:E9:BD:82:5B:98:BE:D2:0C:07:2C:C6:C8:08:DD:E4"
-                        + ":68:5F:EB:A6:FF"),
-        CONSCRYPT("com.google.android.conscrypt",
-                true, ModuleType.APEX,
-                "8C:5D:A9:10:E6:11:21:B9:D6:E0:3B:42:D3:20:6A:7D:AD:29:DD:C1:63:AE:CD:4B:8E:E9:3F"
-                        + ":D3:83:79:CA:2A"),
-        // Privacy
-        PERMISSION_CONTROLLER("com.google.android.permissioncontroller",
-                false, ModuleType.APK,
-                "89:DF:B5:04:7E:E0:19:29:C2:18:4D:68:EF:49:64:F2:A9:0A:F1:24:C3:23:38:28:B8:F6:40"
-                        + ":D9:E6:C0:0F:83"),
-        ANDROID_SERVICES("com.google.android.ext.services",
-                false, ModuleType.APK,
-                "18:46:05:09:5B:E6:CA:22:D0:55:F3:4E:FA:F0:13:44:FD:3A:B3:B5:63:8C:30:62:76:10:EE"
-                        + ":AE:8A:26:0B:29"),
-        DOCUMENTS_UI("com.google.android.documentsui",
-                true, ModuleType.APK,
-                "9A:4B:85:34:44:86:EC:F5:1F:F8:05:EB:9D:23:17:97:79:BE:B7:EC:81:91:93:5A:CA:67:F0"
-                        + ":F4:09:02:52:97"),
-        // Consistency
-        TZDATA("com.google.android.tzdata",
-                true, ModuleType.APEX,
-                "55:93:DD:78:CB:26:EC:9B:00:59:2A:6A:F5:94:E4:16:1F:FD:B5:E9:F3:71:A7:43:54:5F:93"
-                        + ":F2:A0:F6:53:89"),
-        NETWORK_STACK("com.google.android.networkstack",
-                true, ModuleType.APK,
-                "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
-                        + ":F6:0B:F6:2C:1E"),
-        CAPTIVE_PORTAL_LOGIN("com.google.android.captiveportallogin",
-                true, ModuleType.APK,
-                "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
-                        + ":F6:0B:F6:2C:1E"),
-        NETWORK_PERMISSION_CONFIGURATION("com.google.android.networkstack.permissionconfig",
-                true, ModuleType.APK,
-                "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
-                        + ":F6:0B:F6:2C:1E"),
-        MODULE_METADATA("com.google.android.modulemetadata",
-                true, ModuleType.APK,
-                "BF:62:23:1E:28:F0:85:42:75:5C:F3:3C:9D:D8:3C:5D:1D:0F:A3:20:64:50:EF:BC:4C:3F:F3"
-                        + ":D5:FD:A0:33:0F"),
-        ;
-
-        String packageName;
-        boolean isPlayUpdated;
-        ModuleType moduleType;
-        String certSHA256;
-
-        MainlineModule(String packageName, boolean isPlayUpdated, ModuleType moduleType,
-                String certSHA256) {
-            this.packageName = packageName;
-            this.isPlayUpdated = isPlayUpdated;
-            this.moduleType = moduleType;
-            this.certSHA256 = certSHA256;
-        }
-    }
-
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         try {
-            String modules = String.join(",", getPlayManagedModules());
-            Log.i(LOG_TAG, "Play managed modules are: <" + modules + ">");
+            PackageManager pm = getApplicationContext().getPackageManager();
+            Set<MainlineModule> modules = ModuleDetector.getPlayManagedModules(pm);
+            Set<String> moduleNames = new HashSet<>();
+            for (MainlineModule module : modules) {
+                moduleNames.add(module.packageName);
+            }
+            Log.i(LOG_TAG, "Play managed modules are: <" + String.join(",", moduleNames) + ">");
         } catch (Exception e) {
             Log.e(LOG_TAG, "Failed to retrieve modules.", e);
         }
         this.finish();
     }
-
-    private Set<String> getPlayManagedModules() throws Exception {
-        Set<String> playManagedModules = new HashSet<>();
-
-        PackageManager pm = getApplicationContext().getPackageManager();
-
-        Set<String> packages = new HashSet<>();
-        for (PackageInfo info : pm.getInstalledPackages(0)) {
-            packages.add(info.packageName);
-        }
-        for (PackageInfo info : pm.getInstalledPackages(PackageManager.MATCH_APEX)) {
-            packages.add(info.packageName);
-        }
-
-        for (MainlineModule module : EnumSet.allOf(MainlineModule.class)) {
-            if (module.isPlayUpdated && packages.contains(module.packageName)
-                    && module.certSHA256.equals(getSignatureDigest(module))) {
-                playManagedModules.add(module.packageName);
-            }
-        }
-        return playManagedModules;
-    }
-
-    private String getSignatureDigest(MainlineModule module) throws Exception {
-        PackageManager pm = getApplicationContext().getPackageManager();
-        int flag = PackageManager.GET_SIGNING_CERTIFICATES;
-        if (module.moduleType == ModuleType.APEX) {
-            flag |= PackageManager.MATCH_APEX;
-        }
-
-        PackageInfo packageInfo = pm.getPackageInfo(module.packageName, flag);
-        MessageDigest messageDigest = MessageDigest.getInstance("SHA256");
-        messageDigest.update(packageInfo.signingInfo.getApkContentsSigners()[0].toByteArray());
-
-        final byte[] digest = messageDigest.digest();
-        final int digestLength = digest.length;
-        final int charCount = 3 * digestLength - 1;
-
-        final char[] chars = new char[charCount];
-        for (int i = 0; i < digestLength; i++) {
-            final int byteHex = digest[i] & 0xFF;
-            chars[i * 3] = HEX_ARRAY[byteHex >>> 4];
-            chars[i * 3 + 1] = HEX_ARRAY[byteHex & 0x0F];
-            if (i < digestLength - 1) {
-                chars[i * 3 + 2] = ':';
-            }
-        }
-
-        String ret = new String(chars);
-        Log.d(LOG_TAG, "Module: " + module.packageName + " has signature: " + ret);
-        return ret;
-    }
-
 }
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java
new file mode 100644
index 0000000..b34242e
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.compatibility.common.util.mainline;
+
+/**
+ * Enum containing metadata for mainline modules.
+ */
+public enum MainlineModule {
+    // Security
+    MEDIA_SOFTWARE_CODEC("com.google.android.media.swcodec",
+            true, ModuleType.APEX,
+            "0C:2B:13:87:6D:E5:6A:E6:4E:D1:DE:93:42:2A:8A:3F:EA:6F:34:C0:FC:5D:7D:A1:BD:CF:EF"
+                    + ":C1:A7:B7:C9:1D"),
+    MEDIA("com.google.android.media",
+            true, ModuleType.APEX,
+            "16:C1:5C:FA:15:D0:FD:D0:7E:BE:CB:5A:76:6B:40:8B:05:DD:92:7E:1F:3A:DD:C5:AB:F6:8E"
+                    + ":E8:B9:98:F9:FD"),
+    DNS_RESOLVER("com.google.android.resolv",
+            true, ModuleType.APEX,
+            "EC:82:21:76:5E:4F:7E:2C:6D:8D:0F:0C:E9:BD:82:5B:98:BE:D2:0C:07:2C:C6:C8:08:DD:E4"
+                    + ":68:5F:EB:A6:FF"),
+    CONSCRYPT("com.google.android.conscrypt",
+            true, ModuleType.APEX,
+            "8C:5D:A9:10:E6:11:21:B9:D6:E0:3B:42:D3:20:6A:7D:AD:29:DD:C1:63:AE:CD:4B:8E:E9:3F"
+                    + ":D3:83:79:CA:2A"),
+    // Privacy
+    PERMISSION_CONTROLLER("com.google.android.permissioncontroller",
+            false, ModuleType.APK,
+            "89:DF:B5:04:7E:E0:19:29:C2:18:4D:68:EF:49:64:F2:A9:0A:F1:24:C3:23:38:28:B8:F6:40"
+                    + ":D9:E6:C0:0F:83"),
+    ANDROID_SERVICES("com.google.android.ext.services",
+            false, ModuleType.APK,
+            "18:46:05:09:5B:E6:CA:22:D0:55:F3:4E:FA:F0:13:44:FD:3A:B3:B5:63:8C:30:62:76:10:EE"
+                    + ":AE:8A:26:0B:29"),
+    DOCUMENTS_UI("com.google.android.documentsui",
+            true, ModuleType.APK,
+            "9A:4B:85:34:44:86:EC:F5:1F:F8:05:EB:9D:23:17:97:79:BE:B7:EC:81:91:93:5A:CA:67:F0"
+                    + ":F4:09:02:52:97"),
+    // Consistency
+    TZDATA("com.google.android.tzdata",
+            true, ModuleType.APEX,
+            "55:93:DD:78:CB:26:EC:9B:00:59:2A:6A:F5:94:E4:16:1F:FD:B5:E9:F3:71:A7:43:54:5F:93"
+                    + ":F2:A0:F6:53:89"),
+    NETWORK_STACK("com.google.android.networkstack",
+            true, ModuleType.APK,
+            "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
+                    + ":F6:0B:F6:2C:1E"),
+    CAPTIVE_PORTAL_LOGIN("com.google.android.captiveportallogin",
+            true, ModuleType.APK,
+            "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
+                    + ":F6:0B:F6:2C:1E"),
+    NETWORK_PERMISSION_CONFIGURATION("com.google.android.networkstack.permissionconfig",
+            true, ModuleType.APK,
+            "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
+                    + ":F6:0B:F6:2C:1E"),
+    MODULE_METADATA("com.google.android.modulemetadata",
+            true, ModuleType.APK,
+            "BF:62:23:1E:28:F0:85:42:75:5C:F3:3C:9D:D8:3C:5D:1D:0F:A3:20:64:50:EF:BC:4C:3F:F3"
+                    + ":D5:FD:A0:33:0F"),
+    ;
+
+    public final String packageName;
+    public final boolean isPlayUpdated;
+    public final ModuleType moduleType;
+    public final String certSHA256;
+
+    MainlineModule(String packageName, boolean isPlayUpdated, ModuleType moduleType,
+            String certSHA256) {
+        this.packageName = packageName;
+        this.isPlayUpdated = isPlayUpdated;
+        this.moduleType = moduleType;
+        this.certSHA256 = certSHA256;
+    }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleDetector.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleDetector.java
new file mode 100644
index 0000000..11b467d
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleDetector.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.compatibility.common.util.mainline;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import java.security.MessageDigest;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Detects mainline modules.
+ */
+public class ModuleDetector {
+    private static final String LOG_TAG = "MainlineModuleDetector";
+
+    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+
+    /**
+     * Return true if a module is play managed.
+     *
+     * Example of skipping a test based on mainline modules:
+     * <pre>
+     *  @Test
+     *  public void testPocCVE_1234_5678() throws Exception {
+     *      if(!ModuleDetector.moduleIsPlayManaged(
+     *          getInstrumentation().getContext().getPackageManager(),
+     *          MainlineModule.MEDIA_SOFTWARE_CODEC)) {
+     *          doStagefrightTest(R.raw.cve_2018_5882);
+     *      }
+     *  }
+     * </pre>
+     */
+    public static boolean moduleIsPlayManaged(PackageManager pm, MainlineModule module)
+            throws Exception {
+        return getPlayManagedModules(pm).contains(module);
+    }
+
+
+    /**
+     * Return all play managed mainline modules.
+     */
+    public static Set<MainlineModule> getPlayManagedModules(PackageManager pm) throws Exception {
+        Set<MainlineModule> playManagedModules = new HashSet<>();
+
+        Set<String> packages = new HashSet<>();
+        for (PackageInfo info : pm.getInstalledPackages(0)) {
+            packages.add(info.packageName);
+        }
+        for (PackageInfo info : pm.getInstalledPackages(PackageManager.MATCH_APEX)) {
+            packages.add(info.packageName);
+        }
+
+        for (MainlineModule module : EnumSet.allOf(MainlineModule.class)) {
+            if (module.isPlayUpdated && packages.contains(module.packageName)
+                    && module.certSHA256.equals(getSignatureDigest(pm, module))) {
+                playManagedModules.add(module);
+            }
+        }
+        return playManagedModules;
+    }
+
+    private static String getSignatureDigest(PackageManager pm, MainlineModule module)
+            throws Exception {
+        int flag = PackageManager.GET_SIGNING_CERTIFICATES;
+        if (module.moduleType == ModuleType.APEX) {
+            flag |= PackageManager.MATCH_APEX;
+        }
+
+        PackageInfo packageInfo = pm.getPackageInfo(module.packageName, flag);
+        MessageDigest messageDigest = MessageDigest.getInstance("SHA256");
+        messageDigest.update(packageInfo.signingInfo.getApkContentsSigners()[0].toByteArray());
+
+        final byte[] digest = messageDigest.digest();
+        final int digestLength = digest.length;
+        final int charCount = 3 * digestLength - 1;
+
+        final char[] chars = new char[charCount];
+        for (int i = 0; i < digestLength; i++) {
+            final int byteHex = digest[i] & 0xFF;
+            chars[i * 3] = HEX_ARRAY[byteHex >>> 4];
+            chars[i * 3 + 1] = HEX_ARRAY[byteHex & 0x0F];
+            if (i < digestLength - 1) {
+                chars[i * 3 + 2] = ':';
+            }
+        }
+
+        String ret = new String(chars);
+        Log.d(LOG_TAG, "Module: " + module.packageName + " has signature: " + ret);
+        return ret;
+    }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleType.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleType.java
new file mode 100644
index 0000000..b50d62c
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleType.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.compatibility.common.util.mainline;
+
+/**
+ * File type of mainline module.
+ */
+public enum ModuleType {
+    APEX,
+    APK
+}