Snap for 13190096 from e635c66c4e611f1e28be3f57bcaf12ba79c4d770 to 25Q2-release

Change-Id: I1b89877691968f5273ee362fb437041028b70b0f
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/Application.kt b/android/TerminalApp/java/com/android/virtualization/terminal/Application.kt
index 9f4909d..c427337 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/Application.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/Application.kt
@@ -97,14 +97,6 @@
             super.onStop(owner)
         }
 
-        override fun onDestroy(owner: LifecycleOwner) {
-            if (vmLauncherService != null) {
-                this@Application.unbindService(connection)
-                vmLauncherService = null
-            }
-            super.onDestroy(owner)
-        }
-
         fun bindToVmLauncherService() {
             val intent = Intent(this@Application, VmLauncherService::class.java)
             this@Application.bindService(intent, connection, 0) // No BIND_AUTO_CREATE
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt b/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt
index 547f1a7..d02491b 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt
@@ -30,6 +30,7 @@
 import java.nio.file.Files
 import java.nio.file.Path
 import java.nio.file.StandardOpenOption
+import java.time.LocalDateTime
 import java.util.concurrent.ExecutorService
 import libcore.io.Streams
 
@@ -37,14 +38,21 @@
  * Forwards VM's console output to a file on the Android side, and VM's log output to Android logd.
  */
 internal object Logger {
-    fun setup(vm: VirtualMachine, path: Path, executor: ExecutorService) {
+    fun setup(vm: VirtualMachine, dir: Path, executor: ExecutorService) {
+        val tag = vm.name
+
         if (vm.config.debugLevel != VirtualMachineConfig.DEBUG_LEVEL_FULL) {
+            Log.i(tag, "Logs are not captured. Non-debuggable VM.")
             return
         }
 
         try {
+            Files.createDirectories(dir)
+            deleteOldLogs(dir, 10)
+            val logPath = dir.resolve(LocalDateTime.now().toString() + ".txt")
             val console = vm.getConsoleOutput()
-            val file = Files.newOutputStream(path, StandardOpenOption.CREATE)
+            val file =
+                Files.newOutputStream(logPath, StandardOpenOption.CREATE)
             executor.submit<Int?> {
                 console.use { console ->
                     LineBufferedOutputStream(file).use { fileOutput ->
@@ -54,7 +62,7 @@
             }
 
             val log = vm.getLogOutput()
-            executor.submit<Unit> { log.use { writeToLogd(it, vm.name) } }
+            executor.submit<Unit> { log.use { writeToLogd(it, tag) } }
         } catch (e: VirtualMachineException) {
             throw RuntimeException(e)
         } catch (e: IOException) {
@@ -62,12 +70,32 @@
         }
     }
 
+    fun deleteOldLogs(dir: Path, numLogsToKeep: Long) {
+        Files.list(dir)
+            .filter { Files.isRegularFile(it) }
+            .sorted(
+                Comparator.comparingLong { f: Path ->
+                        // for some reason, type inference didn't work here!
+                        Files.getLastModifiedTime(f).toMillis()
+                    }
+                    .reversed()
+            )
+            .skip(numLogsToKeep)
+            .forEach {
+                try {
+                    Files.delete(it)
+                } catch (e: IOException) {
+                    // don't bother
+                }
+            }
+    }
+
     @Throws(IOException::class)
-    private fun writeToLogd(input: InputStream?, vmName: String?) {
+    private fun writeToLogd(input: InputStream?, tag: String?) {
         val reader = BufferedReader(InputStreamReader(input))
         reader
             .useLines { lines -> lines.takeWhile { !Thread.interrupted() } }
-            .forEach { Log.d(vmName, it) }
+            .forEach { Log.d(tag, it) }
     }
 
     private class LineBufferedOutputStream(out: OutputStream?) : BufferedOutputStream(out) {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
index eee0ada..aa1898f 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
@@ -188,10 +188,7 @@
         val displaySize = intent.getParcelableExtra(EXTRA_DISPLAY_INFO, DisplayInfo::class.java)
 
         customImageConfigBuilder.setAudioConfig(
-            AudioConfig.Builder()
-                .setUseSpeaker(true)
-                .setUseMicrophone(true)
-                .build()
+            AudioConfig.Builder().setUseSpeaker(true).setUseMicrophone(true).build()
         )
         if (overrideConfigIfNecessary(customImageConfigBuilder, displaySize)) {
             configBuilder.setCustomImageConfig(customImageConfigBuilder.build())
@@ -219,8 +216,8 @@
             resultReceiver?.send(if (success) RESULT_STOP else RESULT_ERROR, null)
             stopSelf()
         }
-        val logPath = getFileStreamPath(virtualMachine!!.name + ".log").toPath()
-        Logger.setup(virtualMachine!!, logPath, executorService!!)
+        val logDir = getFileStreamPath(virtualMachine!!.name + ".log").toPath()
+        Logger.setup(virtualMachine!!, logDir, executorService!!)
 
         val notification =
             intent.getParcelableExtra<Notification?>(EXTRA_NOTIFICATION, Notification::class.java)
diff --git a/guest/forwarder_guest_launcher/debian/service b/guest/forwarder_guest_launcher/debian/service
index ad57a26..7812d67 100644
--- a/guest/forwarder_guest_launcher/debian/service
+++ b/guest/forwarder_guest_launcher/debian/service
@@ -5,7 +5,7 @@
 After=virtiofs_internal.service
 
 [Service]
-ExecStart=/usr/bin/bash -c '/usr/bin/forwarder_guest_launcher --grpc_port $(cat /mnt/internal/debian_service_port)'
+ExecStart=/usr/bin/bash -c '/usr/bin/forwarder_guest_launcher --grpc-port-file /mnt/internal/debian_service_port'
 Type=simple
 Restart=on-failure
 RestartSec=1
diff --git a/guest/forwarder_guest_launcher/src/main.rs b/guest/forwarder_guest_launcher/src/main.rs
index f4c8ca9..3cb557a 100644
--- a/guest/forwarder_guest_launcher/src/main.rs
+++ b/guest/forwarder_guest_launcher/src/main.rs
@@ -52,10 +52,9 @@
 #[derive(Parser)]
 /// Flags for running command
 pub struct Args {
-    /// grpc port number
+    /// path to a file where grpc port number is written
     #[arg(long)]
-    #[arg(alias = "grpc_port")]
-    grpc_port: String,
+    grpc_port_file: String,
 }
 
 async fn process_forwarding_request_queue(
@@ -163,11 +162,23 @@
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
-    env_logger::init();
+    env_logger::builder().filter_level(log::LevelFilter::Debug).init();
     debug!("Starting forwarder_guest_launcher");
     let args = Args::parse();
     let gateway_ip_addr = netdev::get_default_gateway()?.ipv4[0];
-    let addr = format!("https://{}:{}", gateway_ip_addr.to_string(), args.grpc_port);
+
+    // Wait for `grpc_port_file` becomes available.
+    const GRPC_PORT_MAX_RETRY_COUNT: u32 = 10;
+    for _ in 0..GRPC_PORT_MAX_RETRY_COUNT {
+        if std::path::Path::new(&args.grpc_port_file).exists() {
+            break;
+        }
+        debug!("{} does not exist. Wait 1 second", args.grpc_port_file);
+        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
+    }
+    let grpc_port = std::fs::read_to_string(&args.grpc_port_file)?.trim().to_string();
+
+    let addr = format!("https://{}:{}", gateway_ip_addr.to_string(), grpc_port);
     let channel = Endpoint::from_shared(addr)?.connect().await?;
     let client = DebianServiceClient::new(channel);
 
diff --git a/guest/shutdown_runner/Cargo.toml b/guest/shutdown_runner/Cargo.toml
index 0b44baa..92f8762 100644
--- a/guest/shutdown_runner/Cargo.toml
+++ b/guest/shutdown_runner/Cargo.toml
@@ -7,6 +7,7 @@
 [dependencies]
 anyhow = "1.0.94"
 clap = { version = "4.5.20", features = ["derive"] }
+env_logger = "0.11.5"
 log = "0.4.22"
 netdev = "0.31.0"
 prost = "0.13.3"
diff --git a/guest/shutdown_runner/debian/service b/guest/shutdown_runner/debian/service
index 2668930..a5249d0 100644
--- a/guest/shutdown_runner/debian/service
+++ b/guest/shutdown_runner/debian/service
@@ -4,7 +4,7 @@
 After=virtiofs_internal.service
 
 [Service]
-ExecStart=/usr/bin/bash -c '/usr/bin/shutdown_runner --grpc_port $(cat /mnt/internal/debian_service_port)'
+ExecStart=/usr/bin/bash -c '/usr/bin/shutdown_runner --grpc-port-file /mnt/internal/debian_service_port'
 Type=simple
 Restart=on-failure
 RestartSec=1
diff --git a/guest/shutdown_runner/src/main.rs b/guest/shutdown_runner/src/main.rs
index 19e9883..4043002 100644
--- a/guest/shutdown_runner/src/main.rs
+++ b/guest/shutdown_runner/src/main.rs
@@ -1,3 +1,17 @@
+// 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.
+
 use api::debian_service_client::DebianServiceClient;
 use api::ShutdownQueueOpeningRequest;
 use std::process::Command;
@@ -12,18 +26,28 @@
 #[derive(Parser)]
 /// Flags for running command
 pub struct Args {
-    /// grpc port number
+    /// Path to a file where grpc port number is written
     #[arg(long)]
-    #[arg(alias = "grpc_port")]
-    grpc_port: String,
+    grpc_port_file: String,
 }
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    env_logger::builder().filter_level(log::LevelFilter::Debug).init();
     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);
+    // Wait for `grpc_port_file` becomes available.
+    const GRPC_PORT_MAX_RETRY_COUNT: u32 = 10;
+    for _ in 0..GRPC_PORT_MAX_RETRY_COUNT {
+        if std::path::Path::new(&args.grpc_port_file).exists() {
+            break;
+        }
+        debug!("{} does not exist. Wait 1 second", args.grpc_port_file);
+        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
+    }
+    let grpc_port = std::fs::read_to_string(&args.grpc_port_file)?.trim().to_string();
+    let server_addr = format!("http://{}:{}", gateway_ip_addr.to_string(), grpc_port);
 
     debug!("connect to grpc server {}", server_addr);