Merge "Import translations. DO NOT MERGE ANYWHERE" into main
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/CertificateUtils.kt b/android/TerminalApp/java/com/android/virtualization/terminal/CertificateUtils.kt
index 53809e5..ee8a8f8 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/CertificateUtils.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/CertificateUtils.kt
@@ -39,7 +39,6 @@
 object CertificateUtils {
     private const val ALIAS = "ttyd"
 
-    @JvmStatic
     fun createOrGetKey(): KeyStore.PrivateKeyEntry {
         try {
             val ks = KeyStore.getInstance("AndroidKeyStore")
@@ -87,7 +86,6 @@
         kpg.generateKeyPair()
     }
 
-    @JvmStatic
     fun writeCertificateToFile(context: Context, cert: Certificate) {
         val certFile = File(context.getFilesDir(), "ca.crt")
         try {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.kt
index f253f86..0a1090d 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.kt
@@ -58,7 +58,6 @@
     companion object {
         private const val EXTRA_CAUSE = "cause"
 
-        @JvmStatic
         fun start(context: Context, e: Exception) {
             val intent = Intent(context, ErrorActivity::class.java)
             intent.putExtra(EXTRA_CAUSE, e)
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.kt b/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.kt
index 86d9db6..017ff89 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.kt
@@ -141,7 +141,6 @@
         private const val BUILD_TAG = "latest" // TODO: use actual tag name
         private const val HOST_URL = "https://dl.google.com/android/ferrochrome/$BUILD_TAG"
 
-        @JvmStatic
         fun getSdcardPathForTesting(): Path {
             return Environment.getExternalStoragePublicDirectory(DIR_IN_SDCARD).toPath()
         }
@@ -149,7 +148,6 @@
         /**
          * Creates ImageArchive which is located in the sdcard. This archive is for testing only.
          */
-        @JvmStatic
         fun fromSdCard(): ImageArchive {
             return ImageArchive(getSdcardPathForTesting().resolve(ARCHIVE_NAME))
         }
@@ -157,7 +155,6 @@
         /**
          * Creates ImageArchive which is hosted in the Google server. This is the official archive.
          */
-        @JvmStatic
         fun fromInternet(): ImageArchive {
             val arch =
                 if (listOf<String?>(*Build.SUPPORTED_ABIS).contains("x86_64")) "x86_64"
@@ -174,7 +171,6 @@
          * Creates ImageArchive from either SdCard or Internet. SdCard is used only when the build
          * is debuggable and the file actually exists.
          */
-        @JvmStatic
         fun getDefault(): ImageArchive {
             val archive = fromSdCard()
             return if (Build.isDebuggable() && archive.exists()) {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt
index db918fe..7acc5f3 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt
@@ -34,7 +34,7 @@
 import kotlin.math.ceil
 
 /** Collection of files that consist of a VM image. */
-internal class InstalledImage private constructor(val installDir: Path) {
+public class InstalledImage private constructor(val installDir: Path) {
     private val rootPartition: Path = installDir.resolve(ROOTFS_FILENAME)
     val backupFile: Path = installDir.resolve(BACKUP_FILENAME)
 
@@ -139,7 +139,6 @@
         const val RESIZE_STEP_BYTES: Long = 4 shl 20 // 4 MiB
 
         /** Returns InstalledImage for a given app context */
-        @JvmStatic
         fun getDefault(context: Context): InstalledImage {
             val installDir = context.getFilesDir().toPath().resolve(INSTALL_DIRNAME)
             return InstalledImage(installDir)
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.kt
index 13cd983..ec4a8c4 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.kt
@@ -43,7 +43,7 @@
 import java.lang.Exception
 import java.lang.ref.WeakReference
 
-class InstallerActivity : BaseActivity() {
+public class InstallerActivity : BaseActivity() {
     private lateinit var waitForWifiCheckbox: CheckBox
     private lateinit var installButton: TextView
 
@@ -125,7 +125,7 @@
     }
 
     @VisibleForTesting
-    fun waitForInstallCompleted(timeoutMillis: Long): Boolean {
+    public fun waitForInstallCompleted(timeoutMillis: Long): Boolean {
         return installCompleted.block(timeoutMillis)
     }
 
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt b/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt
index 3a273ec..547f1a7 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/Logger.kt
@@ -37,7 +37,6 @@
  * Forwards VM's console output to a file on the Android side, and VM's log output to Android logd.
  */
 internal object Logger {
-    @JvmStatic
     fun setup(vm: VirtualMachine, path: Path, executor: ExecutorService) {
         if (vm.config.debugLevel != VirtualMachineConfig.DEBUG_LEVEL_FULL) {
             return
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
index b90115f..5e039d9 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
@@ -73,7 +73,7 @@
 import java.util.concurrent.ExecutorService
 import java.util.concurrent.Executors
 
-class MainActivity :
+public class MainActivity :
     BaseActivity(),
     VmLauncherServiceCallback,
     AccessibilityManager.AccessibilityStateChangeListener {
@@ -99,7 +99,6 @@
         val toolbar = findViewById<MaterialToolbar>(R.id.toolbar)
         setSupportActionBar(toolbar)
         terminalView = findViewById<TerminalView>(R.id.webview)
-        terminalView.getSettings().setDatabaseEnabled(true)
         terminalView.getSettings().setDomStorageEnabled(true)
         terminalView.getSettings().setJavaScriptEnabled(true)
         terminalView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE)
@@ -389,10 +388,9 @@
         modifierKeys.visibility = if (showModifierKeys) View.VISIBLE else View.GONE
     }
 
-    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
-        super.onActivityResult(requestCode, resultCode, data)
-
-        if (requestCode == REQUEST_CODE_INSTALLER) {
+    private val installerLauncher =
+        registerForActivityResult(StartActivityForResult()) { result ->
+            val resultCode = result.resultCode
             if (resultCode != RESULT_OK) {
                 Log.e(TAG, "Failed to start VM. Installer returned error.")
                 finish()
@@ -403,14 +401,13 @@
                 startVm()
             }
         }
-    }
 
     private fun installIfNecessary(): Boolean {
         // If payload from external storage exists(only for debuggable build) or there is no
         // installed image, launch installer activity.
         if (!image.isInstalled()) {
             val intent = Intent(this, InstallerActivity::class.java)
-            startActivityForResult(intent, REQUEST_CODE_INSTALLER)
+            installerLauncher.launch(intent)
             return true
         }
         return false
@@ -477,7 +474,7 @@
     }
 
     @VisibleForTesting
-    fun waitForBootCompleted(timeoutMillis: Long): Boolean {
+    public fun waitForBootCompleted(timeoutMillis: Long): Boolean {
         return bootCompleted.block(timeoutMillis)
     }
 
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/PortsStateManager.kt b/android/TerminalApp/java/com/android/virtualization/terminal/PortsStateManager.kt
index c5335ac..20bccc2 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/PortsStateManager.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/PortsStateManager.kt
@@ -121,7 +121,6 @@
 
         private var instance: PortsStateManager? = null
 
-        @JvmStatic
         @Synchronized
         fun getInstance(context: Context): PortsStateManager {
             if (instance == null) {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/Runner.kt b/android/TerminalApp/java/com/android/virtualization/terminal/Runner.kt
index 55268f8..6454cbd 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/Runner.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/Runner.kt
@@ -59,7 +59,6 @@
 
     companion object {
         /** Create a virtual machine of the given config, under the given context. */
-        @JvmStatic
         @Throws(VirtualMachineException::class)
         fun create(context: Context, config: VirtualMachineConfig): Runner {
             // context may already be the app context, but calling this again is not harmful.
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
index 966c4a6..8c0368d 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
@@ -313,7 +313,6 @@
             return Intent(context.getApplicationContext(), VmLauncherService::class.java)
         }
 
-        @JvmStatic
         fun run(
             context: Context,
             callback: VmLauncherServiceCallback?,
@@ -345,7 +344,6 @@
             return ResultReceiver.CREATOR.createFromParcel(parcel).also { parcel.recycle() }
         }
 
-        @JvmStatic
         fun stop(context: Context) {
             val i = getMyIntent(context)
             i.setAction(ACTION_STOP_VM_LAUNCHER_SERVICE)
diff --git a/tests/Terminal/Android.bp b/tests/Terminal/Android.bp
index 029fbea..a4e1f6b 100644
--- a/tests/Terminal/Android.bp
+++ b/tests/Terminal/Android.bp
@@ -4,7 +4,7 @@
 
 android_test {
     name: "TerminalAppTests",
-    srcs: ["src/**/*.java"],
+    srcs: ["src/**/*.kt"],
     libs: [
         "android.test.runner.stubs.system",
         "android.test.base.stubs.system",
diff --git a/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java b/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java
deleted file mode 100644
index b0afb54..0000000
--- a/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.virtualization.terminal;
-
-import static org.junit.Assert.assertTrue;
-
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.SystemProperties;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.microdroid.test.common.DeviceProperties;
-import com.android.microdroid.test.common.MetricsProcessor;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-@RunWith(AndroidJUnit4.class)
-public class TerminalAppTest {
-    private Instrumentation mInstr;
-    private Context mTargetContext;
-    private DeviceProperties mProperties;
-    private final MetricsProcessor mMetricsProc = new MetricsProcessor("avf_perf/terminal/");
-
-    @Before
-    public void setup() {
-        mInstr = InstrumentationRegistry.getInstrumentation();
-        mTargetContext = mInstr.getTargetContext();
-        mProperties = DeviceProperties.create(SystemProperties::get);
-        installVmImage();
-    }
-
-    private void installVmImage() {
-        final long INSTALL_TIMEOUT_MILLIS = 300_000; // 5 min
-
-        Intent intent = new Intent(mTargetContext, InstallerActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        if (mInstr.startActivitySync(intent) instanceof InstallerActivity activity) {
-            assertTrue(
-                    "Failed to install VM image",
-                    activity.waitForInstallCompleted(INSTALL_TIMEOUT_MILLIS));
-        }
-    }
-
-    @Test
-    public void boot() throws Exception {
-        final boolean isNestedVirt = mProperties.isCuttlefish() || mProperties.isGoldfish();
-        final long BOOT_TIMEOUT_MILLIS = isNestedVirt ? 180_000 : 30_000; // 30 sec (or 3 min)
-
-        Intent intent = new Intent(mTargetContext, MainActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        long start = System.currentTimeMillis();
-        if (mInstr.startActivitySync(intent) instanceof MainActivity activity) {
-            assertTrue("Failed to boot in 30s", activity.waitForBootCompleted(BOOT_TIMEOUT_MILLIS));
-        }
-        long delay = System.currentTimeMillis() - start;
-
-        // TODO: measure multiple times?
-        List<Long> measurements = new ArrayList<>();
-        measurements.add(delay);
-        Map<String, Double> stats = mMetricsProc.computeStats(measurements, "boot", "ms");
-        Bundle bundle = new Bundle();
-        for (Map.Entry<String, Double> entry : stats.entrySet()) {
-            bundle.putDouble(entry.getKey(), entry.getValue());
-        }
-        mInstr.sendStatus(0, bundle);
-    }
-
-    @After
-    public void tearDown() throws IOException {
-        PortsStateManager.getInstance(mTargetContext).clearEnabledPorts();
-        InstalledImage.getDefault(mTargetContext).uninstallFully();
-    }
-}
diff --git a/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.kt b/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.kt
new file mode 100644
index 0000000..88bdfab
--- /dev/null
+++ b/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 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.
+ */
+package com.android.virtualization.terminal
+
+import android.app.Instrumentation
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.os.SystemProperties
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.microdroid.test.common.DeviceProperties
+import com.android.microdroid.test.common.MetricsProcessor
+import java.io.IOException
+import java.lang.Exception
+import java.util.ArrayList
+import org.junit.After
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class TerminalAppTest {
+    private lateinit var instr: Instrumentation
+    private lateinit var targetContext: Context
+    private lateinit var properties: DeviceProperties
+    private val metricsProc = MetricsProcessor("avf_perf/terminal/")
+
+    @Before
+    fun setup() {
+        instr = InstrumentationRegistry.getInstrumentation()
+        targetContext = instr.targetContext
+        properties =
+            DeviceProperties.create(DeviceProperties.PropertyGetter { SystemProperties.get(it) })
+        installVmImage()
+    }
+
+    private fun installVmImage() {
+        val INSTALL_TIMEOUT_MILLIS: Long = 300000 // 5 min
+
+        val intent = Intent(targetContext, InstallerActivity::class.java)
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+        val activity = instr.startActivitySync(intent)
+        if (activity is InstallerActivity) {
+            Assert.assertTrue(
+                "Failed to install VM image",
+                activity.waitForInstallCompleted(INSTALL_TIMEOUT_MILLIS),
+            )
+        }
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun boot() {
+        val isNestedVirt = properties.isCuttlefish() || properties.isGoldfish()
+        val BOOT_TIMEOUT_MILLIS =
+            (if (isNestedVirt) 180000 else 30000).toLong() // 30 sec (or 3 min)
+
+        val intent = Intent(targetContext, MainActivity::class.java)
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+        val start = System.currentTimeMillis()
+        val activity = instr.startActivitySync(intent)
+        if (activity is MainActivity) {
+            Assert.assertTrue(
+                "Failed to boot in 30s",
+                activity.waitForBootCompleted(BOOT_TIMEOUT_MILLIS),
+            )
+        }
+        val delay = System.currentTimeMillis() - start
+
+        // TODO: measure multiple times?
+        val measurements: MutableList<Long?> = ArrayList<Long?>()
+        measurements.add(delay)
+        val stats = metricsProc.computeStats(measurements, "boot", "ms")
+        val bundle = Bundle()
+        for (entry in stats.entries) {
+            bundle.putDouble(entry.key, entry.value)
+        }
+        instr.sendStatus(0, bundle)
+    }
+
+    @After
+    @Throws(IOException::class)
+    fun tearDown() {
+        PortsStateManager.getInstance(targetContext).clearEnabledPorts()
+        InstalledImage.getDefault(targetContext).uninstallFully()
+    }
+}