Merge "Enable adb and adb root with debug level or debug policy"
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index bc4db6c..a2a4138 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -51,6 +51,7 @@
deps: [
"init_second_stage",
"microdroid_build_prop",
+ "microdroid_init_debug_policy",
"microdroid_init_rc",
"microdroid_ueventd_rc",
"microdroid_launcher",
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 70c22d4..5187a12 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -21,13 +21,9 @@
write /linkerconfig/ld.config.txt \#
chmod 644 /linkerconfig/ld.config.txt
-# If VM is debuggable, send logs to outside ot the VM via the serial console.
-# If non-debuggable, logs are internally consumed at /dev/null
-on early-init && property:ro.boot.microdroid.debuggable=1
- setprop ro.log.file_logger.path /dev/hvc2
-
-on early-init && property:ro.boot.microdroid.debuggable=0
- setprop ro.log.file_logger.path /dev/null
+ # Applies debug policy to decide whether to enable adb, adb root, and logcat.
+ # We don't directly exec the binary to specify stdio_to_kmsg.
+ exec_start init_debug_policy
on init
mkdir /mnt/apk 0755 system system
@@ -47,8 +43,6 @@
# payloads are not designed to run with bootstrap bionic
setprop apex_config.done true
- setprop ro.debuggable ${ro.boot.microdroid.debuggable:-0}
-
on property:microdroid_manager.init_done=1
# Stop ueventd to save memory
stop ueventd
@@ -57,7 +51,7 @@
# Mount tracefs (with GID=AID_READTRACEFS)
mount tracefs tracefs /sys/kernel/tracing gid=3012
-on init && property:ro.boot.adb.enabled=1
+on property:init_debug_policy.adbd.enabled=1
start adbd
# Mount filesystems and start core system services.
@@ -179,3 +173,8 @@
group shell log readproc
seclabel u:r:shell:s0
setenv HOSTNAME console
+
+service init_debug_policy /system/bin/init_debug_policy
+ oneshot
+ disabled
+ stdio_to_kmsg
diff --git a/microdroid/init_debug_policy/Android.bp b/microdroid/init_debug_policy/Android.bp
new file mode 100644
index 0000000..b56ef79
--- /dev/null
+++ b/microdroid/init_debug_policy/Android.bp
@@ -0,0 +1,11 @@
+rust_binary {
+ name: "microdroid_init_debug_policy",
+ srcs: ["src/init_debug_policy.rs"],
+ stem: "init_debug_policy",
+ rustlibs: [
+ "librustutils",
+ ],
+ installable: false, // match with microdroid_init_rc.
+ bootstrap: true,
+ prefer_rlib: true,
+}
diff --git a/microdroid/init_debug_policy/src/init_debug_policy.rs b/microdroid/init_debug_policy/src/init_debug_policy.rs
new file mode 100644
index 0000000..6c80926
--- /dev/null
+++ b/microdroid/init_debug_policy/src/init_debug_policy.rs
@@ -0,0 +1,57 @@
+// Copyright 2023, 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.
+
+//! Applies debug policies when booting microdroid
+
+use rustutils::system_properties;
+use rustutils::system_properties::PropertyWatcherError;
+use std::fs::File;
+use std::io::Read;
+
+/// Get debug policy value in bool. It's true iff the value is explicitly set to <1>.
+fn get_debug_policy_bool(path: &'static str) -> Option<bool> {
+ let mut file = File::open(path).ok()?;
+ let mut log: [u8; 4] = Default::default();
+ file.read_exact(&mut log).ok()?;
+ // DT spec uses big endian although Android is always little endian.
+ Some(u32::from_be_bytes(log) == 1)
+}
+
+fn main() -> Result<(), PropertyWatcherError> {
+ // If VM is debuggable or debug policy says so, send logs to outside ot the VM via the serial console.
+ // Otherwise logs are internally consumed at /dev/null
+ let log_path = if system_properties::read_bool("ro.boot.microdroid.debuggable", false)?
+ || get_debug_policy_bool("/sys/firmware/devicetree/base/avf/guest/common/log")
+ .unwrap_or_default()
+ {
+ "/dev/hvc2"
+ } else {
+ "/dev/null"
+ };
+ system_properties::write("ro.log.file_logger.path", log_path)?;
+
+ let (adbd_enabled, debuggable) = if system_properties::read_bool("ro.boot.adb.enabled", false)?
+ || get_debug_policy_bool("/sys/firmware/devicetree/base/avf/guest/microdroid/adb")
+ .unwrap_or_default()
+ {
+ // debuggable is required for adb root and bypassing adb authorization.
+ ("1", "1")
+ } else {
+ ("0", "0")
+ };
+ system_properties::write("init_debug_policy.adbd.enabled", adbd_enabled)?;
+ system_properties::write("ro.debuggable", debuggable)?;
+
+ Ok(())
+}
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index d217c00..78500af 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -36,6 +36,20 @@
out: ["avf_debug_policy_without_console_output.dtbo"],
}
+genrule {
+ name: "test_avf_debug_policy_with_adb",
+ defaults: ["test_avf_debug_policy_overlay"],
+ srcs: ["assets/avf_debug_policy_with_adb.dts"],
+ out: ["avf_debug_policy_with_adb.dtbo"],
+}
+
+genrule {
+ name: "test_avf_debug_policy_without_adb",
+ defaults: ["test_avf_debug_policy_overlay"],
+ srcs: ["assets/avf_debug_policy_without_adb.dts"],
+ out: ["avf_debug_policy_without_adb.dtbo"],
+}
+
java_test_host {
name: "MicrodroidHostTestCases",
srcs: ["java/**/*.java"],
@@ -64,6 +78,8 @@
":test_avf_debug_policy_without_ramdump",
":test_avf_debug_policy_with_console_output",
":test_avf_debug_policy_without_console_output",
+ ":test_avf_debug_policy_with_adb",
+ ":test_avf_debug_policy_without_adb",
"assets/bcc.dat",
],
data_native_bins: [
diff --git a/tests/hostside/assets/avf_debug_policy_with_adb.dts b/tests/hostside/assets/avf_debug_policy_with_adb.dts
new file mode 100644
index 0000000..9ad15dd
--- /dev/null
+++ b/tests/hostside/assets/avf_debug_policy_with_adb.dts
@@ -0,0 +1,18 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ fragment@avf {
+ target-path = "/";
+
+ __overlay__ {
+ avf {
+ guest {
+ microdroid {
+ adb = <1>;
+ };
+ };
+ };
+ };
+ };
+};
\ No newline at end of file
diff --git a/tests/hostside/assets/avf_debug_policy_with_ramdump.dts b/tests/hostside/assets/avf_debug_policy_with_ramdump.dts
index f1a5196..26db7be 100644
--- a/tests/hostside/assets/avf_debug_policy_with_ramdump.dts
+++ b/tests/hostside/assets/avf_debug_policy_with_ramdump.dts
@@ -11,6 +11,9 @@
common {
ramdump = <1>;
};
+ microdroid {
+ adb = <1>; // adb is required to check VM's bootargs.
+ };
};
};
};
diff --git a/tests/hostside/assets/avf_debug_policy_without_adb.dts b/tests/hostside/assets/avf_debug_policy_without_adb.dts
new file mode 100644
index 0000000..992e0ff
--- /dev/null
+++ b/tests/hostside/assets/avf_debug_policy_without_adb.dts
@@ -0,0 +1,18 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ fragment@avf {
+ target-path = "/";
+
+ __overlay__ {
+ avf {
+ guest {
+ microdroid {
+ adb = <0>;
+ };
+ };
+ };
+ };
+ };
+};
\ No newline at end of file
diff --git a/tests/hostside/assets/avf_debug_policy_without_ramdump.dts b/tests/hostside/assets/avf_debug_policy_without_ramdump.dts
index 5b15d51..194e314 100644
--- a/tests/hostside/assets/avf_debug_policy_without_ramdump.dts
+++ b/tests/hostside/assets/avf_debug_policy_without_ramdump.dts
@@ -11,6 +11,9 @@
common {
ramdump = <0>;
};
+ microdroid {
+ adb = <1>; // adb is required to check VM's bootargs.
+ };
};
};
};
diff --git a/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java b/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
index 755613a..10f7003 100644
--- a/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
@@ -34,6 +34,7 @@
import com.android.tradefed.device.TestDevice;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.FileUtil;
@@ -55,9 +56,11 @@
@NonNull private static final String PACKAGE_FILE_NAME = "MicrodroidTestApp.apk";
@NonNull private static final String PACKAGE_NAME = "com.android.microdroid.test";
@NonNull private static final String MICRODROID_DEBUG_FULL = "full";
+ @NonNull private static final String MICRODROID_DEBUG_NONE = "none";
@NonNull private static final String MICRODROID_CONFIG_PATH = "assets/vm_config_apex.json";
@NonNull private static final String MICRODROID_LOG_PATH = TEST_ROOT + "log.txt";
private static final int BOOT_COMPLETE_TIMEOUT_MS = 30000; // 30 seconds
+ private static final int BOOT_FAILURE_WAIT_TIME_MS = 10000; // 10 seconds
private static final int CONSOLE_OUTPUT_WAIT_MS = 5000; // 5 seconds
@NonNull private static final String CUSTOM_PVMFW_FILE_PREFIX = "pvmfw";
@@ -146,7 +149,7 @@
public void testRamdump() throws Exception {
Pvmfw pvmfw = createPvmfw("avf_debug_policy_with_ramdump.dtbo");
pvmfw.serialize(mCustomPvmfwBinFileOnHost);
- mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted();
+ mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_FULL);
assertThat(readMicrodroidFileAsString(MICRODROID_CMDLINE_PATH)).contains("crashkernel=");
assertThat(readMicrodroidFileAsString(MICRODROID_DT_BOOTARGS_PATH))
@@ -159,7 +162,7 @@
public void testNoRamdump() throws Exception {
Pvmfw pvmfw = createPvmfw("avf_debug_policy_without_ramdump.dtbo");
pvmfw.serialize(mCustomPvmfwBinFileOnHost);
- mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted();
+ mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_FULL);
assertThat(readMicrodroidFileAsString(MICRODROID_CMDLINE_PATH))
.doesNotContain("crashkernel=");
@@ -193,6 +196,33 @@
.isFalse();
}
+ @Test
+ public void testNoAdb_boots() throws Exception {
+ Pvmfw pvmfw = createPvmfw("avf_debug_policy_without_adb.dtbo");
+ pvmfw.serialize(mCustomPvmfwBinFileOnHost);
+
+ // VM would boot, but cannot verify directly because of no adbd in the VM.
+ CommandResult result = tryLaunchProtectedNonDebuggableVm();
+ assertThat(result.getStatus()).isEqualTo(CommandStatus.TIMED_OUT);
+ assertWithMessage("Microdroid should have booted")
+ .that(result.getStderr())
+ .contains("payload is ready");
+ }
+
+ @Test
+ public void testNoAdb_noConnection() throws Exception {
+ Pvmfw pvmfw = createPvmfw("avf_debug_policy_without_adb.dtbo");
+ pvmfw.serialize(mCustomPvmfwBinFileOnHost);
+
+ try {
+ launchProtectedVmAndWaitForBootCompleted(
+ MICRODROID_DEBUG_NONE, BOOT_FAILURE_WAIT_TIME_MS);
+ assertWithMessage("adb shouldn't be available").fail();
+ } catch (Exception e) {
+ // expected exception. passthrough.
+ }
+ }
+
@NonNull
private String readMicrodroidFileAsString(@NonNull String path)
throws DeviceNotAvailableException {
@@ -220,14 +250,20 @@
return result.getStdout().contains("Run /init as init process");
}
- private ITestDevice launchProtectedVmAndWaitForBootCompleted()
+ private ITestDevice launchProtectedVmAndWaitForBootCompleted(String debugLevel)
throws DeviceNotAvailableException {
+ return launchProtectedVmAndWaitForBootCompleted(debugLevel, BOOT_COMPLETE_TIMEOUT_MS);
+ }
+
+ private ITestDevice launchProtectedVmAndWaitForBootCompleted(
+ String debugLevel, long adbTimeoutMs) throws DeviceNotAvailableException {
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(
getPathForPackage(PACKAGE_NAME), MICRODROID_CONFIG_PATH)
- .debugLevel(MICRODROID_DEBUG_FULL)
+ .debugLevel(debugLevel)
.protectedVm(/* protectedVm= */ true)
.addBootFile(mCustomPvmfwBinFileOnHost, PVMFW_FILE_NAME)
+ .setAdbConnectTimeoutMs(adbTimeoutMs)
.build(mAndroidDevice);
assertThat(mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT_MS)).isTrue();
assertThat(mMicrodroidDevice.enableAdbRoot()).isTrue();
diff --git a/virtualizationmanager/src/debug_config.rs b/virtualizationmanager/src/debug_config.rs
index eda854a..666c98d 100644
--- a/virtualizationmanager/src/debug_config.rs
+++ b/virtualizationmanager/src/debug_config.rs
@@ -34,6 +34,13 @@
pub fn should_prepare_console_output(debug_level: DebugLevel) -> bool {
debug_level != DebugLevel::NONE
|| get_debug_policy_bool("/proc/device-tree/avf/guest/common/log").unwrap_or_default()
+ || get_debug_policy_bool("/proc/device-tree/avf/guest/microdroid/adb").unwrap_or_default()
+}
+
+/// Get whether debug apexes (MICRODROID_REQUIRED_APEXES_DEBUG) are required.
+pub fn should_include_debug_apexes(debug_level: DebugLevel) -> bool {
+ debug_level != DebugLevel::NONE
+ || get_debug_policy_bool("/proc/device-tree/avf/guest/microdroid/adb").unwrap_or_default()
}
/// Decision to support ramdump
diff --git a/virtualizationmanager/src/payload.rs b/virtualizationmanager/src/payload.rs
index 02e8f8e..99aea01 100644
--- a/virtualizationmanager/src/payload.rs
+++ b/virtualizationmanager/src/payload.rs
@@ -14,6 +14,7 @@
//! Payload disk image
+use crate::debug_config::should_include_debug_apexes;
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
DiskImage::DiskImage,
Partition::Partition,
@@ -382,7 +383,7 @@
debug_level: DebugLevel,
) -> Vec<&'a ApexInfo> {
let mut additional_apexes: Vec<&str> = MICRODROID_REQUIRED_APEXES.to_vec();
- if debug_level != DebugLevel::NONE {
+ if should_include_debug_apexes(debug_level) {
additional_apexes.extend(MICRODROID_REQUIRED_APEXES_DEBUG.to_vec());
}