Merge "Implement collecting VmStatus in microdroid_manager"
diff --git a/OWNERS b/OWNERS
index 5bd97f3..ecd24ed 100644
--- a/OWNERS
+++ b/OWNERS
@@ -9,6 +9,7 @@
# Other owners
alanstokes@google.com
+aliceywang@google.com
ardb@google.com
ascull@google.com
inseob@google.com
diff --git a/authfs/tests/Android.bp b/authfs/tests/Android.bp
index b662bee..0177254 100644
--- a/authfs/tests/Android.bp
+++ b/authfs/tests/Android.bp
@@ -23,6 +23,7 @@
":authfs_test_files",
":CtsApkVerityTestPrebuiltFiles",
":MicrodroidTestApp",
+ ":measure_io",
],
}
@@ -42,3 +43,13 @@
test_suites: ["general-tests"],
test_harness: false,
}
+
+cc_binary {
+ name: "measure_io",
+ srcs: [
+ "measure_io.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsBenchmarks.java b/authfs/tests/java/src/com/android/fs/AuthFsBenchmarks.java
new file mode 100644
index 0000000..fb8c0cc
--- /dev/null
+++ b/authfs/tests/java/src/com/android/fs/AuthFsBenchmarks.java
@@ -0,0 +1,136 @@
+/*
+ * 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 com.android.virt.fs;
+
+import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.RootPermissionTest;
+
+import com.android.microdroid.test.common.MetricsProcessor;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.metrics.proto.MetricMeasurement.DataType;
+import com.android.tradefed.metrics.proto.MetricMeasurement.Measurements;
+import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@RootPermissionTest
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class AuthFsBenchmarks extends BaseHostJUnit4Test {
+ private static final int TRIAL_COUNT = 5;
+ private static final int FILE_SIZE_IN_MB = 4;
+
+ /** Name of the measure_io binary on host. */
+ private static final String MEASURE_IO_BIN_NAME = "measure_io";
+
+ /** Path to measure_io on Microdroid. */
+ private static final String MEASURE_IO_BIN_PATH = "/data/local/tmp/measure_io";
+
+ /** fs-verity digest (sha256) of testdata/input.4m */
+ private static final String DIGEST_4M =
+ "sha256-f18a268d565348fb4bbf11f10480b198f98f2922eb711de149857b3cecf98a8d";
+
+ @Rule public final AuthFsTestRule mAuthFsTestRule = new AuthFsTestRule();
+ @Rule public final TestMetrics mTestMetrics = new TestMetrics();
+ private MetricsProcessor mMetricsProcessor;
+
+ @BeforeClassWithInfo
+ public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
+ AuthFsTestRule.setUpClass(testInfo);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ String metricsPrefix =
+ MetricsProcessor.getMetricPrefix(
+ getDevice().getProperty("debug.hypervisor.metrics_tag"));
+ mMetricsProcessor = new MetricsProcessor(metricsPrefix + "authfs/");
+ }
+
+ @AfterClassWithInfo
+ public static void afterClassWithDevice(TestInformation testInfo)
+ throws DeviceNotAvailableException {
+ AuthFsTestRule.tearDownClass(testInfo);
+ }
+
+ @Test
+ public void seqReadRemoteFile() throws Exception {
+ readRemoteFile("seq");
+ }
+
+ @Test
+ public void randReadRemoteFile() throws Exception {
+ readRemoteFile("rand");
+ }
+
+ private void readRemoteFile(String mode) throws DeviceNotAvailableException {
+ pushMeasureIoBinToMicrodroid();
+ // Cache the file in memory for the host.
+ mAuthFsTestRule
+ .getAndroid()
+ .run("cat " + mAuthFsTestRule.TEST_DIR + "/input.4m > /dev/null");
+
+ String filePath = mAuthFsTestRule.MOUNT_DIR + "/3";
+ String cmd = MEASURE_IO_BIN_PATH + " " + filePath + " " + FILE_SIZE_IN_MB + " " + mode;
+ List<Double> rates = new ArrayList<>(TRIAL_COUNT);
+ for (int i = 0; i < TRIAL_COUNT + 1; ++i) {
+ mAuthFsTestRule.runFdServerOnAndroid(
+ "--open-ro 3:input.4m --open-ro 4:input.4m.fsv_meta", "--ro-fds 3:4");
+ mAuthFsTestRule.runAuthFsOnMicrodroid("--remote-ro-file 3:" + DIGEST_4M);
+
+ String rate = mAuthFsTestRule.getMicrodroid().run(cmd);
+ rates.add(Double.parseDouble(rate));
+ }
+ reportMetrics(rates, mode + "_read", "mb_per_sec");
+ }
+
+ private void pushMeasureIoBinToMicrodroid() throws DeviceNotAvailableException {
+ File measureReadBin = mAuthFsTestRule.findTestFile(getBuild(), MEASURE_IO_BIN_NAME);
+ assertThat(measureReadBin.exists()).isTrue();
+ mAuthFsTestRule.getMicrodroidDevice().pushFile(measureReadBin, MEASURE_IO_BIN_PATH);
+ assertThat(mAuthFsTestRule.getMicrodroid().run("ls " + MEASURE_IO_BIN_PATH))
+ .isEqualTo(MEASURE_IO_BIN_PATH);
+ }
+
+ private void reportMetrics(List<Double> metrics, String name, String unit) {
+ Map<String, Double> stats = mMetricsProcessor.computeStats(metrics, name, unit);
+ for (Map.Entry<String, Double> entry : stats.entrySet()) {
+ Metric metric =
+ Metric.newBuilder()
+ .setType(DataType.RAW)
+ .setMeasurements(
+ Measurements.newBuilder().setSingleDouble(entry.getValue()))
+ .build();
+ mTestMetrics.addTestMetric(entry.getKey(), metric);
+ }
+ }
+}
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
index 2deb490..14dc88b 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
@@ -52,7 +52,7 @@
private static final String FSVERITY_BIN = "/data/local/tmp/fsverity";
/** Mount point of authfs on Microdroid during the test */
- private static final String MOUNT_DIR = "/data/local/tmp";
+ private static final String MOUNT_DIR = AuthFsTestRule.MOUNT_DIR;
/** Input manifest path in the VM. */
private static final String INPUT_MANIFEST_PATH = "/mnt/apk/assets/input_manifest.pb";
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java b/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
index 48c6b22..ebeac4f 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
@@ -56,7 +56,7 @@
private static final String VM_CONFIG_PATH_IN_APK = "assets/vm_config.json";
/** Test directory on Android where data are located */
- private static final String TEST_DIR = "/data/local/tmp/authfs";
+ static final String TEST_DIR = "/data/local/tmp/authfs";
/** File name of the test APK */
private static final String TEST_APK_NAME = "MicrodroidTestApp.apk";
@@ -65,7 +65,7 @@
private static final String TEST_OUTPUT_DIR = "/data/local/tmp/authfs/output_dir";
/** Mount point of authfs on Microdroid during the test */
- private static final String MOUNT_DIR = "/data/local/tmp";
+ static final String MOUNT_DIR = "/data/local/tmp/mnt";
/** VM's log file */
private static final String LOG_PATH = TEST_OUTPUT_DIR + "/log.txt";
@@ -85,6 +85,7 @@
private static final int VMADDR_CID_HOST = 2;
private static TestInformation sTestInfo;
+ private static ITestDevice sMicrodroidDevice;
private static CommandRunner sAndroid;
private static CommandRunner sMicrodroid;
@@ -110,18 +111,21 @@
// For each test case, boot and adb connect to a new Microdroid
CLog.i("Starting the shared VM");
- ITestDevice microdroidDevice =
+ sMicrodroidDevice =
MicrodroidBuilder.fromFile(
- findTestApk(testInfo.getBuildInfo()), VM_CONFIG_PATH_IN_APK)
+ findTestFile(testInfo.getBuildInfo(), TEST_APK_NAME),
+ VM_CONFIG_PATH_IN_APK)
.debugLevel("full")
.build((TestDevice) androidDevice);
// From this point on, we need to tear down the Microdroid instance
- sMicrodroid = new CommandRunner(microdroidDevice);
+ sMicrodroid = new CommandRunner(sMicrodroidDevice);
+
+ sMicrodroid.runForResult("mkdir -p " + MOUNT_DIR);
// Root because authfs (started from shell in this test) currently require root to open
// /dev/fuse and mount the FUSE.
- assertThat(microdroidDevice.enableAdbRoot()).isTrue();
+ assertThat(sMicrodroidDevice.enableAdbRoot()).isTrue();
}
static void tearDownClass(TestInformation testInfo) throws DeviceNotAvailableException {
@@ -148,6 +152,11 @@
return sMicrodroid;
}
+ static ITestDevice getMicrodroidDevice() {
+ assertThat(sMicrodroidDevice).isNotNull();
+ return sMicrodroidDevice;
+ }
+
@Override
public Statement apply(final Statement base, Description description) {
return super.apply(
@@ -205,11 +214,11 @@
}
}
- private static File findTestApk(IBuildInfo buildInfo) {
+ static File findTestFile(IBuildInfo buildInfo, String fileName) {
try {
- return (new CompatibilityBuildHelper(buildInfo)).getTestFile(TEST_APK_NAME);
+ return (new CompatibilityBuildHelper(buildInfo)).getTestFile(fileName);
} catch (FileNotFoundException e) {
- fail("Missing test file: " + TEST_APK_NAME);
+ fail("Missing test file: " + fileName);
return null;
}
}
diff --git a/authfs/tests/measure_io.cpp b/authfs/tests/measure_io.cpp
new file mode 100644
index 0000000..736c244
--- /dev/null
+++ b/authfs/tests/measure_io.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include <android-base/unique_fd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <random>
+
+using android::base::unique_fd;
+
+constexpr int kBlockSizeBytes = 4096;
+constexpr int kNumBytesPerMB = 1024 * 1024;
+
+int main(int argc, const char *argv[]) {
+ if (argc != 4 || !(strcmp(argv[3], "rand") == 0 || strcmp(argv[3], "seq") == 0)) {
+ std::cerr << "Usage: " << argv[0] << " <filename> <file_size_mb> <rand|seq>" << std::endl;
+ return EXIT_FAILURE;
+ }
+ int file_size_mb = std::stoi(argv[2]);
+ bool is_rand = (strcmp(argv[3], "rand") == 0);
+ const int block_count = file_size_mb * kNumBytesPerMB / kBlockSizeBytes;
+ std::vector<int> offsets(block_count);
+ for (auto i = 0; i < block_count; ++i) {
+ offsets[i] = i * kBlockSizeBytes;
+ }
+ if (is_rand) {
+ std::mt19937 rd{std::random_device{}()};
+ std::shuffle(offsets.begin(), offsets.end(), rd);
+ }
+ unique_fd fd(open(argv[1], O_RDONLY | O_CLOEXEC));
+ if (fd.get() == -1) {
+ std::cerr << "failed to open file: " << argv[1] << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ char buf[kBlockSizeBytes];
+ clock_t start = clock();
+ for (auto i = 0; i < block_count; ++i) {
+ auto bytes = pread(fd, buf, kBlockSizeBytes, offsets[i]);
+ if (bytes == 0) {
+ std::cerr << "unexpected end of file" << std::endl;
+ return EXIT_FAILURE;
+ } else if (bytes == -1) {
+ std::cerr << "failed to read" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+ double elapsed_seconds = ((double)clock() - start) / CLOCKS_PER_SEC;
+ double rate = (double)file_size_mb / elapsed_seconds;
+ std::cout << std::setprecision(12) << rate << std::endl;
+
+ return EXIT_SUCCESS;
+}
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index 34a990d..a354339 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -33,7 +33,7 @@
### Pixel 6 and 6 Pro
If the device is running Android 12, join the [Android Beta
-Program](https://www.google.com/android/beta) to uprade to Android 13 Beta.
+Program](https://www.google.com/android/beta) to upgrade to Android 13 Beta.
Once upgraded to Android 13, execute the following command to enable pKVM.
@@ -66,7 +66,7 @@
```
Finally, if the `pvmfw` partition has been corrupted, both slots may be flashed
-using the [`pvmfw.img` pre-built](https://android.googlesource.com/platform/packages/modules/Virtualization/+/refs/heads/master/pvmfw/pvmfw.img)
+using the [`pvmfw.img` pre-built](https://android.googlesource.com/platform/packages/modules/Virtualization/+/08deac98acefd62e222edfa374d5292458cf97eb%5E/pvmfw/pvmfw.img)
as long as the bootloader remains unlocked. Otherwise, a fresh install of
Android 13 followed by the manual steps above for flashing the `other` slot
should be used as a last resort.
diff --git a/libs/apkverify/Android.bp b/libs/apkverify/Android.bp
index 78192d2..1862820 100644
--- a/libs/apkverify/Android.bp
+++ b/libs/apkverify/Android.bp
@@ -33,6 +33,7 @@
name: "libapkverify.test",
defaults: ["libapkverify.defaults"],
test_suites: ["general-tests"],
+ rustlibs: ["libhex"],
data: ["tests/data/*"],
}
diff --git a/libs/apkverify/src/v4.rs b/libs/apkverify/src/v4.rs
index 715f742..6c085f6 100644
--- a/libs/apkverify/src/v4.rs
+++ b/libs/apkverify/src/v4.rs
@@ -312,10 +312,6 @@
const TEST_APK_PATH: &str = "tests/data/v4-digest-v3-Sha256withEC.apk";
- fn hexstring_from(s: &[u8]) -> String {
- s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or_default()
- }
-
#[test]
fn parse_idsig_file() {
let parsed = V4Signature::from_idsig_path(format!("{}.idsig", TEST_APK_PATH)).unwrap();
@@ -325,22 +321,22 @@
let hi = parsed.hashing_info;
assert_eq!(HashAlgorithm::SHA256, hi.hash_algorithm);
assert_eq!(12, hi.log2_blocksize);
- assert_eq!("", hexstring_from(hi.salt.as_ref()));
+ assert_eq!("", hex::encode(hi.salt.as_ref()));
assert_eq!(
"77f063b48b63f846690fa76450a8d3b61a295b6158f50592e873f76dbeeb0201",
- hexstring_from(hi.raw_root_hash.as_ref())
+ hex::encode(hi.raw_root_hash.as_ref())
);
let si = parsed.signing_info;
assert_eq!(
"c02fe2eddeb3078801828b930de546ea4f98d37fb98b40c7c7ed169b0d713583",
- hexstring_from(si.apk_digest.as_ref())
+ hex::encode(si.apk_digest.as_ref())
);
- assert_eq!("", hexstring_from(si.additional_data.as_ref()));
+ assert_eq!("", hex::encode(si.additional_data.as_ref()));
assert_eq!(
"3046022100fb6383ba300dc7e1e6931a25b381398a16e5575baefd82afd12ba88660d9a6\
4c022100ebdcae13ab18c4e30bf6ae634462e526367e1ba26c2647a1d87a0f42843fc128",
- hexstring_from(si.signature.as_ref())
+ hex::encode(si.signature.as_ref())
);
assert_eq!(SignatureAlgorithmID::EcdsaWithSha256, si.signature_algorithm_id);
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl
index bed4097..424eec1 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl
@@ -28,9 +28,6 @@
/** The UID of the process which requested the VM. */
int requesterUid;
- /** The SID of the process which requested the VM. */
- @utf8InCpp String requesterSid;
-
/**
* The PID of the process which requested the VM. Note that this process may no longer exist and
* the PID may have been reused for a different process, so this should not be trusted.
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 33102eb..eb2bac4 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -118,8 +118,6 @@
.or(Err(StatusCode::UNKNOWN_ERROR))?;
writeln!(file, "\trequester_uid: {}", vm.requester_uid)
.or(Err(StatusCode::UNKNOWN_ERROR))?;
- writeln!(file, "\trequester_sid: {}", vm.requester_sid)
- .or(Err(StatusCode::UNKNOWN_ERROR))?;
writeln!(file, "\trequester_debug_pid: {}", vm.requester_debug_pid)
.or(Err(StatusCode::UNKNOWN_ERROR))?;
}
@@ -224,7 +222,6 @@
cid: vm.cid as i32,
temporaryDirectory: vm.temporary_directory.to_string_lossy().to_string(),
requesterUid: vm.requester_uid as i32,
- requesterSid: vm.requester_sid.clone(),
requesterPid: vm.requester_debug_pid,
state: get_state(&vm),
})
@@ -353,7 +350,6 @@
let console_fd = console_fd.map(clone_file).transpose()?;
let log_fd = log_fd.map(clone_file).transpose()?;
let requester_uid = ThreadState::get_calling_uid();
- let requester_sid = get_calling_sid()?;
let requester_debug_pid = ThreadState::get_calling_pid();
let cid = next_cid().or(Err(ExceptionCode::ILLEGAL_STATE))?;
@@ -473,20 +469,14 @@
detect_hangup: is_app_config,
};
let instance = Arc::new(
- VmInstance::new(
- crosvm_config,
- temporary_directory,
- requester_uid,
- requester_sid,
- requester_debug_pid,
- )
- .map_err(|e| {
- error!("Failed to create VM with config {:?}: {:?}", config, e);
- Status::new_service_specific_error_str(
- -1,
- Some(format!("Failed to create VM: {:?}", e)),
- )
- })?,
+ VmInstance::new(crosvm_config, temporary_directory, requester_uid, requester_debug_pid)
+ .map_err(|e| {
+ error!("Failed to create VM with config {:?}: {:?}", config, e);
+ Status::new_service_specific_error_str(
+ -1,
+ Some(format!("Failed to create VM: {:?}", e)),
+ )
+ })?,
);
state.add_vm(Arc::downgrade(&instance));
Ok(VirtualMachine::create(instance))
@@ -712,27 +702,6 @@
footer: PathBuf,
}
-/// Gets the calling SID of the current Binder thread.
-fn get_calling_sid() -> Result<String, Status> {
- ThreadState::with_calling_sid(|sid| {
- if let Some(sid) = sid {
- match sid.to_str() {
- Ok(sid) => Ok(sid.to_owned()),
- Err(e) => {
- error!("SID was not valid UTF-8: {}", e);
- Err(Status::new_exception_str(
- ExceptionCode::ILLEGAL_ARGUMENT,
- Some(format!("SID was not valid UTF-8: {}", e)),
- ))
- }
- }
- } else {
- error!("Missing SID on createVm");
- Err(Status::new_exception_str(ExceptionCode::SECURITY, Some("Missing SID on createVm")))
- }
- })
-}
-
/// Checks whether the caller has a specific permission
fn check_permission(perm: &str) -> binder::Result<()> {
let calling_pid = ThreadState::get_calling_pid();
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 54cdeb6..fa961c2 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -185,8 +185,6 @@
pub temporary_directory: PathBuf,
/// The UID of the process which requested the VM.
pub requester_uid: u32,
- /// The SID of the process which requested the VM.
- pub requester_sid: String,
/// The PID of the process which requested the VM. Note that this process may no longer exist
/// and the PID may have been reused for a different process, so this should not be trusted.
pub requester_debug_pid: i32,
@@ -210,7 +208,6 @@
config: CrosvmConfig,
temporary_directory: PathBuf,
requester_uid: u32,
- requester_sid: String,
requester_debug_pid: i32,
) -> Result<VmInstance, Error> {
validate_config(&config)?;
@@ -224,7 +221,6 @@
protected,
temporary_directory,
requester_uid,
- requester_sid,
requester_debug_pid,
callbacks: Default::default(),
stream: Mutex::new(None),
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index cb10eff..327a45d 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -50,10 +50,7 @@
clear_temporary_files().expect("Failed to delete old temporary files");
let service = VirtualizationService::init();
- let service = BnVirtualizationService::new_binder(
- service,
- BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
- );
+ let service = BnVirtualizationService::new_binder(service, BinderFeatures::default());
register_lazy_service(BINDER_SERVICE_IDENTIFIER, service.as_binder()).unwrap();
info!("Registered Binder service, joining threadpool.");
ProcessState::join_thread_pool();