Merge "The test app built from AOSP is installable to T devices"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 11648c4..ea81bc4 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -28,9 +28,6 @@
     },
     {
       "name": "ComposBenchmarkApp"
-    },
-    {
-      "name": "AVFHostTestCases"
     }
   ],
   "imports": [
diff --git a/tests/benchmark_hostside/Android.bp b/tests/benchmark_hostside/Android.bp
deleted file mode 100644
index 354a260..0000000
--- a/tests/benchmark_hostside/Android.bp
+++ /dev/null
@@ -1,19 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_test_host {
-    name: "AVFHostTestCases",
-    srcs: ["java/**/*.java"],
-    libs: [
-        "tradefed",
-        "compatibility-tradefed",
-        "compatibility-host-util",
-    ],
-    static_libs: [
-        "MicrodroidHostTestHelper",
-    ],
-    test_suites: [
-        "general-tests",
-    ],
-}
diff --git a/tests/benchmark_hostside/AndroidTest.xml b/tests/benchmark_hostside/AndroidTest.xml
deleted file mode 100644
index 5161269..0000000
--- a/tests/benchmark_hostside/AndroidTest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 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.
--->
-<configuration description="Tests for AVF">
-    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
-        <option name="force-root" value="true" />
-    </target_preparer>
-
-    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
-        <option name="jar" value="AVFHostTestCases.jar" />
-    </test>
-</configuration>
\ No newline at end of file
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
deleted file mode 100644
index cbc7188..0000000
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2022 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 android.avf.test;
-
-import android.platform.test.annotations.RootPermissionTest;
-
-import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import com.android.microdroid.test.CommandRunner;
-import com.android.microdroid.test.MicrodroidHostTestCaseBase;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.util.CommandResult;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-@RootPermissionTest
-@RunWith(DeviceJUnit4ClassRunner.class)
-public final class AVFHostTestCase extends MicrodroidHostTestCaseBase {
-
-    private static final String COMPOSD_CMD_BIN = "/apex/com.android.compos/bin/composd_cmd";
-
-    // Files that define the "test" instance of CompOS
-    private static final String COMPOS_TEST_ROOT = "/data/misc/apexdata/com.android.compos/test/";
-
-    private static final String SYSTEM_SERVER_COMPILER_FILTER_PROP_NAME =
-            "dalvik.vm.systemservercompilerfilter";
-    private String mBackupSystemServerCompilerFilter;
-
-    /** Boot time test related variables */
-    private static final int BOOT_COMPLETE_TIMEOUT_MS = 10 * 60 * 1000;
-    private static final int DEVICE_AVAILABLE_WAIT_TIMEOUT_MS = 3 * 60 * 1000;
-    private static final double NANOS_IN_SEC = 1_000_000_000.0;
-    private static final int ROUND_COUNT = 3;
-    private static final String METRIC_PREFIX = "avf_perf/compos/";
-
-    @Before
-    public void setUp() throws Exception {
-        testIfDeviceIsCapable(getDevice());
-
-        String value = getDevice().getProperty(SYSTEM_SERVER_COMPILER_FILTER_PROP_NAME);
-        if (value == null) {
-            mBackupSystemServerCompilerFilter = "";
-        } else {
-            mBackupSystemServerCompilerFilter = value;
-        }
-
-        getDevice().setProperty(SYSTEM_SERVER_COMPILER_FILTER_PROP_NAME, "speed");
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        CommandRunner android = new CommandRunner(getDevice());
-
-        // Clear up any CompOS instance files we created
-        android.tryRun("rm", "-rf", COMPOS_TEST_ROOT);
-
-        if (mBackupSystemServerCompilerFilter != null) {
-            CLog.d("Restore dalvik.vm.systemservercompilerfilter to "
-                    + mBackupSystemServerCompilerFilter);
-            getDevice().setProperty(SYSTEM_SERVER_COMPILER_FILTER_PROP_NAME,
-                    mBackupSystemServerCompilerFilter);
-        }
-    }
-
-    @Test
-    public void testBootWithAndWithoutCompOS() throws Exception {
-
-        double[] bootWithCompOsTime = new double[ROUND_COUNT];
-        double[] bootWithoutCompOsTime = new double[ROUND_COUNT];
-
-        for (int round = 0; round < ROUND_COUNT; ++round) {
-
-            // Boot time with compilation OS test.
-            getDevice().waitForDeviceAvailable(DEVICE_AVAILABLE_WAIT_TIMEOUT_MS);
-            reInstallApex();
-            compileStagedApex();
-            long start = System.nanoTime();
-            rebootAndWaitBootCompleted();
-            long elapsedWithCompOS = System.nanoTime() - start;
-            double elapsedSec = elapsedWithCompOS / NANOS_IN_SEC;
-            bootWithCompOsTime[round] = elapsedSec;
-            CLog.i("Boot time with compilation OS took " + elapsedSec + "s");
-
-            // Boot time without compilation OS test.
-            getDevice().waitForDeviceAvailable(DEVICE_AVAILABLE_WAIT_TIMEOUT_MS);
-            reInstallApex();
-            start = System.nanoTime();
-            rebootAndWaitBootCompleted();
-            long elapsedWithoutCompOS = System.nanoTime() - start;
-            elapsedSec = elapsedWithoutCompOS / NANOS_IN_SEC;
-            bootWithoutCompOsTime[round] = elapsedSec;
-            CLog.i("Boot time without compilation OS took " + elapsedSec + "s");
-
-            assertWithMessage("Boot time with compilation OS is higher than without")
-                .that(elapsedWithCompOS).isLessThan(elapsedWithoutCompOS);
-        }
-
-        reportMetric("boot_time_with_compos", "s", bootWithCompOsTime);
-        reportMetric("boot_time_without_compos", "s", bootWithoutCompOsTime);
-    }
-
-    private void reportMetric(String name, String unit, double[] values) {
-        double sum = 0;
-        double min = Double.MAX_VALUE;
-        double max = Double.MIN_VALUE;
-
-        for (double val : values) {
-            sum += val;
-            min = val < min ? val : min;
-            max = val > max ? val : max;
-        }
-
-        double average = sum / values.length;
-
-        double variance = 0;
-        for (double val : values) {
-            final double tmp = val - average;
-            variance += tmp * tmp;
-        }
-        double stdev = Math.sqrt(variance / (double) (values.length - 1));
-
-        TestMetrics metrics = new TestMetrics();
-        metrics.addTestMetric(METRIC_PREFIX + name + "_average_" + unit, Double.toString(average));
-        metrics.addTestMetric(METRIC_PREFIX + name + "_min_" + unit, Double.toString(min));
-        metrics.addTestMetric(METRIC_PREFIX + name + "_max_" + unit, Double.toString(max));
-        metrics.addTestMetric(METRIC_PREFIX + name + "_stdev_" + unit, Double.toString(stdev));
-    }
-
-    private void rebootAndWaitBootCompleted() throws Exception {
-        getDevice().nonBlockingReboot();
-        getDevice().waitForDeviceOnline();
-        getDevice().waitForBootComplete(BOOT_COMPLETE_TIMEOUT_MS);
-    }
-
-    private void compileStagedApex() throws Exception {
-        CommandRunner android = new CommandRunner(getDevice());
-
-        String result = android.run(
-                COMPOSD_CMD_BIN + " staged-apex-compile");
-        assertWithMessage("Failed to compile staged apex. Reason: " + result)
-            .that(result).ignoringCase().contains("all ok");
-    }
-
-    private void reInstallApex() throws Exception {
-        CommandRunner android = new CommandRunner(getDevice());
-
-        String packagesOutput =
-                android.run("pm list packages -f --apex-only");
-
-        Pattern p = Pattern.compile(
-                 "package:(.*)=(com(?:\\.google)?\\.android\\.art)$", Pattern.MULTILINE);
-        Matcher m = p.matcher(packagesOutput);
-        assertWithMessage("ART module not found. Packages are:\n" + packagesOutput)
-            .that(m.find())
-            .isTrue();
-
-        String artApexPath = m.group(1);
-
-        CommandResult result = android.runForResult(
-                 "pm install --apex " + artApexPath);
-        assertWithMessage("Failed to install APEX. Reason: " + result.toString())
-             .that(result.getExitCode()).isEqualTo(0);
-    }
-}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index bba75ac..5bc646f 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -14,6 +14,7 @@
 
 //! Implementation of the AIDL interface of the VirtualizationService.
 
+use crate::atom::write_vm_creation_stats;
 use crate::composite::make_composite_image;
 use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmInstance, VmState};
 use crate::payload::add_microdroid_images;
@@ -48,11 +49,10 @@
 use binder_common::rpc_server::run_rpc_server_with_factory;
 use disk::QcowFile;
 use idsig::{HashAlgorithm, V4Signature};
-use log::{debug, error, info, warn, trace};
+use log::{debug, error, info, warn};
 use microdroid_payload_config::VmPayloadConfig;
 use rustutils::system_properties;
 use semver::VersionReq;
-use statslog_virtualization_rust::vm_creation_requested::{stats_write, Hypervisor};
 use std::convert::TryInto;
 use std::ffi::CStr;
 use std::fs::{create_dir, File, OpenOptions};
@@ -131,23 +131,7 @@
     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
         let mut is_protected = false;
         let ret = self.create_vm_internal(config, console_fd, log_fd, &mut is_protected);
-        match ret {
-            Ok(_) => {
-                let ok_status = Status::ok();
-                write_vm_creation_stats(
-                    is_protected,
-                    /*creation_succeeded*/ true,
-                    ok_status.exception_code() as i32,
-                );
-            }
-            Err(ref e) => {
-                write_vm_creation_stats(
-                    is_protected,
-                    /*creation_succeeded*/ false,
-                    e.exception_code() as i32,
-                );
-            }
-        }
+        write_vm_creation_stats(config, is_protected, &ret);
         ret
     }
 
@@ -506,16 +490,6 @@
     }
 }
 
-/// Write the stats of VMCreation to statsd
-fn write_vm_creation_stats(is_protected: bool, creation_succeeded: bool, exception_code: i32) {
-    match stats_write(Hypervisor::Pkvm, is_protected, creation_succeeded, exception_code) {
-        Err(e) => {
-            warn!("statslog_rust failed with error: {}", e);
-        }
-        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
-    }
-}
-
 /// Waits for incoming connections from VM. If a new connection is made, stores the stream in the
 /// corresponding `VmInstance`.
 fn handle_stream_connection_from_vm(state: Arc<Mutex<State>>) -> Result<()> {
@@ -1003,7 +977,7 @@
 }
 
 /// Converts a `&ParcelFileDescriptor` to a `File` by cloning the file.
-fn clone_file(file: &ParcelFileDescriptor) -> Result<File, Status> {
+pub fn clone_file(file: &ParcelFileDescriptor) -> Result<File, Status> {
     file.as_ref().try_clone().map_err(|e| {
         Status::new_exception_str(
             ExceptionCode::BAD_PARCELABLE,
diff --git a/virtualizationservice/src/atom.rs b/virtualizationservice/src/atom.rs
new file mode 100644
index 0000000..960eaa7
--- /dev/null
+++ b/virtualizationservice/src/atom.rs
@@ -0,0 +1,114 @@
+// Copyright 2022, 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.
+
+//! Functions for creating and collecting atoms.
+
+use crate::aidl::clone_file;
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+    IVirtualMachine::IVirtualMachine, VirtualMachineAppConfig::VirtualMachineAppConfig,
+    VirtualMachineConfig::VirtualMachineConfig,
+};
+use android_system_virtualizationservice::binder::{Status, Strong};
+use anyhow::{anyhow, Result};
+use log::{trace, warn};
+use microdroid_payload_config::VmPayloadConfig;
+use statslog_virtualization_rust::vm_creation_requested;
+use zip::ZipArchive;
+
+fn get_vm_payload_config(config: &VirtualMachineAppConfig) -> Result<VmPayloadConfig> {
+    let apk = config.apk.as_ref().ok_or_else(|| anyhow!("APK is none"))?;
+    let apk_file = clone_file(apk)?;
+    let mut apk_zip = ZipArchive::new(&apk_file)?;
+    let config_file = apk_zip.by_name(&config.configPath)?;
+    let vm_payload_config: VmPayloadConfig = serde_json::from_reader(config_file)?;
+    Ok(vm_payload_config)
+}
+
+/// Write the stats of VMCreation to statsd
+pub fn write_vm_creation_stats(
+    config: &VirtualMachineConfig,
+    is_protected: bool,
+    ret: &binder::Result<Strong<dyn IVirtualMachine>>,
+) {
+    let creation_succeeded;
+    let binder_exception_code;
+    match ret {
+        Ok(_) => {
+            creation_succeeded = true;
+            binder_exception_code = Status::ok().exception_code() as i32;
+        }
+        Err(ref e) => {
+            creation_succeeded = false;
+            binder_exception_code = e.exception_code() as i32;
+        }
+    }
+
+    let config_type;
+    let num_cpus;
+    let cpu_affinity;
+    let memory_mib;
+    let apexes;
+    match config {
+        VirtualMachineConfig::AppConfig(config) => {
+            config_type = vm_creation_requested::ConfigType::VirtualMachineAppConfig;
+            num_cpus = config.numCpus;
+            cpu_affinity = config.cpuAffinity.clone().unwrap_or_default();
+            memory_mib = config.memoryMib;
+
+            let vm_payload_config = get_vm_payload_config(config);
+            if let Ok(vm_payload_config) = vm_payload_config {
+                apexes = vm_payload_config
+                    .apexes
+                    .iter()
+                    .map(|x| x.name.clone())
+                    .collect::<Vec<String>>()
+                    .join(":");
+            } else {
+                apexes = "INFO: Can't get VmPayloadConfig".into();
+            }
+        }
+        VirtualMachineConfig::RawConfig(config) => {
+            config_type = vm_creation_requested::ConfigType::VirtualMachineRawConfig;
+            num_cpus = config.numCpus;
+            cpu_affinity = config.cpuAffinity.clone().unwrap_or_default();
+            memory_mib = config.memoryMib;
+            apexes = String::new();
+        }
+    }
+
+    let empty_string = String::new();
+    let vm_creation_requested = vm_creation_requested::VmCreationRequested {
+        // TODO(seungjaeyoo) Implement sending proper data about uid & vm_identifier
+        uid: -1,
+        vm_identifier: &empty_string,
+        hypervisor: vm_creation_requested::Hypervisor::Pkvm,
+        is_protected,
+        creation_succeeded,
+        binder_exception_code,
+        config_type,
+        num_cpus,
+        cpu_affinity: &cpu_affinity,
+        memory_mib,
+        apexes: &apexes,
+        // TODO(seungjaeyoo) Fill information about task_profile
+        // TODO(seungjaeyoo) Fill information about disk_image for raw config
+    };
+
+    match vm_creation_requested.stats_write() {
+        Err(e) => {
+            warn!("statslog_rust failed with error: {}", e);
+        }
+        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
+    }
+}
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index 3b0adb9..93a5966 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -15,6 +15,7 @@
 //! Android VirtualizationService
 
 mod aidl;
+mod atom;
 mod composite;
 mod crosvm;
 mod payload;