Snap for 13197820 from dc2cb4c5198cb80c860a180fe1ec8df2f95fa640 to 25Q2-release

Change-Id: I727fded3f3ae62ed03c0bd53c095ad077ddaab28
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt b/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt
index d02491b..4162247 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt
@@ -51,8 +51,7 @@
             deleteOldLogs(dir, 10)
             val logPath = dir.resolve(LocalDateTime.now().toString() + ".txt")
             val console = vm.getConsoleOutput()
-            val file =
-                Files.newOutputStream(logPath, StandardOpenOption.CREATE)
+            val file = Files.newOutputStream(logPath, StandardOpenOption.CREATE)
             executor.submit<Int?> {
                 console.use { console ->
                     LineBufferedOutputStream(file).use { fileOutput ->
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
index f6eeff9..662fef5 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
@@ -24,8 +24,6 @@
 import android.graphics.drawable.Icon
 import android.graphics.fonts.FontStyle
 import android.net.Uri
-import android.net.nsd.NsdManager
-import android.net.nsd.NsdServiceInfo
 import android.os.Build
 import android.os.Bundle
 import android.os.ConditionVariable
@@ -62,6 +60,7 @@
 import java.io.IOException
 import java.net.MalformedURLException
 import java.net.URL
+import java.util.concurrent.CompletableFuture
 import java.util.concurrent.ExecutorService
 import java.util.concurrent.Executors
 
@@ -78,12 +77,11 @@
     private lateinit var image: InstalledImage
     private lateinit var accessibilityManager: AccessibilityManager
     private lateinit var manageExternalStorageActivityResultLauncher: ActivityResultLauncher<Intent>
-    private var ipAddress: String? = null
-    private var port: Int? = null
     private lateinit var terminalViewModel: TerminalViewModel
     private lateinit var viewPager: ViewPager2
     private lateinit var tabLayout: TabLayout
     private lateinit var terminalTabAdapter: TerminalTabAdapter
+    private val terminalInfo = CompletableFuture<TerminalInfo>()
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -243,40 +241,12 @@
     }
 
     fun connectToTerminalService(terminalView: TerminalView) {
-        if (ipAddress != null && port != null) {
-            val url = getTerminalServiceUrl(ipAddress, port!!)
-            terminalView.loadUrl(url.toString())
-            return
-        }
-        // TODO: refactor this block as a method
-        val nsdManager = getSystemService<NsdManager>(NsdManager::class.java)
-        val info = NsdServiceInfo()
-        info.serviceType = "_http._tcp"
-        info.serviceName = "ttyd"
-        nsdManager.registerServiceInfoCallback(
-            info,
-            executorService,
-            object : NsdManager.ServiceInfoCallback {
-                var loaded: Boolean = false
-
-                override fun onServiceInfoCallbackRegistrationFailed(errorCode: Int) {}
-
-                override fun onServiceInfoCallbackUnregistered() {}
-
-                override fun onServiceLost() {}
-
-                override fun onServiceUpdated(info: NsdServiceInfo) {
-                    Log.i(TAG, "Service found: $info")
-                    if (!loaded) {
-                        ipAddress = info.hostAddresses[0].hostAddress
-                        port = info.port
-                        val url = getTerminalServiceUrl(ipAddress, port!!)
-                        loaded = true
-                        nsdManager.unregisterServiceInfoCallback(this)
-                        runOnUiThread(Runnable { terminalView.loadUrl(url.toString()) })
-                    }
-                }
+        terminalInfo.thenAcceptAsync(
+            { info ->
+                val url = getTerminalServiceUrl(info.ipAddress, info.port)
+                runOnUiThread({ terminalView.loadUrl(url.toString()) })
             },
+            executorService,
         )
     }
 
@@ -292,6 +262,10 @@
         Log.i(TAG, "onVmStart()")
     }
 
+    override fun onTerminalAvailable(info: TerminalInfo) {
+        terminalInfo.complete(info)
+    }
+
     override fun onVmStop() {
         Log.i(TAG, "onVmStop()")
         finish()
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
index aa1898f..345e8dd 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
@@ -55,11 +55,12 @@
 import java.io.File
 import java.io.FileOutputStream
 import java.io.IOException
-import java.lang.RuntimeException
 import java.lang.Math.min
+import java.lang.RuntimeException
 import java.net.InetSocketAddress
 import java.net.SocketAddress
 import java.nio.file.Files
+import java.util.concurrent.CompletableFuture
 import java.util.concurrent.ExecutorService
 import java.util.concurrent.Executors
 
@@ -78,43 +79,47 @@
     private var debianService: DebianServiceImpl? = null
     private var portNotifier: PortNotifier? = null
     private var mLock = Object()
-    @GuardedBy("mLock")
-    private var currentMemBalloonPercent = 0;
+    @GuardedBy("mLock") private var currentMemBalloonPercent = 0
 
-    @GuardedBy("mLock")
-    private val inflateMemBalloonHandler = Handler(Looper.getMainLooper())
-    private val inflateMemBalloonTask: Runnable = object : Runnable {
-        override fun run() {
-            synchronized(mLock) {
-                if (currentMemBalloonPercent < INITIAL_MEM_BALLOON_PERCENT
-                    || currentMemBalloonPercent > MAX_MEM_BALLOON_PERCENT
-                ) {
-                    Log.e(
-                        TAG, "currentBalloonPercent=$currentMemBalloonPercent is invalid," +
+    @GuardedBy("mLock") private val inflateMemBalloonHandler = Handler(Looper.getMainLooper())
+    private val inflateMemBalloonTask: Runnable =
+        object : Runnable {
+            override fun run() {
+                synchronized(mLock) {
+                    if (
+                        currentMemBalloonPercent < INITIAL_MEM_BALLOON_PERCENT ||
+                            currentMemBalloonPercent > MAX_MEM_BALLOON_PERCENT
+                    ) {
+                        Log.e(
+                            TAG,
+                            "currentBalloonPercent=$currentMemBalloonPercent is invalid," +
                                 " should be in range: " +
-                                "$INITIAL_MEM_BALLOON_PERCENT~$MAX_MEM_BALLOON_PERCENT"
-                    )
-                    return
-                }
-                // Increases the balloon size by MEM_BALLOON_PERCENT_STEP% every time
-                if (currentMemBalloonPercent < MAX_MEM_BALLOON_PERCENT) {
-                    currentMemBalloonPercent =
-                        min(
-                            MAX_MEM_BALLOON_PERCENT,
-                            currentMemBalloonPercent + MEM_BALLOON_PERCENT_STEP
+                                "$INITIAL_MEM_BALLOON_PERCENT~$MAX_MEM_BALLOON_PERCENT",
                         )
-                    virtualMachine?.setMemoryBalloonByPercent(currentMemBalloonPercent)
-                    inflateMemBalloonHandler.postDelayed(this,
-                        MEM_BALLOON_INFLATE_INTERVAL_MILLIS)
+                        return
+                    }
+                    // Increases the balloon size by MEM_BALLOON_PERCENT_STEP% every time
+                    if (currentMemBalloonPercent < MAX_MEM_BALLOON_PERCENT) {
+                        currentMemBalloonPercent =
+                            min(
+                                MAX_MEM_BALLOON_PERCENT,
+                                currentMemBalloonPercent + MEM_BALLOON_PERCENT_STEP,
+                            )
+                        virtualMachine?.setMemoryBalloonByPercent(currentMemBalloonPercent)
+                        inflateMemBalloonHandler.postDelayed(
+                            this,
+                            MEM_BALLOON_INFLATE_INTERVAL_MILLIS,
+                        )
+                    }
                 }
             }
         }
-    }
-
 
     interface VmLauncherServiceCallback {
         fun onVmStart()
 
+        fun onTerminalAvailable(info: TerminalInfo)
+
         fun onVmStop()
 
         fun onVmError()
@@ -136,8 +141,8 @@
             // This gives the app maximum available memory.
             ApplicationLifeCycleEvent.APP_ON_START -> {
                 synchronized(mLock) {
-                    inflateMemBalloonHandler.removeCallbacks(inflateMemBalloonTask);
-                    currentMemBalloonPercent = 0;
+                    inflateMemBalloonHandler.removeCallbacks(inflateMemBalloonTask)
+                    currentMemBalloonPercent = 0
                     virtualMachine?.setMemoryBalloonByPercent(currentMemBalloonPercent)
                 }
             }
@@ -148,12 +153,12 @@
                 // MAX_MEM_BALLOON_PERCENT of total memory. This allows the system to reclaim
                 // memory while the app is in the background.
                 synchronized(mLock) {
-                    currentMemBalloonPercent = INITIAL_MEM_BALLOON_PERCENT;
+                    currentMemBalloonPercent = INITIAL_MEM_BALLOON_PERCENT
                     virtualMachine?.setMemoryBalloonByPercent(currentMemBalloonPercent)
                     inflateMemBalloonHandler.postDelayed(
                         inflateMemBalloonTask,
-                        MEM_BALLOON_INFLATE_INTERVAL_MILLIS
-                    );
+                        MEM_BALLOON_INFLATE_INTERVAL_MILLIS,
+                    )
                 }
             }
             else -> {
@@ -228,35 +233,56 @@
 
         portNotifier = PortNotifier(this)
 
-        // TODO: dedup this part
+        getTerminalServiceInfo()
+            .thenAcceptAsync(
+                { info ->
+                    val ipAddress = info.hostAddresses[0].hostAddress
+                    val port = info.port
+                    val bundle = Bundle()
+                    bundle.putString(KEY_TERMINAL_IPADDRESS, ipAddress)
+                    bundle.putInt(KEY_TERMINAL_PORT, port)
+                    resultReceiver!!.send(RESULT_TERMINAL_AVAIL, bundle)
+                    startDebianServer(ipAddress)
+                },
+                executorService,
+            )
+
+        return START_NOT_STICKY
+    }
+
+    private fun getTerminalServiceInfo(): CompletableFuture<NsdServiceInfo> {
+        val executor = Executors.newSingleThreadExecutor(TerminalThreadFactory(applicationContext))
         val nsdManager = getSystemService<NsdManager?>(NsdManager::class.java)
-        val info = NsdServiceInfo()
-        info.serviceType = "_http._tcp"
-        info.serviceName = "ttyd"
+        val queryInfo = NsdServiceInfo()
+        queryInfo.serviceType = "_http._tcp"
+        queryInfo.serviceName = "ttyd"
+        var resolvedInfo = CompletableFuture<NsdServiceInfo>()
+
         nsdManager.registerServiceInfoCallback(
-            info,
-            executorService!!,
+            queryInfo,
+            executor,
             object : NsdManager.ServiceInfoCallback {
-                var started: Boolean = false
+                var found: Boolean = false
 
                 override fun onServiceInfoCallbackRegistrationFailed(errorCode: Int) {}
 
-                override fun onServiceInfoCallbackUnregistered() {}
+                override fun onServiceInfoCallbackUnregistered() {
+                    executor.shutdown()
+                }
 
                 override fun onServiceLost() {}
 
                 override fun onServiceUpdated(info: NsdServiceInfo) {
                     Log.i(TAG, "Service found: $info")
-                    if (!started) {
-                        started = true
+                    if (!found) {
+                        found = true
                         nsdManager.unregisterServiceInfoCallback(this)
-                        startDebianServer(info.hostAddresses[0].hostAddress)
+                        resolvedInfo.complete(info)
                     }
                 }
             },
         )
-
-        return START_NOT_STICKY
+        return resolvedInfo
     }
 
     private fun createNotificationForTerminalClose(): Notification {
@@ -436,11 +462,15 @@
         private const val RESULT_START = 0
         private const val RESULT_STOP = 1
         private const val RESULT_ERROR = 2
+        private const val RESULT_TERMINAL_AVAIL = 3
+
+        private const val KEY_TERMINAL_IPADDRESS = "address"
+        private const val KEY_TERMINAL_PORT = "port"
 
         private const val INITIAL_MEM_BALLOON_PERCENT = 10
         private const val MAX_MEM_BALLOON_PERCENT = 50
         private const val MEM_BALLOON_INFLATE_INTERVAL_MILLIS = 60000L
-        private const val MEM_BALLOON_PERCENT_STEP = 5;
+        private const val MEM_BALLOON_PERCENT_STEP = 5
 
         private fun getMyIntent(context: Context): Intent {
             return Intent(context.getApplicationContext(), VmLauncherService::class.java)
@@ -461,6 +491,11 @@
                         }
                         when (resultCode) {
                             RESULT_START -> callback.onVmStart()
+                            RESULT_TERMINAL_AVAIL -> {
+                                val ipAddress = resultData!!.getString(KEY_TERMINAL_IPADDRESS)
+                                val port = resultData!!.getInt(KEY_TERMINAL_PORT)
+                                callback.onTerminalAvailable(TerminalInfo(ipAddress!!, port))
+                            }
                             RESULT_STOP -> callback.onVmStop()
                             RESULT_ERROR -> callback.onVmError()
                         }
@@ -487,6 +522,8 @@
     }
 }
 
+data class TerminalInfo(val ipAddress: String, val port: Int)
+
 data class DisplayInfo(val width: Int, val height: Int, val dpi: Int, val refreshRate: Int) :
     Parcelable {
     constructor(
diff --git a/guest/trusty/test_vm/Android.bp b/guest/trusty/test_vm/Android.bp
index 06b7d9d..676e231 100644
--- a/guest/trusty/test_vm/Android.bp
+++ b/guest/trusty/test_vm/Android.bp
@@ -34,6 +34,30 @@
     filename: "trusty-test_vm-config.json",
 }
 
+prebuilt_etc {
+    name: "trusty_vm_launcher_sh",
+    enabled: false,
+    arch: {
+        arm64: {
+            enabled: true,
+        },
+    },
+    src: "trusty-vm-launcher.sh",
+    filename: "trusty-vm-launcher.sh",
+}
+
+prebuilt_etc {
+    name: "trusty_wait_ready_sh",
+    enabled: false,
+    arch: {
+        arm64: {
+            enabled: true,
+        },
+    },
+    src: "trusty-wait-ready.sh",
+    filename: "trusty-wait-ready.sh",
+}
+
 sh_test {
     // VTS tests for all Trusted HALs defined
     // under hardware/interfaces/security/see
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
index 40050c0..ad63206 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
@@ -1807,7 +1807,7 @@
      * {@linkplain #connectToVsockServer binder request}, and wait for {@link
      * VirtualMachineCallback#onPayloadFinished} to be called.
      *
-     * <p>A stopped virtual machine can be re-started by calling {@link #run()}.
+     * <p>A stopped virtual machine cannot be re-started.
      *
      * <p>NOTE: This method may block and should not be called on the main thread.
      *
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 5513af6..2434ed0 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -101,7 +101,7 @@
     private static final String INSTANCE_IMG = TEST_ROOT + "instance.img";
     private static final String INSTANCE_ID_FILE = TEST_ROOT + "instance_id";
 
-    private static final String DEBUG_LEVEL_FULL = "full --enable-earlycon";
+    private static final String DEBUG_LEVEL_FULL = "full";
     private static final String DEBUG_LEVEL_NONE = "none";
 
     private static final int MIN_MEM_ARM64 = 170;
@@ -555,6 +555,7 @@
             throws Exception {
         // Preconditions
         assumeKernelSupported(os);
+        assumeVmTypeSupported(os, false);
 
         File key = findTestFile("test.com.android.virt.pem");
         Map<String, File> keyOverrides = Map.of();
@@ -582,6 +583,7 @@
     public void testBootFailsWhenVbMetaDigestDoesNotMatchBootconfig(String os) throws Exception {
         // protectedVmWithImageSignedWithDifferentKeyRunsPvmfw() is the protected case.
         assumeKernelSupported(os);
+        assumeVmTypeSupported(os, false);
 
         // Sign everything with key1 except vbmeta
         File key = findTestFile("test.com.android.virt.pem");
@@ -1155,6 +1157,8 @@
     @Test
     @CddTest
     public void testRunEmptyPayload() throws Exception {
+        assumeVmTypeSupported("microdroid", false);
+
         CommandRunner android = new CommandRunner(getDevice());
 
         // Create the idsig file for the APK