Merge "Import translations. DO NOT MERGE ANYWHERE" into main
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/DebianServiceImpl.java b/android/TerminalApp/java/com/android/virtualization/terminal/DebianServiceImpl.java
index d167da3..9cf6093 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/DebianServiceImpl.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/DebianServiceImpl.java
@@ -30,6 +30,8 @@
import com.android.virtualization.terminal.proto.ReportVmActivePortsRequest;
import com.android.virtualization.terminal.proto.ReportVmActivePortsResponse;
import com.android.virtualization.terminal.proto.ReportVmIpAddrResponse;
+import com.android.virtualization.terminal.proto.ShutdownQueueOpeningRequest;
+import com.android.virtualization.terminal.proto.ShutdownRequestItem;
import io.grpc.stub.StreamObserver;
@@ -41,6 +43,7 @@
private final PortsStateManager mPortsStateManager;
private PortsStateManager.Listener mPortsStateListener;
private final DebianServiceCallback mCallback;
+ private Runnable mShutdownRunnable;
static {
System.loadLibrary("forwarder_host_jni");
@@ -93,6 +96,28 @@
responseObserver.onCompleted();
}
+ public boolean shutdownDebian() {
+ if (mShutdownRunnable == null) {
+ Log.d(TAG, "mShutdownRunnable is not ready.");
+ return false;
+ }
+ mShutdownRunnable.run();
+ return true;
+ }
+
+ @Override
+ public void openShutdownRequestQueue(
+ ShutdownQueueOpeningRequest request,
+ StreamObserver<ShutdownRequestItem> responseObserver) {
+ Log.d(TAG, "openShutdownRequestQueue");
+ mShutdownRunnable =
+ () -> {
+ responseObserver.onNext(ShutdownRequestItem.newBuilder().build());
+ responseObserver.onCompleted();
+ mShutdownRunnable = null;
+ };
+ }
+
@Keep
private static class ForwarderHostCallback {
private StreamObserver<ForwardingRequestItem> mResponseObserver;
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java b/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java
index 7f14179..b9910f7 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java
@@ -46,7 +46,7 @@
class ImageArchive {
private static final String DIR_IN_SDCARD = "linux";
private static final String ARCHIVE_NAME = "images.tar.gz";
- private static final String BUILD_TAG = "latest"; // TODO: use actual tag name
+ private static final String BUILD_TAG = Integer.toString(Build.VERSION.SDK_INT_FULL);
private static final String HOST_URL = "https://dl.google.com/android/ferrochrome/" + BUILD_TAG;
// Only one can be non-null
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.java b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.java
index a82c688..6d2c5bd 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.java
@@ -54,7 +54,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
-import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -144,7 +143,10 @@
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Objects.equals(intent.getAction(), ACTION_STOP_VM_LAUNCHER_SERVICE)) {
- stopSelf();
+ // If there is no Debian service or it fails to shutdown, just stop the service.
+ if (mDebianService == null || !mDebianService.shutdownDebian()) {
+ stopSelf();
+ }
return START_NOT_STICKY;
}
if (mVirtualMachine != null) {
@@ -293,12 +295,15 @@
public static void stop(Context context) {
Intent i = getMyIntent(context);
- context.stopService(i);
+ i.setAction(VmLauncherService.ACTION_STOP_VM_LAUNCHER_SERVICE);
+ context.startService(i);
}
@Override
public void onDestroy() {
- mPortNotifier.stop();
+ if (mPortNotifier != null) {
+ mPortNotifier.stop();
+ }
getSystemService(NotificationManager.class).cancelAll();
stopDebianServer();
if (mVirtualMachine != null) {
diff --git a/build/debian/build.sh b/build/debian/build.sh
index 9104adc..19894c2 100755
--- a/build/debian/build.sh
+++ b/build/debian/build.sh
@@ -192,6 +192,7 @@
build_rust_binary_and_copy forwarder_guest
build_rust_binary_and_copy forwarder_guest_launcher
build_rust_binary_and_copy ip_addr_reporter
+ build_rust_binary_and_copy shutdown_runner
}
run_fai() {
diff --git a/build/debian/fai_config/files/etc/systemd/system/shutdown_runner.service/AVF b/build/debian/fai_config/files/etc/systemd/system/shutdown_runner.service/AVF
new file mode 100644
index 0000000..bfb8afb
--- /dev/null
+++ b/build/debian/fai_config/files/etc/systemd/system/shutdown_runner.service/AVF
@@ -0,0 +1,11 @@
+[Unit]
+After=syslog.target
+After=network.target
+After=virtiofs_internal.service
+[Service]
+ExecStart=/usr/bin/bash -c '/usr/local/bin/shutdown_runner --grpc_port $(cat /mnt/internal/debian_service_port)'
+Type=simple
+User=root
+Group=root
+[Install]
+WantedBy=multi-user.target
diff --git a/build/debian/fai_config/scripts/AVF/10-systemd b/build/debian/fai_config/scripts/AVF/10-systemd
index 94838bc..119bec7 100755
--- a/build/debian/fai_config/scripts/AVF/10-systemd
+++ b/build/debian/fai_config/scripts/AVF/10-systemd
@@ -3,6 +3,7 @@
chmod +x $target/usr/local/bin/forwarder_guest
chmod +x $target/usr/local/bin/forwarder_guest_launcher
chmod +x $target/usr/local/bin/ip_addr_reporter
+chmod +x $target/usr/local/bin/shutdown_runner
chmod +x $target/usr/local/bin/ttyd
ln -s /etc/systemd/system/ttyd.service $target/etc/systemd/system/multi-user.target.wants/ttyd.service
ln -s /etc/systemd/system/ip_addr_reporter.service $target/etc/systemd/system/multi-user.target.wants/ip_addr_reporter.service
@@ -10,5 +11,6 @@
ln -s /etc/systemd/system/forwarder_guest_launcher.service $target/etc/systemd/system/multi-user.target.wants/forwarder_guest_launcher.service
ln -s /etc/systemd/system/virtiofs_internal.service $target/etc/systemd/system/multi-user.target.wants/virtiofs_internal.service
ln -s /etc/systemd/system/backup_mount.service $target/etc/systemd/system/multi-user.target.wants/backup_mount.service
+ln -s /etc/systemd/system/shutdown_runner.service $target/etc/systemd/system/multi-user.target.wants/shutdown_runner.service
sed -i 's/#LLMNR=yes/LLMNR=no/' $target/etc/systemd/resolved.conf
diff --git a/build/debian/release.sh b/build/debian/release.sh
index 437f9c8..8f89e21 100755
--- a/build/debian/release.sh
+++ b/build/debian/release.sh
@@ -83,7 +83,7 @@
local image=$(get_image_path ${arch} ${build_id})
local tag=${tag:-${build_id}}
- local serving_url=/android/ferrochrome/${arch}/${tag}/${image_filename}
+ local serving_url=/android/ferrochrome/${tag}/${arch}/${image_filename}
echo "Releasing ${image} to ${serving_url}"
local request='payload : { url_path: '"\"${serving_url}\""' source_path : '"\"${image}\""' }'
diff --git a/guest/pvmfw/src/entry.rs b/guest/pvmfw/src/entry.rs
index 2f0b391..343c2fc 100644
--- a/guest/pvmfw/src/entry.rs
+++ b/guest/pvmfw/src/entry.rs
@@ -81,7 +81,9 @@
// - can't access MMIO (except the console, already configured by vmbase)
match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
- Ok((entry, bcc)) => jump_to_payload(fdt_address, entry.try_into().unwrap(), bcc),
+ Ok((entry, bcc, keep_uart)) => {
+ jump_to_payload(fdt_address, entry.try_into().unwrap(), bcc, keep_uart)
+ }
Err(e) => {
const REBOOT_REASON_CONSOLE: usize = 1;
console_writeln!(REBOOT_REASON_CONSOLE, "{}", e.as_avf_reboot_string());
@@ -100,7 +102,7 @@
fdt: usize,
payload: usize,
payload_size: usize,
-) -> Result<(usize, Range<usize>), RebootReason> {
+) -> Result<(usize, Range<usize>, bool), RebootReason> {
// Limitations in this function:
// - only access MMIO once (and while) it has been mapped and configured
// - only perform logging once the logger has been initialized
@@ -136,6 +138,8 @@
config_entries.bcc,
config_entries.debug_policy,
)?;
+ // Keep UART MMIO_GUARD-ed for debuggable payloads, to enable earlycon.
+ let keep_uart = cfg!(debuggable_vms_improvements) && debuggable_payload;
// Writable-dirty regions will be flushed when MemoryTracker is dropped.
config_entries.bcc.zeroize();
@@ -144,24 +148,18 @@
error!("Failed to unshare MMIO ranges: {e}");
RebootReason::InternalError
})?;
- // Call unshare_all_memory here (instead of relying on the dtor) while UART is still mapped.
unshare_all_memory();
- if cfg!(debuggable_vms_improvements) && debuggable_payload {
- // Keep UART MMIO_GUARD-ed for debuggable payloads, to enable earlycon.
- } else {
- unshare_uart().map_err(|e| {
- error!("Failed to unshare the UART: {e}");
- RebootReason::InternalError
- })?;
+ Ok((slices.kernel.as_ptr() as usize, next_bcc, keep_uart))
+}
+
+fn jump_to_payload(fdt_address: u64, payload_start: u64, bcc: Range<usize>, keep_uart: bool) -> ! {
+ if !keep_uart {
+ unshare_uart().unwrap();
}
deactivate_dynamic_page_tables();
- Ok((slices.kernel.as_ptr() as usize, next_bcc))
-}
-
-fn jump_to_payload(fdt_address: u64, payload_start: u64, bcc: Range<usize>) -> ! {
const ASM_STP_ALIGN: usize = size_of::<u64>() * 2;
const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
// Stage 1 instruction access cacheability is unaffected.
diff --git a/guest/shutdown_runner/.gitignore b/guest/shutdown_runner/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/guest/shutdown_runner/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/guest/shutdown_runner/Cargo.toml b/guest/shutdown_runner/Cargo.toml
new file mode 100644
index 0000000..b74e7ee
--- /dev/null
+++ b/guest/shutdown_runner/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "shutdown_runner"
+version = "0.1.0"
+edition = "2021"
+license = "Apache-2.0"
+
+[dependencies]
+anyhow = "1.0.94"
+clap = { version = "4.5.20", features = ["derive"] }
+log = "0.4.22"
+netdev = "0.31.0"
+prost = "0.13.3"
+tokio = { version = "1.40.0", features = ["rt-multi-thread"] }
+tonic = "0.12.3"
+
+[build-dependencies]
+tonic-build = "0.12.3"
diff --git a/guest/shutdown_runner/build.rs b/guest/shutdown_runner/build.rs
new file mode 100644
index 0000000..e3939d4
--- /dev/null
+++ b/guest/shutdown_runner/build.rs
@@ -0,0 +1,7 @@
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let proto_file = "../../libs/debian_service/proto/DebianService.proto";
+
+ tonic_build::compile_protos(proto_file).unwrap();
+
+ Ok(())
+}
diff --git a/guest/shutdown_runner/src/main.rs b/guest/shutdown_runner/src/main.rs
new file mode 100644
index 0000000..19e9883
--- /dev/null
+++ b/guest/shutdown_runner/src/main.rs
@@ -0,0 +1,46 @@
+use api::debian_service_client::DebianServiceClient;
+use api::ShutdownQueueOpeningRequest;
+use std::process::Command;
+
+use anyhow::anyhow;
+use clap::Parser;
+use log::debug;
+pub mod api {
+ tonic::include_proto!("com.android.virtualization.terminal.proto");
+}
+
+#[derive(Parser)]
+/// Flags for running command
+pub struct Args {
+ /// grpc port number
+ #[arg(long)]
+ #[arg(alias = "grpc_port")]
+ grpc_port: String,
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let args = Args::parse();
+ let gateway_ip_addr = netdev::get_default_gateway()?.ipv4[0];
+
+ let server_addr = format!("http://{}:{}", gateway_ip_addr.to_string(), args.grpc_port);
+
+ debug!("connect to grpc server {}", server_addr);
+
+ let mut client = DebianServiceClient::connect(server_addr).await.map_err(|e| e.to_string())?;
+
+ let mut res_stream = client
+ .open_shutdown_request_queue(tonic::Request::new(ShutdownQueueOpeningRequest {}))
+ .await?
+ .into_inner();
+
+ while let Some(_response) = res_stream.message().await? {
+ let status = Command::new("poweroff").status().expect("power off");
+ if !status.success() {
+ return Err(anyhow!("Failed to power off: {status}").into());
+ }
+ debug!("poweroff");
+ break;
+ }
+ Ok(())
+}
diff --git a/libs/debian_service/proto/DebianService.proto b/libs/debian_service/proto/DebianService.proto
index 61bcece..739f0ac 100644
--- a/libs/debian_service/proto/DebianService.proto
+++ b/libs/debian_service/proto/DebianService.proto
@@ -25,6 +25,7 @@
rpc ReportVmActivePorts (ReportVmActivePortsRequest) returns (ReportVmActivePortsResponse) {}
rpc ReportVmIpAddr (IpAddr) returns (ReportVmIpAddrResponse) {}
rpc OpenForwardingRequestQueue (QueueOpeningRequest) returns (stream ForwardingRequestItem) {}
+ rpc OpenShutdownRequestQueue (ShutdownQueueOpeningRequest) returns (stream ShutdownRequestItem) {}
}
message QueueOpeningRequest {
@@ -51,3 +52,7 @@
int32 guest_tcp_port = 1;
int32 vsock_port = 2;
}
+
+message ShutdownQueueOpeningRequest {}
+
+message ShutdownRequestItem {}
\ No newline at end of file
diff --git a/tests/pvmfw/java/com/android/pvmfw/test/CustomPvmfwHostTestCaseBase.java b/tests/pvmfw/java/com/android/pvmfw/test/CustomPvmfwHostTestCaseBase.java
index 3116cc5..296604b 100644
--- a/tests/pvmfw/java/com/android/pvmfw/test/CustomPvmfwHostTestCaseBase.java
+++ b/tests/pvmfw/java/com/android/pvmfw/test/CustomPvmfwHostTestCaseBase.java
@@ -52,6 +52,7 @@
public static final String VM_REFERENCE_DT_PATH = "/data/local/tmp/pvmfw/reference_dt.dtb";
@NonNull public static final String MICRODROID_LOG_PATH = TEST_ROOT + "log.txt";
+ @NonNull public static final String MICRODROID_CONSOLE_PATH = TEST_ROOT + "console.txt";
public static final int BOOT_COMPLETE_TIMEOUT_MS = 30000; // 30 seconds
public static final int BOOT_FAILURE_WAIT_TIME_MS = 10000; // 10 seconds
public static final int CONSOLE_OUTPUT_WAIT_MS = 5000; // 5 seconds
diff --git a/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java b/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
index 7efbbc7..6a28d5e 100644
--- a/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
+++ b/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
@@ -248,6 +248,8 @@
"run-app",
"--log",
MICRODROID_LOG_PATH,
+ "--console",
+ MICRODROID_CONSOLE_PATH,
"--protected",
getPathForPackage(PACKAGE_NAME),
TEST_ROOT + "idsig",