Merge "Handle hsum target as well" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index db0b43a..3651dfa 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -66,6 +66,11 @@
       "keywords": ["internal"]
     }
   ],
+  "ferrochrome-postsubmit": [
+    {
+      "name": "ferrochrome-tests"
+    }
+  ],
   "postsubmit": [
     {
       "name": "CtsMicrodroidDisabledTestCases"
diff --git a/docs/custom_vm.md b/docs/custom_vm.md
index 945798f..fce6da2 100644
--- a/docs/custom_vm.md
+++ b/docs/custom_vm.md
@@ -63,15 +63,23 @@
 As of today (April 2024), ChromiumOS is the only officially supported guest
 payload. We will be adding more OSes in the future.
 
-#### Download from build server
+#### Download ChromiumOS from build server
 
-Here's the link the comprehensive artifacts
-https://pantheon.corp.google.com/storage/browser/chromiumos-image-archive/ferrochrome-public
+Download
+https://storage.googleapis.com/chromiumos-image-archive/ferrochrome-public/R128-15926.0.0/chromiumos_test_image.tar.xz.
+The above will download ferrochrome test image with version `R128-15926.0.0`.
 
-Pick a build, download, and untar `chromiumos_test_image.tar.xz`. We'll boot with `chromiumos_test_image.bin` in it.
+To download latest version, use following code.
 
-To find the latest green build, check following:
-https://pantheon.corp.google.com/storage/browser/_details/chromiumos-image-archive/ferrochrome-public/LATEST-main
+```sh
+URL=https://storage.googleapis.com/chromiumos-image-archive/ferrochrome-public
+LATEST_VERSION=$(curl -s ${URL}/LATEST-main)
+curl -O ${URL}/${LATEST_VERSION}/chromiumos_test_image.tar.xz
+```
+
+To navigate build server artifacts,
+[install gsutil](https://cloud.google.com/storage/docs/gsutil_install).
+`gs://chromiumos-image-archive/ferrochrome-public` is the top level directory for ferrochrome build.
 
 #### Build ChromiumOS for VM
 
diff --git a/ferrochrome_app/java/com/android/virtualization/ferrochrome/FerrochromeActivity.java b/ferrochrome_app/java/com/android/virtualization/ferrochrome/FerrochromeActivity.java
index 8ca39eb..d9e5229 100644
--- a/ferrochrome_app/java/com/android/virtualization/ferrochrome/FerrochromeActivity.java
+++ b/ferrochrome_app/java/com/android/virtualization/ferrochrome/FerrochromeActivity.java
@@ -20,6 +20,7 @@
 import android.app.ActivityManager;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.SystemProperties;
@@ -39,12 +40,14 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 public class FerrochromeActivity extends Activity {
     ExecutorService executorService = Executors.newSingleThreadExecutor();
     private static final String TAG = "FerrochromeActivity";
+    private static final String ACTION_VM_LAUNCHER = "android.virtualization.VM_LAUNCHER";
     private static final String FERROCHROME_VERSION = "R128-15926.0.0";
     private static final String EXTERNAL_STORAGE_DIR =
             Environment.getExternalStorageDirectory().getPath() + File.separator;
@@ -60,9 +63,19 @@
         setContentView(R.layout.activity_ferrochrome);
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
 
+        // Find VM Launcher
+        Intent intent = new Intent(ACTION_VM_LAUNCHER);
+        PackageManager pm = getPackageManager();
+        List<ResolveInfo> resolveInfos =
+                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+        if (resolveInfos == null || resolveInfos.size() != 1) {
+            updateStatus("Failed to resolve VM Launcher");
+            return;
+        }
+
         // Clean up the existing vm launcher process if there is
         ActivityManager am = getSystemService(ActivityManager.class);
-        am.killBackgroundProcesses(getVmLauncherAppPackageName());
+        am.killBackgroundProcesses(resolveInfos.get(0).activityInfo.packageName);
 
         executorService.execute(
                 () -> {
@@ -96,33 +109,10 @@
                     }
                     updateStatus("Done.");
                     updateStatus("Starting Ferrochrome...");
-                    runOnUiThread(
-                            () ->
-                                    startActivity(
-                                            new Intent()
-                                                    .setClassName(
-                                                            getVmLauncherAppPackageName(),
-                                                            "com.android.virtualization.vmlauncher.MainActivity")));
+                    runOnUiThread(() -> startActivity(intent));
                 });
     }
 
-    private String getVmLauncherAppPackageName() {
-        PackageManager pm = getPackageManager();
-        for (String packageName :
-                new String[] {
-                    "com.android.virtualization.vmlauncher",
-                    "com.google.android.virtualization.vmlauncher"
-                }) {
-            try {
-                pm.getPackageInfo(packageName, 0);
-                return packageName;
-            } catch (PackageManager.NameNotFoundException e) {
-                continue;
-            }
-        }
-        return null;
-    }
-
     private void updateStatus(String line) {
         Log.d(TAG, line);
         runOnUiThread(
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index ff17ed1..c6d6090 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -395,12 +395,9 @@
 // python -c "import hashlib; print(hashlib.sha256(b'bootloader').hexdigest())"
 bootloader_salt = "3b4a12881d11f33cff968a24d7c53723a8232cde9a8d91e29fdbd6a95ae6adf0"
 
-// Note that keys can be different for filesystem images even though we're using the same key
-// for microdroid. However, the key signing VBmeta should match with the pubkey embedded in
-// bootloader.
 filegroup {
     name: "microdroid_sign_key",
-    srcs: [":avb_testkey_rsa4096"],
+    srcs: [":pvmfw_embedded_key"],
 }
 
 soong_config_module_type {
diff --git a/tests/ferrochrome/Android.bp b/tests/ferrochrome/Android.bp
index 889f41e..f165b8f 100644
--- a/tests/ferrochrome/Android.bp
+++ b/tests/ferrochrome/Android.bp
@@ -4,10 +4,26 @@
 
 sh_test_host {
     name: "ferrochrome-tests",
-    src: "ferrochrome.sh",
+    src: ":ferrochrome-tests.sh",
+    test_suites: ["general-tests"],
     test_options: {
         unit_test: false,
     },
     per_testcase_directory: true,
     data: ["assets/vm_config.json"],
+    data_bins: ["ferrochrome-precondition-checker.sh"],
+}
+
+// Workaround for enabling verbose logging only on CI
+genrule {
+    name: "ferrochrome-tests.sh",
+    srcs: ["ferrochrome.sh"],
+    out: ["ferrochrome-tests"],
+    // This breaks shebang, but test will execute the script with bash
+    cmd: "echo \"set -x\" > $(out); cat $(in) >> $(out)",
+}
+
+sh_binary_host {
+    name: "ferrochrome-precondition-checker.sh",
+    src: "ferrochrome-precondition-checker.sh",
 }
diff --git a/tests/ferrochrome/AndroidTest.xml b/tests/ferrochrome/AndroidTest.xml
index 3f902ea..79cbe72 100644
--- a/tests/ferrochrome/AndroidTest.xml
+++ b/tests/ferrochrome/AndroidTest.xml
@@ -23,14 +23,23 @@
         <option name="force-root" value="true"/>
     </target_preparer>
 
-    <!-- Check presence of vmlauncher here, because we can't skip tests in shell test -->
+    <!-- Check assumptions here, because we can't skip tests in shell test -->
+    <target_preparer class="com.android.tradefed.targetprep.RunHostScriptTargetPreparer">
+        <option name="script-file" value="ferrochrome-precondition-checker.sh" />
+    </target_preparer>
+
+    <!-- Explicitly clean up ferrochrome image when done.
+         It's too large (6.5G+), so this may break further tests. -->
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="throw-if-cmd-fail" value="true" />
-        <option name="run-command" value="pm list packages | grep vmlauncher" />
+        <option name="throw-if-cmd-fail" value="false" />
+        <option name="run-command" value="mkdir /data/local/tmp/ferrochrome" />
+        <option name="teardown-command" value="pkill vmlauncher" />
+        <option name="teardown-command" value="rm -rf /data/local/tmp/ferrochrome" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
         <option name="binary" value="ferrochrome-tests" />
+        <option name="relative-path-execution" value="true" />
         <option name="runtime-hint" value="10m" />
         <option name="per-binary-timeout" value="20m" />
     </test>
diff --git a/tests/ferrochrome/ferrochrome-precondition-checker.sh b/tests/ferrochrome/ferrochrome-precondition-checker.sh
new file mode 100644
index 0000000..d3f7f5a
--- /dev/null
+++ b/tests/ferrochrome/ferrochrome-precondition-checker.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+# Copyright 2024 Google Inc. All rights reserved.
+#
+# 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.
+
+
+## Precondition checks for running ferrochrome
+## Used by CI for skipping tests.
+
+REQUIRED_DISK_SPACE=7340032    # Requires 7G, while image is 6.5G
+
+# `adb root` always returns exit code 0
+if [[ "$(adb root)" == *"cannot"* ]]; then
+  >&2 echo "Failed to run adb root"
+  exit 1
+fi
+
+# `pm resolve-activity` always returns exit code 0
+resolved_activity=$(adb shell pm resolve-activity -a android.virtualization.VM_LAUNCHER)
+if [[ "${resolved_activity}" == "No activity found" ]]; then
+  >&2 echo "Failed to find vmlauncher"
+  exit 1
+fi
+
+free_space=$(adb shell df /data/local | tail -1 | awk '{print $4}')
+if [[ ${free_space} -lt ${REQUIRED_DISK_SPACE} ]]; then
+  >&2 echo "Insufficient space on DUT. Need ${REQUIRED_DISK_SPACE}, but was ${free_space}"
+  exit 1
+fi
+
+free_space=$(df /tmp | tail -1 | awk '{print $4}')
+if [[ ${free_space} -lt ${REQUIRED_DISK_SPACE} ]]; then
+  >&2 echo "Insufficient space on host. Need ${REQUIRED_DISK_SPACE}, but was ${free_space}"
+  exit 1
+fi
+
+cpu_abi=$(adb shell getprop ro.product.cpu.abi)
+if [[ "${cpu_abi}" != "arm64"* ]]; then
+  >&2 echo "Unsupported architecture. Requires arm64, but was ${cpu_abi}"
+  exit 1
+fi
+
+device=$(adb shell getprop ro.product.vendor.device)
+if [[ "${device}" == "vsock_"* ]]; then
+  >&2 echo "Unsupported device. Cuttlefish isn't supported"
+  exit 1
+fi
diff --git a/tests/ferrochrome/ferrochrome.sh b/tests/ferrochrome/ferrochrome.sh
index 92702ee..d72e882 100755
--- a/tests/ferrochrome/ferrochrome.sh
+++ b/tests/ferrochrome/ferrochrome.sh
@@ -26,8 +26,7 @@
 FECR_CONSOLE_LOG_PATH="/data/data/\${pkg_name}/files/console.log"
 FECR_BOOT_COMPLETED_LOG="Have fun and send patches!"
 FECR_BOOT_TIMEOUT="300" # 5 minutes (300 seconds)
-AOSP_PKG_NAME="com.android.virtualization.vmlauncher"
-SIGNED_PKG_NAME="com.google.android.virtualization.vmlauncher"
+ACTION_NAME="android.virtualization.VM_LAUNCHER"
 
 fecr_clean_up() {
   trap - INT
@@ -38,32 +37,36 @@
 }
 
 print_usage() {
-  echo "ferochrome.sh: Launches ferrochrome image"
+  echo "ferochrome: Launches ferrochrome image"
   echo ""
   echo "By default, this downloads ferrochrome image with version ${FECR_DEFAULT_VERSION},"
   echo "launches, and waits for boot completed."
-  echo "When done, removes downloaded image."
+  echo "When done, removes downloaded image on host while keeping pushed image on device."
   echo ""
-  echo "Usage: ferrochrome.sh [options]"
+  echo "Usage: ferrochrome [options]"
   echo ""
   echo "Options"
   echo "  --help or -h: This message"
-  echo "  --dir \${dir}: Use ferrochrome images at the dir instead of downloading"
+  echo "  --dir DIR: Use ferrochrome images at the dir instead of downloading"
+  echo "  --verbose: Verbose log message (set -x)"
   echo "  --skip: Skipping downloading and/or pushing images"
   echo "  --version \${version}: ferrochrome version to be downloaded"
   echo "  --keep: Keep downloaded ferrochrome image"
 }
 
-
 fecr_version=""
 fecr_dir=""
 fecr_keep=""
 fecr_skip=""
 fecr_script_path=$(dirname ${0})
+fecr_verbose=""
 
 # Parse parameters
 while (( "${#}" )); do
   case "${1}" in
+    --verbose)
+      fecr_verbose="true"
+      ;;
     --version)
       shift
       fecr_version="${1}"
@@ -94,6 +97,24 @@
 trap fecr_clean_up INT
 trap fecr_clean_up EXIT
 
+if [[ -n "${fecr_verbose}" ]]; then
+  set -x
+fi
+
+. "${fecr_script_path}/ferrochrome-precondition-checker.sh"
+
+resolved_activities=$(adb shell pm query-activities --components -a ${ACTION_NAME})
+
+if [[ "$(echo ${resolved_activities} | wc -l)" != "1" ]]; then
+  >&2 echo "Multiple VM launchers exists"
+  exit 1
+fi
+
+pkg_name=$(dirname ${resolved_activities})
+
+adb shell pm grant ${pkg_name} android.permission.USE_CUSTOM_VIRTUAL_MACHINE > /dev/null
+adb shell pm clear ${pkg_name} > /dev/null
+
 if [[ -z "${fecr_skip}" ]]; then
   if [[ -z "${fecr_dir}" ]]; then
     # Download fecr image archive, and extract necessary files
@@ -111,20 +132,8 @@
   adb push ${fecr_script_path}/assets/vm_config.json ${FECR_CONFIG_PATH}
 fi
 
-adb root > /dev/null
-adb shell pm list packages | grep ${AOSP_PKG_NAME} > /dev/null
-if [[ "${?}" == "0" ]]; then
-  pkg_name=${AOSP_PKG_NAME}
-else
-  pkg_name=${SIGNED_PKG_NAME}
-fi
-
-adb shell pm enable ${pkg_name}/${AOSP_PKG_NAME}.MainActivity > /dev/null
-adb shell pm grant ${pkg_name} android.permission.USE_CUSTOM_VIRTUAL_MACHINE > /dev/null
-adb shell pm clear ${pkg_name} > /dev/null
-
 echo "Starting ferrochrome"
-adb shell am start-activity ${pkg_name}/${AOSP_PKG_NAME}.MainActivity > /dev/null
+adb shell am start-activity -a ${ACTION_NAME} > /dev/null
 
 log_path="/data/data/${pkg_name}/files/console.log"
 fecr_start_time=${EPOCHSECONDS}
@@ -134,5 +143,7 @@
   sleep 10
 done
 
-echo "Ferrochrome failed to boot"
+>&2 echo "Ferrochrome failed to boot. Dumping console log"
+>&2 adb shell cat ${log_path}
+
 exit 1
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index 5b67083..c94f171 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -2525,19 +2525,30 @@
                 .isTrue();
 
         VirtualMachine[] vms = new VirtualMachine[numVMs];
-        for (int i = 0; i < numVMs; i++) {
-            VirtualMachineConfig config =
-                    newVmConfigBuilderWithPayloadBinary("MicrodroidIdleNativeLib.so")
-                            .setDebugLevel(DEBUG_LEVEL_NONE)
-                            .setMemoryBytes(vmSize)
-                            .build();
+        try {
+            for (int i = 0; i < numVMs; i++) {
+                VirtualMachineConfig config =
+                        newVmConfigBuilderWithPayloadBinary("MicrodroidIdleNativeLib.so")
+                                .setDebugLevel(DEBUG_LEVEL_NONE)
+                                .setMemoryBytes(vmSize)
+                                .build();
 
-            vms[i] = forceCreateNewVirtualMachine("test_concurrent_vms_" + i, config);
-            vms[i].run();
-        }
+                vms[i] = forceCreateNewVirtualMachine("test_concurrent_vms_" + i, config);
+                vms[i].run();
+            }
 
-        for (VirtualMachine vm : vms) {
-            assertThat(vm.getStatus()).isEqualTo(VirtualMachine.STATUS_RUNNING);
+            for (VirtualMachine vm : vms) {
+                assertThat(vm.getStatus()).isEqualTo(VirtualMachine.STATUS_RUNNING);
+            }
+
+        } finally {
+            // Ensure that VMs are all stopped. Otherwise we may try to reuse some of these for
+            // another run of this test with different parameters.
+            for (VirtualMachine vm : vms) {
+                if (vm != null) {
+                    vm.close();
+                }
+            }
         }
     }
 
diff --git a/vmlauncher_app/AndroidManifest.xml b/vmlauncher_app/AndroidManifest.xml
index 4f95086..bae3227 100644
--- a/vmlauncher_app/AndroidManifest.xml
+++ b/vmlauncher_app/AndroidManifest.xml
@@ -13,6 +13,11 @@
                   android:configChanges="orientation|screenSize|keyboard|keyboardHidden|navigation|uiMode"
                   android:theme="@style/MyTheme"
                   android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <action android:name="android.virtualization.VM_LAUNCHER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
         </activity>
         <activity-alias android:name=".MainActivityAlias"
                 android:targetActivity="com.android.virtualization.vmlauncher.MainActivity"