Merge "Minor cleanup on custom VM configs" into main
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index e32ff88..8314f43 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -57,6 +57,9 @@
         "MicrodroidCrashNativeLib",
         "libmicrodroid_testlib_rust",
         "libvm_attestation_test_payload",
+
+        // Non-VM payload libraries
+        "libhwtrust_jni",
     ],
     min_sdk_version: "33",
 }
@@ -186,3 +189,20 @@
         "libvm_payload_rs",
     ],
 }
+
+rust_ffi_shared {
+    name: "libhwtrust_jni",
+    crate_name: "hwtrust_jni",
+    srcs: ["src/native/hwtrust_jni.rs"],
+    prefer_rlib: true,
+    rustlibs: [
+        "libandroid_logger",
+        "libanyhow",
+        "liblog_rust",
+        "libhwtrust",
+        "libjni",
+    ],
+    shared_libs: [
+        "libcrypto",
+    ],
+}
diff --git a/tests/testapk/src/java/com/android/microdroid/test/HwTrustJni.java b/tests/testapk/src/java/com/android/microdroid/test/HwTrustJni.java
new file mode 100644
index 0000000..3b237aa
--- /dev/null
+++ b/tests/testapk/src/java/com/android/microdroid/test/HwTrustJni.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 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.microdroid.test;
+
+class HwTrustJni {
+    static {
+        System.loadLibrary("hwtrust_jni");
+    }
+
+    /**
+     * Validates a DICE chain.
+     *
+     * @param diceChain The dice chain to validate.
+     * @return true if the dice chain is valid, false otherwise.
+     */
+    public static native boolean validateDiceChain(byte[] diceChain);
+}
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 fd67659..658b1bb 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -1308,6 +1308,36 @@
     }
 
     @Test
+    @VsrTest(requirements = {"VSR-7.1-001.005"})
+    public void protectedVmHasValidDiceChain() throws Exception {
+        // This test validates two things regarding the pVM DICE chain:
+        // 1. The DICE chain is well-formed that all the entries conform to the DICE spec.
+        // 2. Each entry in the DICE chain is signed by the previous entry's subject public key.
+        assumeSupportedDevice();
+        assumeProtectedVM();
+        assumeVsrCompliant();
+        assumeTrue("Vendor API must be at least 202404", getVendorApiLevel() >= 202404);
+
+        grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
+        VirtualMachineConfig config =
+                newVmConfigBuilderWithPayloadConfig("assets/vm_config.json")
+                        .setDebugLevel(DEBUG_LEVEL_FULL)
+                        .build();
+        VirtualMachine vm = forceCreateNewVirtualMachine("bcc_vm_for_vsr", config);
+        TestResults testResults =
+                runVmTestService(
+                        TAG,
+                        vm,
+                        (service, results) -> {
+                            results.mBcc = service.getBcc();
+                        });
+        testResults.assertNoException();
+        byte[] bccBytes = testResults.mBcc;
+        assertThat(bccBytes).isNotNull();
+        assertThat(HwTrustJni.validateDiceChain(bccBytes)).isTrue();
+    }
+
+    @Test
     @CddTest(requirements = {
             "9.17/C-1-1",
             "9.17/C-1-2"
diff --git a/tests/testapk/src/native/hwtrust_jni.rs b/tests/testapk/src/native/hwtrust_jni.rs
new file mode 100644
index 0000000..3b00364
--- /dev/null
+++ b/tests/testapk/src/native/hwtrust_jni.rs
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2024, 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.
+ */
+
+//! JNI bindings to call into `hwtrust` from Java.
+
+use anyhow::Result;
+use hwtrust::{dice, session::Session};
+use jni::objects::{JByteArray, JClass};
+use jni::sys::jboolean;
+use jni::JNIEnv;
+use log::{debug, error, info};
+
+/// Validates the given DICE chain.
+#[no_mangle]
+pub extern "system" fn Java_com_android_microdroid_test_HwTrustJni_validateDiceChain(
+    env: JNIEnv,
+    _class: JClass,
+    dice_chain: JByteArray,
+) -> jboolean {
+    android_logger::init_once(
+        android_logger::Config::default()
+            .with_tag("hwtrust_jni")
+            .with_max_level(log::LevelFilter::Debug),
+    );
+    debug!("Starting the DICE chain validation ...");
+    match validate_dice_chain(env, dice_chain) {
+        Ok(_) => {
+            info!("DICE chain validated successfully");
+            true
+        }
+        Err(e) => {
+            error!("Failed to validate DICE chain: {:?}", e);
+            false
+        }
+    }
+    .into()
+}
+
+fn validate_dice_chain(env: JNIEnv, jdice_chain: JByteArray) -> Result<()> {
+    let dice_chain = env.convert_byte_array(jdice_chain)?;
+    let session = Session::default();
+    let _chain = dice::Chain::from_cbor(&session, &dice_chain)?;
+    Ok(())
+}
diff --git a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
index cf6c3a7..102b86e 100644
--- a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
+++ b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
@@ -323,7 +323,7 @@
             } catch (VirtualMachineException e) {
                 vmm.delete(VM_NAME);
                 mVirtualMachine = vmm.create(VM_NAME, config);
-                Log.e(TAG, "error" + e);
+                Log.e(TAG, "error for setting VM config", e);
             }
 
             Log.d(TAG, "vm start");
@@ -387,7 +387,9 @@
                             @Override
                             public void surfaceChanged(
                                     SurfaceHolder holder, int format, int width, int height) {
-                                Log.d(TAG, "width: " + width + ", height: " + height);
+                                Log.d(
+                                        TAG,
+                                        "surface changed, width: " + width + ", height: " + height);
                             }
 
                             @Override
@@ -413,8 +415,13 @@
                                     runWithDisplayService(
                                             (service) -> service.setCursorStream(pfds[1]));
                                 } catch (Exception e) {
-                                    Log.d("TAG", "failed to run cursor stream handler", e);
+                                    Log.d(TAG, "failed to run cursor stream handler", e);
                                 }
+                                Log.d(
+                                        TAG,
+                                        "ICrosvmAndroidDisplayService.setSurface("
+                                                + holder.getSurface()
+                                                + ")");
                                 runWithDisplayService(
                                         (service) ->
                                                 service.setSurface(
@@ -424,7 +431,12 @@
                             @Override
                             public void surfaceChanged(
                                     SurfaceHolder holder, int format, int width, int height) {
-                                Log.d(TAG, "width: " + width + ", height: " + height);
+                                Log.d(
+                                        TAG,
+                                        "cursor surface changed, width: "
+                                                + width
+                                                + ", height: "
+                                                + height);
                             }
 
                             @Override
@@ -432,13 +444,6 @@
                                 Log.d(TAG, "ICrosvmAndroidDisplayService.removeSurface()");
                                 runWithDisplayService(
                                         (service) -> service.removeSurface(true /* forCursor */));
-                                if (mCursorStream != null) {
-                                    try {
-                                        mCursorStream.close();
-                                    } catch (IOException e) {
-                                        Log.d(TAG, "failed to close fd", e);
-                                    }
-                                }
                             }
                         });
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@@ -631,9 +636,9 @@
                     ICrosvmAndroidDisplayService.Stub.asInterface(vs.waitDisplayService());
             assert service != null;
             func.apply(service);
-            Log.d(TAG, "job done");
+            Log.d(TAG, "display service runs successfully");
         } catch (Exception e) {
-            Log.d(TAG, "error", e);
+            Log.d(TAG, "error on running display service", e);
         }
     }
 
@@ -648,7 +653,7 @@
 
         @Override
         public void run() {
-            Log.d(TAG, "CursorHandler");
+            Log.d(TAG, "running CursorHandler");
             try {
                 ByteBuffer byteBuffer = ByteBuffer.allocate(8 /* (x: u32, y: u32) */);
                 byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
@@ -669,7 +674,7 @@
                             });
                 }
             } catch (IOException e) {
-                Log.e(TAG, e.getMessage());
+                Log.e(TAG, "failed to run CursorHandler", e);
             }
         }
     }