Merge "Import translations. DO NOT MERGE ANYWHERE" into main
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 45519d4..d47afc4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,16 +6,19 @@
 jsonlint = true
 google_java_format = true
 pylint3 = true
+ktfmt = true
 rustfmt = true
 xmllint = true
 
 [Tool Paths]
 google-java-format = ${REPO_ROOT}/prebuilts/tools/common/google-java-format/google-java-format
 google-java-format-diff = ${REPO_ROOT}/prebuilts/tools/common/google-java-format/google-java-format-diff.py
+ktfmt = ${REPO_ROOT}/external/ktfmt/ktfmt.sh
 
 [Builtin Hooks Options]
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
 rustfmt = --config-path=rustfmt.toml
+ktfmt = --kotlinlang-style
 
 [Hook Scripts]
 aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/android/TerminalApp/AndroidManifest.xml b/android/TerminalApp/AndroidManifest.xml
index a9d6e9d..495598b 100644
--- a/android/TerminalApp/AndroidManifest.xml
+++ b/android/TerminalApp/AndroidManifest.xml
@@ -32,7 +32,7 @@
     <application
         android:label="@string/app_name"
         android:icon="@mipmap/ic_launcher"
-        android:theme="@style/Theme.Material3.DayNight.NoActionBar"
+        android:theme="@style/VmTerminalAppTheme"
         android:usesCleartextTraffic="true"
         android:supportsRtl="true"
         android:enabled="false">
@@ -47,9 +47,12 @@
             </intent-filter>
         </activity>
         <activity android:name=".SettingsActivity" />
-        <activity android:name=".SettingsDiskResizeActivity" />
-        <activity android:name=".SettingsPortForwardingActivity" />
-        <activity android:name=".SettingsRecoveryActivity" />
+        <activity android:name=".SettingsDiskResizeActivity"
+            android:label="@string/settings_disk_resize_title" />
+        <activity android:name=".SettingsPortForwardingActivity"
+            android:label="@string/settings_port_forwarding_title" />
+        <activity android:name=".SettingsRecoveryActivity"
+            android:label="@string/settings_recovery_title" />
         <activity android:name=".ErrorActivity" />
         <property
             android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/BaseActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/BaseActivity.java
index 66552d5..d6521be 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/BaseActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/BaseActivity.java
@@ -17,7 +17,10 @@
 package com.android.virtualization.terminal;
 
 import android.Manifest;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.content.pm.PackageManager;
+import android.os.Bundle;
 
 import androidx.appcompat.app.AppCompatActivity;
 
@@ -25,6 +28,20 @@
     private static final int POST_NOTIFICATIONS_PERMISSION_REQUEST_CODE = 101;
 
     @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        NotificationManager notificationManager = getSystemService(NotificationManager.class);
+        if (notificationManager.getNotificationChannel(this.getPackageName()) == null) {
+            NotificationChannel channel =
+                    new NotificationChannel(
+                            this.getPackageName(),
+                            getString(R.string.app_name),
+                            NotificationManager.IMPORTANCE_DEFAULT);
+            notificationManager.createNotificationChannel(channel);
+        }
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
 
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.java
index f616b87..f318358 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.java
@@ -113,6 +113,7 @@
                             * 1024;
             return roundUp(minSize);
         } catch (NumberFormatException e) {
+            Log.e(TAG, "Failed to parse min size, p=" + p + ", result=" + result);
             throw new IOException(e);
         }
     }
@@ -168,7 +169,12 @@
         try {
             Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
             process.waitFor();
-            return new String(process.getInputStream().readAllBytes());
+            String result = new String(process.getInputStream().readAllBytes());
+            if (process.exitValue() != 0) {
+                Log.w(TAG, "Process returned with error, command=" + String.join(" ", command)
+                    + ", exitValue=" + process.exitValue() + ", result=" + result);
+            }
+            return result;
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
             throw new IOException("Command interrupted", e);
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index 22f7c4e..0f62984 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -18,8 +18,6 @@
 import static android.webkit.WebSettings.LOAD_NO_CACHE;
 
 import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -106,16 +104,6 @@
 
         mImage = InstalledImage.getDefault(this);
 
-        NotificationManager notificationManager = getSystemService(NotificationManager.class);
-        if (notificationManager.getNotificationChannel(this.getPackageName()) == null) {
-            NotificationChannel channel =
-                    new NotificationChannel(
-                            this.getPackageName(),
-                            getString(R.string.app_name),
-                            NotificationManager.IMPORTANCE_DEFAULT);
-            notificationManager.createNotificationChannel(channel);
-        }
-
         boolean launchInstaller = installIfNecessary();
 
         setContentView(R.layout.activity_headless);
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt
index 6a30971..784e740 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt
@@ -18,12 +18,10 @@
 import android.os.Bundle
 import android.os.Handler
 import android.os.Looper
-import android.view.Window
 import android.view.WindowManager
 import androidx.appcompat.app.AppCompatActivity
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
-
 import com.google.android.material.appbar.MaterialToolbar
 
 class SettingsActivity : AppCompatActivity() {
@@ -40,26 +38,27 @@
 
         val toolbar: MaterialToolbar = findViewById(R.id.settings_toolbar)
         setSupportActionBar(toolbar)
-        val settingsItems = arrayOf(
-            SettingsItem(
-                resources.getString(R.string.settings_disk_resize_title),
-                resources.getString(R.string.settings_disk_resize_sub_title),
-                R.drawable.baseline_storage_24,
-                SettingsItemEnum.DiskResize
-            ),
-            SettingsItem(
-                resources.getString(R.string.settings_port_forwarding_title),
-                resources.getString(R.string.settings_port_forwarding_sub_title),
-                R.drawable.baseline_call_missed_outgoing_24,
-                SettingsItemEnum.PortForwarding
-            ),
-            SettingsItem(
-                resources.getString(R.string.settings_recovery_title),
-                resources.getString(R.string.settings_recovery_sub_title),
-                R.drawable.baseline_settings_backup_restore_24,
-                SettingsItemEnum.Recovery
-            ),
-        )
+        val settingsItems =
+            arrayOf(
+                SettingsItem(
+                    resources.getString(R.string.settings_disk_resize_title),
+                    resources.getString(R.string.settings_disk_resize_sub_title),
+                    R.drawable.baseline_storage_24,
+                    SettingsItemEnum.DiskResize,
+                ),
+                SettingsItem(
+                    resources.getString(R.string.settings_port_forwarding_title),
+                    resources.getString(R.string.settings_port_forwarding_sub_title),
+                    R.drawable.baseline_call_missed_outgoing_24,
+                    SettingsItemEnum.PortForwarding,
+                ),
+                SettingsItem(
+                    resources.getString(R.string.settings_recovery_title),
+                    resources.getString(R.string.settings_recovery_sub_title),
+                    R.drawable.baseline_settings_backup_restore_24,
+                    SettingsItemEnum.Recovery,
+                ),
+            )
         val settingsListItemAdapter = SettingsItemAdapter(settingsItems)
 
         val recyclerView: RecyclerView = findViewById(R.id.settings_list_recycler_view)
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
index d9086a4..30475f5 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
@@ -17,25 +17,22 @@
 
 import android.content.Context
 import android.content.Intent
+import android.content.SharedPreferences
 import android.icu.text.MeasureFormat
 import android.icu.text.NumberFormat
 import android.icu.util.Measure
 import android.icu.util.MeasureUnit
 import android.os.Bundle
-import android.os.FileUtils
-import android.os.Handler
-import android.os.Looper
 import android.text.SpannableString
 import android.text.Spanned
 import android.text.TextUtils
 import android.text.style.RelativeSizeSpan
+import android.view.View
 import android.widget.SeekBar
-import android.view.Window
-import android.view.WindowManager
 import android.widget.TextView
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.view.isVisible
-import com.google.android.material.button.MaterialButton
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import java.util.Locale
 import java.util.regex.Pattern
 
@@ -45,15 +42,19 @@
 
     private var diskSizeStepMb: Long = 0
     private var diskSizeMb: Long = 0
+    private lateinit var sharedPref: SharedPreferences
+    private lateinit var buttons: View
+    private lateinit var cancelButton: View
+    private lateinit var resizeButton: View
     private lateinit var diskSizeText: TextView
     private lateinit var diskSizeSlider: SeekBar
 
     private fun bytesToMb(bytes: Long): Long {
-        return bytes shr 20;
+        return bytes shr 20
     }
 
     private fun mbToBytes(bytes: Long): Long {
-        return bytes shl 20;
+        return bytes shl 20
     }
 
     private fun mbToProgress(bytes: Long): Int {
@@ -68,87 +69,103 @@
         super.onCreate(savedInstanceState)
         setContentView(R.layout.settings_disk_resize)
 
-        Handler(Looper.getMainLooper()).post {
-            val lp: WindowManager.LayoutParams = getWindow().getAttributes()
-            lp.accessibilityTitle = getString(R.string.settings_disk_resize_title)
-            getWindow().setAttributes(lp)
-        }
-
         diskSizeStepMb = 1L shl resources.getInteger(R.integer.disk_size_round_up_step_size_in_mb)
 
-        val sharedPref =
+        sharedPref =
             this.getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE)
-        diskSizeMb = bytesToMb(sharedPref.getLong(
-                    getString(R.string.preference_disk_size_key),
-                    /* defValue= */ 0))
+        diskSizeMb =
+            bytesToMb(
+                sharedPref.getLong(getString(R.string.preference_disk_size_key), /* defValue= */ 0)
+            )
         val image = InstalledImage.getDefault(this)
-        val minDiskSizeMb =
-            bytesToMb(image.getSmallestSizePossible())
-                .coerceAtMost(diskSizeMb)
+        val minDiskSizeMb = bytesToMb(image.getSmallestSizePossible()).coerceAtMost(diskSizeMb)
 
         diskSizeText = findViewById<TextView>(R.id.settings_disk_resize_resize_gb_assigned)!!
         val diskMaxSizeText = findViewById<TextView>(R.id.settings_disk_resize_resize_gb_max)
-        diskMaxSizeText.text = getString(R.string.settings_disk_resize_resize_gb_max_format,
-            localizedFileSize(maxDiskSizeMb, /* isShort= */ true)
-        );
+        diskMaxSizeText.text =
+            getString(
+                R.string.settings_disk_resize_resize_gb_max_format,
+                localizedFileSize(maxDiskSizeMb, /* isShort= */ true),
+            )
 
+        buttons = findViewById<View>(R.id.buttons)
         diskSizeSlider = findViewById<SeekBar>(R.id.settings_disk_resize_disk_size_slider)!!
-        val cancelButton = findViewById<MaterialButton>(R.id.settings_disk_resize_cancel_button)
-        val resizeButton = findViewById<MaterialButton>(R.id.settings_disk_resize_resize_button)
+        cancelButton = findViewById<View>(R.id.settings_disk_resize_cancel_button)
+        resizeButton = findViewById<View>(R.id.settings_disk_resize_resize_button)
         diskSizeSlider.min = mbToProgress(minDiskSizeMb)
         diskSizeSlider.max = mbToProgress(maxDiskSizeMb)
         diskSizeSlider.progress = mbToProgress(diskSizeMb)
         updateSliderText(diskSizeMb)
 
-        diskSizeSlider.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
-            override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
-                updateSliderText(progressToMb(progress))
-                cancelButton.isVisible = true
-                resizeButton.isVisible = true
+        diskSizeSlider.setOnSeekBarChangeListener(
+            object : SeekBar.OnSeekBarChangeListener {
+                override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+                    updateSliderText(progressToMb(progress))
+                    buttons.isVisible = true
+                    cancelButton.isVisible = true
+                    resizeButton.isVisible = true
+                }
+
+                override fun onStartTrackingTouch(seekBar: SeekBar?) {
+                    // no-op
+                }
+
+                override fun onStopTrackingTouch(seekBar: SeekBar?) {
+                    // no-op
+                }
             }
+        )
 
-            override fun onStartTrackingTouch(seekBar: SeekBar?) {
-                // no-op
+        cancelButton.setOnClickListener { cancel() }
+
+        resizeButton.setOnClickListener { showConfirmationDialog() }
+    }
+
+    fun cancel() {
+        diskSizeSlider.progress = mbToProgress(diskSizeMb)
+        buttons.isVisible = false
+    }
+
+    fun showConfirmationDialog() {
+        MaterialAlertDialogBuilder(this)
+            .setTitle(R.string.settings_disk_resize_title)
+            .setMessage(R.string.settings_disk_resize_resize_confirm_dialog_message)
+            .setPositiveButton(R.string.settings_disk_resize_resize_confirm_dialog_confirm) { _, _
+                ->
+                resize()
             }
+            .setNegativeButton(R.string.settings_disk_resize_resize_cancel) { _, _ -> cancel() }
+            .create()
+            .show()
+    }
 
-            override fun onStopTrackingTouch(seekBar: SeekBar?) {
-                // no-op
-            }
-        })
+    fun resize() {
+        diskSizeMb = progressToMb(diskSizeSlider.progress)
+        buttons.isVisible = false
+        val editor = sharedPref.edit()
+        editor.putLong(getString(R.string.preference_disk_size_key), mbToBytes(diskSizeMb))
+        editor.apply()
 
-        cancelButton.setOnClickListener {
-            diskSizeSlider.progress = mbToProgress(diskSizeMb)
-            cancelButton.isVisible = false
-            resizeButton.isVisible = false
-        }
-
-        resizeButton.setOnClickListener {
-            diskSizeMb = progressToMb(diskSizeSlider.progress)
-            cancelButton.isVisible = false
-            resizeButton.isVisible = false
-            val editor = sharedPref.edit()
-            editor.putLong(
-                getString(R.string.preference_disk_size_key),
-                mbToBytes(diskSizeMb)
-            )
-            editor.apply()
-
-            // Restart terminal
-            val intent =
-                baseContext.packageManager.getLaunchIntentForPackage(baseContext.packageName)
-            intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
-            finish()
-            startActivity(intent)
-        }
+        // Restart terminal
+        val intent = baseContext.packageManager.getLaunchIntentForPackage(baseContext.packageName)
+        intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+        finish()
+        startActivity(intent)
     }
 
     fun updateSliderText(sizeMb: Long) {
-        diskSizeText.text = enlargeFontOfNumber(
-            getString(R.string.settings_disk_resize_resize_gb_assigned_format,
-                localizedFileSize(sizeMb, /* isShort= */ true)))
+        diskSizeText.text =
+            enlargeFontOfNumber(
+                getString(
+                    R.string.settings_disk_resize_resize_gb_assigned_format,
+                    localizedFileSize(sizeMb, /* isShort= */ true),
+                )
+            )
         diskSizeSlider.stateDescription =
-            getString(R.string.settings_disk_resize_resize_gb_assigned_format,
-                localizedFileSize(sizeMb, /* isShort= */ false))
+            getString(
+                R.string.settings_disk_resize_resize_gb_assigned_format,
+                localizedFileSize(sizeMb, /* isShort= */ false),
+            )
     }
 
     fun localizedFileSize(sizeMb: Long, isShort: Boolean): String {
@@ -160,7 +177,8 @@
         numberFormatter.minimumFractionDigits = 1
         numberFormatter.maximumFractionDigits = 1
 
-        val formatWidth = if (isShort) MeasureFormat.FormatWidth.SHORT else MeasureFormat.FormatWidth.WIDE
+        val formatWidth =
+            if (isShort) MeasureFormat.FormatWidth.SHORT else MeasureFormat.FormatWidth.WIDE
         val measureFormat: MeasureFormat =
             MeasureFormat.getInstance(localeFromContext, formatWidth, numberFormatter)
         return measureFormat.format(measure)
@@ -171,14 +189,15 @@
             return ""
         }
 
-        val matcher = numberPattern.matcher(summary);
+        val matcher = numberPattern.matcher(summary)
         if (matcher.find()) {
             val spannableSummary = SpannableString(summary)
             spannableSummary.setSpan(
-                    RelativeSizeSpan(2f),
-                    matcher.start(),
-                    matcher.end(),
-                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
+                RelativeSizeSpan(2f),
+                matcher.start(),
+                matcher.end(),
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
+            )
             return spannableSummary
         }
         return summary
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItem.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItem.kt
index e1723a7..5098ecd 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItem.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItem.kt
@@ -16,13 +16,14 @@
 package com.android.virtualization.terminal
 
 enum class SettingsItemEnum {
-    DiskResize, PortForwarding, Recovery
+    DiskResize,
+    PortForwarding,
+    Recovery,
 }
 
 class SettingsItem(
     val title: String,
     val subTitle: String,
     val icon: Int,
-    val settingsItemEnum: SettingsItemEnum
-) {
-}
\ No newline at end of file
+    val settingsItemEnum: SettingsItemEnum,
+) {}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt
index 86f5c92..132d749 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt
@@ -35,8 +35,9 @@
     }
 
     override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
-        val view = LayoutInflater.from(viewGroup.context)
-            .inflate(R.layout.settings_list_item, viewGroup, false)
+        val view =
+            LayoutInflater.from(viewGroup.context)
+                .inflate(R.layout.settings_list_item, viewGroup, false)
         return ViewHolder(view)
     }
 
@@ -46,17 +47,19 @@
         viewHolder.subTitle.text = dataSet[position].subTitle
 
         viewHolder.card.setOnClickListener { view ->
-            val intent = Intent(
-                viewHolder.itemView.context,
-                when (dataSet[position].settingsItemEnum) {
-                    SettingsItemEnum.DiskResize -> SettingsDiskResizeActivity::class.java
-                    SettingsItemEnum.PortForwarding -> SettingsPortForwardingActivity::class.java
-                    SettingsItemEnum.Recovery -> SettingsRecoveryActivity::class.java
-                }
-            )
+            val intent =
+                Intent(
+                    viewHolder.itemView.context,
+                    when (dataSet[position].settingsItemEnum) {
+                        SettingsItemEnum.DiskResize -> SettingsDiskResizeActivity::class.java
+                        SettingsItemEnum.PortForwarding ->
+                            SettingsPortForwardingActivity::class.java
+                        SettingsItemEnum.Recovery -> SettingsRecoveryActivity::class.java
+                    },
+                )
             view.context.startActivity(intent)
         }
     }
 
     override fun getItemCount() = dataSet.size
-}
\ No newline at end of file
+}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt
index 0377de4..fe693c4 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt
@@ -18,10 +18,6 @@
 import android.content.Context
 import android.content.SharedPreferences
 import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
-import android.view.Window
-import android.view.WindowManager
 import androidx.appcompat.app.AppCompatActivity
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
@@ -34,12 +30,6 @@
         super.onCreate(savedInstanceState)
         setContentView(R.layout.settings_port_forwarding)
 
-        Handler(Looper.getMainLooper()).post {
-            val lp: WindowManager.LayoutParams = getWindow().getAttributes()
-            lp.accessibilityTitle = getString(R.string.settings_port_forwarding_title)
-            getWindow().setAttributes(lp)
-        }
-
         mSharedPref =
             this.getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE)
 
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingAdapter.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingAdapter.kt
index c3501d4..afe985a 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingAdapter.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingAdapter.kt
@@ -90,8 +90,9 @@
     }
 
     override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
-        val view = LayoutInflater.from(viewGroup.context)
-            .inflate(R.layout.settings_port_forwarding_item, viewGroup, false)
+        val view =
+            LayoutInflater.from(viewGroup.context)
+                .inflate(R.layout.settings_port_forwarding_item, viewGroup, false)
         return ViewHolder(view)
     }
 
@@ -100,13 +101,16 @@
         viewHolder.enabledSwitch.contentDescription = viewHolder.port.text
         viewHolder.enabledSwitch.isChecked = mItems[position].enabled
         viewHolder.enabledSwitch.setOnCheckedChangeListener { _, isChecked ->
-            val sharedPref: SharedPreferences = context.getSharedPreferences(
-                context.getString(R.string.preference_file_key), Context.MODE_PRIVATE
-            )
+            val sharedPref: SharedPreferences =
+                context.getSharedPreferences(
+                    context.getString(R.string.preference_file_key),
+                    Context.MODE_PRIVATE,
+                )
             val editor = sharedPref.edit()
             editor.putBoolean(
-                context.getString(R.string.preference_forwarding_port_is_enabled) + viewHolder.port.text,
-                isChecked
+                context.getString(R.string.preference_forwarding_port_is_enabled) +
+                    viewHolder.port.text,
+                isChecked,
             )
             editor.apply()
         }
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt
index 3adeece..0d74eb0 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt
@@ -17,12 +17,8 @@
 
 import android.content.Intent
 import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
 import android.util.Log
 import android.view.View
-import android.view.Window
-import android.view.WindowManager
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.view.isVisible
 import androidx.lifecycle.lifecycleScope
@@ -31,7 +27,6 @@
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.snackbar.Snackbar
 import java.io.IOException
-import java.nio.file.Files
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -41,29 +36,28 @@
         super.onCreate(savedInstanceState)
         setContentView(R.layout.settings_recovery)
 
-        Handler(Looper.getMainLooper()).post {
-            val lp: WindowManager.LayoutParams = getWindow().getAttributes()
-            lp.accessibilityTitle = getString(R.string.settings_recovery_title)
-            getWindow().setAttributes(lp)
-        }
-
         val resetCard = findViewById<MaterialCardView>(R.id.settings_recovery_reset_card)
         resetCard.setOnClickListener {
             var backupRootfs = false
-            val dialog = MaterialAlertDialogBuilder(this)
-                .setTitle(R.string.settings_recovery_reset_dialog_title)
-                .setMultiChoiceItems(arrayOf(getString(R.string.settings_recovery_reset_dialog_backup_option)), booleanArrayOf(backupRootfs)) {_, _, checked ->
-                    backupRootfs = checked
-                }
-                .setPositiveButton(R.string.settings_recovery_reset_dialog_confirm) { _, _ ->
-                    // This coroutine will be killed when the activity is killed. The behavior is both acceptable
-                    // either removing is done or not
-                    runInBackgroundAndRestartApp {
-                        uninstall(backupRootfs)
+            val dialog =
+                MaterialAlertDialogBuilder(this)
+                    .setTitle(R.string.settings_recovery_reset_dialog_title)
+                    .setMultiChoiceItems(
+                        arrayOf(getString(R.string.settings_recovery_reset_dialog_backup_option)),
+                        booleanArrayOf(backupRootfs),
+                    ) { _, _, checked ->
+                        backupRootfs = checked
                     }
-                }
-                .setNegativeButton(R.string.settings_recovery_reset_dialog_cancel) { dialog, _ -> dialog.dismiss() }
-                .create()
+                    .setPositiveButton(R.string.settings_recovery_reset_dialog_confirm) { _, _ ->
+                        // This coroutine will be killed when the activity is killed. Either
+                        // finishing removing or not is acceptable behavior.
+                        runInBackgroundAndRestartApp { uninstall(backupRootfs) }
+                    }
+                    .setNegativeButton(R.string.settings_recovery_reset_dialog_cancel) { dialog, _
+                        ->
+                        dialog.dismiss()
+                    }
+                    .create()
             dialog.show()
         }
         val resetBackupCard = findViewById<View>(R.id.settings_recovery_reset_backup_card)
@@ -71,16 +65,18 @@
         resetBackupCard.isVisible = InstalledImage.getDefault(this).hasBackup()
 
         resetBackupCard.setOnClickListener {
-            val dialog = MaterialAlertDialogBuilder(this)
-                .setTitle(R.string.settings_recovery_remove_backup_title)
-                .setMessage(R.string.settings_recovery_remove_backup_sub_title)
-                .setPositiveButton(R.string.settings_recovery_reset_dialog_confirm) { _, _ ->
-                    runInBackgroundAndRestartApp {
-                        removeBackup()
+            val dialog =
+                MaterialAlertDialogBuilder(this)
+                    .setTitle(R.string.settings_recovery_remove_backup_title)
+                    .setMessage(R.string.settings_recovery_remove_backup_sub_title)
+                    .setPositiveButton(R.string.settings_recovery_reset_dialog_confirm) { _, _ ->
+                        runInBackgroundAndRestartApp { removeBackup() }
                     }
-                }
-                .setNegativeButton(R.string.settings_recovery_reset_dialog_cancel) { dialog, _ -> dialog.dismiss() }
-                .create()
+                    .setNegativeButton(R.string.settings_recovery_reset_dialog_cancel) { dialog, _
+                        ->
+                        dialog.dismiss()
+                    }
+                    .create()
             dialog.show()
         }
     }
@@ -90,10 +86,11 @@
             InstalledImage.getDefault(this).deleteBackup()
         } catch (e: IOException) {
             Snackbar.make(
-                findViewById(android.R.id.content),
-                R.string.settings_recovery_error_during_removing_backup,
-                Snackbar.LENGTH_SHORT
-            ).show();
+                    findViewById(android.R.id.content),
+                    R.string.settings_recovery_error_during_removing_backup,
+                    Snackbar.LENGTH_SHORT,
+                )
+                .show()
             Log.e(TAG, "cannot remove backup")
         }
     }
@@ -109,34 +106,36 @@
                 image.uninstallFully()
             }
         } catch (e: IOException) {
-            val errorMsgId = if (backupRootfs && !backupDone) R.string.settings_recovery_error_due_to_backup
-                    else R.string.settings_recovery_error;
-            Snackbar.make(
-                findViewById(android.R.id.content),
-                errorMsgId,
-                Snackbar.LENGTH_SHORT
-            ).show();
+            val errorMsgId =
+                if (backupRootfs && !backupDone) R.string.settings_recovery_error_due_to_backup
+                else R.string.settings_recovery_error
+            Snackbar.make(findViewById(android.R.id.content), errorMsgId, Snackbar.LENGTH_SHORT)
+                .show()
             Log.e(TAG, "cannot recovery ", e)
         }
     }
 
-    private fun runInBackgroundAndRestartApp(backgroundWork: suspend CoroutineScope.() -> Unit): Unit {
+    private fun runInBackgroundAndRestartApp(
+        backgroundWork: suspend CoroutineScope.() -> Unit
+    ): Unit {
         findViewById<View>(R.id.setting_recovery_card_container).visibility = View.INVISIBLE
         findViewById<View>(R.id.recovery_boot_progress).visibility = View.VISIBLE
-        lifecycleScope.launch(Dispatchers.IO) {
-            backgroundWork()
-        }.invokeOnCompletion {
-            runOnUiThread {
-                findViewById<View>(R.id.setting_recovery_card_container).visibility =
-                    View.VISIBLE
-                findViewById<View>(R.id.recovery_boot_progress).visibility = View.INVISIBLE
-                // Restart terminal
-                val intent =
-                    baseContext.packageManager.getLaunchIntentForPackage(baseContext.packageName)
-                intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
-                finish()
-                startActivity(intent)
+        lifecycleScope
+            .launch(Dispatchers.IO) { backgroundWork() }
+            .invokeOnCompletion {
+                runOnUiThread {
+                    findViewById<View>(R.id.setting_recovery_card_container).visibility =
+                        View.VISIBLE
+                    findViewById<View>(R.id.recovery_boot_progress).visibility = View.INVISIBLE
+                    // Restart terminal
+                    val intent =
+                        baseContext.packageManager.getLaunchIntentForPackage(
+                            baseContext.packageName
+                        )
+                    intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                    finish()
+                    startActivity(intent)
+                }
             }
-        }
     }
 }
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt
index cb917bd..7562779 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt
@@ -30,4 +30,4 @@
     override fun dependencies(): List<Class<out Initializer<*>>> {
         return emptyList()
     }
-}
\ No newline at end of file
+}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/TerminalView.java b/android/TerminalApp/java/com/android/virtualization/terminal/TerminalView.java
index efee62f..efa3dcc 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/TerminalView.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/TerminalView.java
@@ -216,6 +216,8 @@
                             if (id != View.NO_ID) {
                                 info.setText(null);
                                 info.setContentDescription(getString(R.string.terminal_display));
+                                // b/376827536
+                                info.setHintText(getString(R.string.double_tap_to_edit_text));
                             }
 
                             // These two lines below are to prevent this WebView element from being
@@ -230,6 +232,8 @@
                             // Localize the spoken text.
                             if (isEmptyLine(info)) {
                                 info.setContentDescription(getString(R.string.empty_line));
+                                // b/376827536
+                                info.setHintText(getString(R.string.double_tap_to_edit_text));
                             }
                             break;
                         case "android.widget.TextView":
diff --git a/android/TerminalApp/res/layout/settings_disk_resize.xml b/android/TerminalApp/res/layout/settings_disk_resize.xml
index fb7f85b..55fb7af 100644
--- a/android/TerminalApp/res/layout/settings_disk_resize.xml
+++ b/android/TerminalApp/res/layout/settings_disk_resize.xml
@@ -65,14 +65,18 @@
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintBottom_toBottomOf="parent" />
 
+        <androidx.constraintlayout.widget.Group
+            android:id="@+id/buttons"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="invisible"
+            app:constraint_referenced_ids="settings_disk_resize_cancel_button,settings_disk_resize_resize_button" />
+
         <com.google.android.material.button.MaterialButton
             android:id="@+id/settings_disk_resize_cancel_button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:maxWidth="150sp"
-            android:hyphenationFrequency="full"
             android:text="@string/settings_disk_resize_resize_cancel"
-            android:visibility="invisible"
             android:layout_marginTop="48dp"
             android:layout_marginHorizontal="8dp"
             app:layout_constraintTop_toTopOf="@+id/settings_disk_resize_disk_size_slider"
@@ -83,10 +87,7 @@
             android:id="@+id/settings_disk_resize_resize_button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:maxWidth="150sp"
-            android:hyphenationFrequency="full"
             android:text="@string/settings_disk_resize_resize_restart_vm_to_apply"
-            android:visibility="invisible"
             android:layout_marginTop="48dp"
             app:layout_constraintTop_toTopOf="@+id/settings_disk_resize_disk_size_slider"
             app:layout_constraintBottom_toBottomOf="parent"
diff --git a/android/TerminalApp/res/values/strings.xml b/android/TerminalApp/res/values/strings.xml
index da8ca84..44d88a2 100644
--- a/android/TerminalApp/res/values/strings.xml
+++ b/android/TerminalApp/res/values/strings.xml
@@ -26,29 +26,31 @@
     <string name="terminal_input">Cursor</string>
     <!-- Description of an empty line in the terminal. This is read by talkback. [CHAR LIMIT=none] -->
     <string name="empty_line">Empty line</string>
+    <!-- Description of the hint supported by the terminal UI elements. This is read by talkback. [CHAR LIMIT=none] -->
+    <string name="double_tap_to_edit_text">Double-tap to go to cursor</string>
 
     <!-- Installer activity title [CHAR LIMIT=none] -->
     <string name="installer_title_text">Install Linux terminal</string>
     <!-- Installer activity description format [CHAR LIMIT=none] -->
-    <string name="installer_desc_text_format">To launch Linux terminal, you need to download roughly <xliff:g id="expected_size" example="350GB">%1$s</xliff:g> of data over network.\nWould you proceed?</string>
+    <string name="installer_desc_text_format">To launch Linux terminal, you need to download roughly <xliff:g id="expected_size" example="350GB">%1$s</xliff:g> of data over the network.\nWould you like to proceed?</string>
     <!-- Checkbox at the installer activity to download when Wi-Fi is available to prevent from paying network traffic [CHAR LIMIT=none] -->
-    <string name="installer_wait_for_wifi_checkbox_text">Download when Wi-Fi is available</string>
+    <string name="installer_wait_for_wifi_checkbox_text">Download using Wi-Fi only</string>
     <!-- Button at the installer activity to confirm installation [CHAR LIMIT=16] -->
     <string name="installer_install_button_enabled_text">Install</string>
     <!-- Button at the installer activity to when installation is already in progress [CHAR LIMIT=16] -->
     <string name="installer_install_button_disabled_text">Installing</string>
     <!-- Toast message at installer activity when network doesn't meet[CHAR LIMIT=none] -->
-    <string name="installer_install_network_error_message">Network error. Check connection and retry.</string>
+    <string name="installer_install_network_error_message">Failed to install due to a network error. Check your connection and try again.</string>
     <!-- Notification title for installer [CHAR LIMIT=64] -->
     <string name="installer_notif_title_text">Installing Linux terminal</string>
     <!-- Notification description for installer [CHAR LIMIT=none] -->
-    <string name="installer_notif_desc_text">Linux terminal will be started after finish</string>
+    <string name="installer_notif_desc_text">Linux terminal will start after the installation is finished</string>
     <!-- Toast error message for install failure due to the network issue [CHAR LIMIT=none] -->
-    <string name="installer_error_network">Failed to install due to the network issue</string>
+    <string name="installer_error_network">Failed to install due to a network issue</string>
     <!-- Toast error message for install failure because Wi-Fi isn't available although required [CHAR LIMIT=none] -->
-    <string name="installer_error_no_wifi">Failed to install because Wi-Fi isn\'t available</string>
+    <string name="installer_error_no_wifi">Failed to install because Wi-Fi is not available</string>
     <!-- Toast error message for install failure due to the unidentified issue [CHAR LIMIT=none] -->
-    <string name="installer_error_unknown">Failed to install. Try again.</string>
+    <string name="installer_error_unknown">Failed to install. Please try again</string>
 
     <!-- Action bar icon name for the settings view CHAR LIMIT=none] -->
     <string name="action_settings">Settings</string>
@@ -61,9 +63,9 @@
     <string name="vm_error_message">Terminal crashed</string>
 
     <!-- Settings memu title for resizing disk of the virtual machine. [CHAR LIMIT=none] -->
-    <string name="settings_disk_resize_title">Disk Resize</string>
+    <string name="settings_disk_resize_title">Disk resize</string>
     <!-- Settings memu subtitle for resizing disk of the virtual machine. [CHAR LIMIT=none] -->
-    <string name="settings_disk_resize_sub_title">Resize / Rootfs</string>
+    <string name="settings_disk_resize_sub_title">Resize the root partition size</string>
     <!-- Toast message after new disk size is set. [CHAR LIMIT=none] -->
     <string name="settings_disk_resize_resize_message">Disk size set</string>
     <!-- Settings menu option description format of the current disk size. [CHAR LIMIT=none] -->
@@ -72,17 +74,21 @@
     <string name="settings_disk_resize_resize_gb_max_format"><xliff:g id="max_size" example="256GB">%1$s</xliff:g> max</string>
     <!-- Settings menu button to cancel disk resize. [CHAR LIMIT=16] -->
     <string name="settings_disk_resize_resize_cancel">Cancel</string>
-    <!-- Settings menu button to apply change that requires to restart Terminal app. [CHAR LIMIT=20] -->
-    <string name="settings_disk_resize_resize_restart_vm_to_apply">Restart to apply</string>
+    <!-- Settings menu button to apply change Terminal app. This will launch a confirmation dialog [CHAR LIMIT=16] -->
+    <string name="settings_disk_resize_resize_restart_vm_to_apply">Apply</string>
+    <!-- Dialog description for applying disk resize Terminal app, which requires to restart the terminal [CHAR LIMIT=none] -->
+    <string name="settings_disk_resize_resize_confirm_dialog_message">Terminal will be restarted to resize disk</string>
+    <!-- Dialog confirmation button for restarting the terminal [CHAR LIMIT=16] -->
+    <string name="settings_disk_resize_resize_confirm_dialog_confirm">Confirm</string>
 
     <!-- Settings menu title for 'port forwarding' [CHAR LIMIT=none] -->
-    <string name="settings_port_forwarding_title">Port Forwarding</string>
+    <string name="settings_port_forwarding_title">Port forwarding</string>
     <!-- Settings menu subtitle for 'port forwarding' [CHAR LIMIT=none] -->
     <string name="settings_port_forwarding_sub_title">Configure port forwarding</string>
     <!-- Notification title for new port forwarding [CHAR LIMIT=none] -->
-    <string name="settings_port_forwarding_notification_title">Terminal is trying to open a new port</string>
+    <string name="settings_port_forwarding_notification_title">Terminal is requesting to open a new port</string>
     <!-- Notification content for new port forwarding [CHAR LIMIT=none] -->
-    <string name="settings_port_forwarding_notification_content">Port requested to be open: <xliff:g id="port_number" example="8080">%d</xliff:g></string>
+    <string name="settings_port_forwarding_notification_content">Port requested: <xliff:g id="port_number" example="8080">%d</xliff:g></string>
     <!-- Notification action accept [CHAR LIMIT=none] -->
     <string name="settings_port_forwarding_notification_accept">Accept</string>
     <!-- Notification action deny [CHAR LIMIT=none] -->
@@ -91,36 +97,36 @@
     <!-- Settings menu title for recoverying image [CHAR LIMIT=none] -->
     <string name="settings_recovery_title">Recovery</string>
     <!-- Settings menu subtitle for recoverying image [CHAR LIMIT=none] -->
-    <string name="settings_recovery_sub_title">Partition Recovery options</string>
+    <string name="settings_recovery_sub_title">Partition recovery options</string>
     <!-- Settings menu title for resetting the terminal [CHAR LIMIT=none] -->
-    <string name="settings_recovery_reset_title">Change to Initial version</string>
+    <string name="settings_recovery_reset_title">Reset to initial version</string>
     <!-- Settings menu subtitle for resetting the terminal [CHAR LIMIT=none] -->
-    <string name="settings_recovery_reset_sub_title">Remove all</string>
+    <string name="settings_recovery_reset_sub_title">Remove all data</string>
     <!-- Dialog title for resetting the terminal [CHAR LIMIT=none] -->
     <string name="settings_recovery_reset_dialog_title">Reset terminal</string>
     <!-- Dialog message for resetting the terminal [CHAR LIMIT=none] -->
-    <string name="settings_recovery_reset_dialog_message">Data will be deleted</string>
+    <string name="settings_recovery_reset_dialog_message">Data will be removed</string>
     <!-- Dialog button confirm for resetting the terminal [CHAR LIMIT=16] -->
-    <string name="settings_recovery_reset_dialog_confirm">Confirm</string>
+    <string name="settings_recovery_reset_dialog_confirm">Reset</string>
     <!-- Dialog button cancel for resetting the terminal [CHAR LIMIT=16] -->
     <string name="settings_recovery_reset_dialog_cancel">Cancel</string>
     <!-- Dialog option to back up previous image(/mnt/backup is the path which is supposed not to be translated) [CHAR LIMIT=none] -->
     <string name="settings_recovery_reset_dialog_backup_option">Back up data to <xliff:g id="path" example="/mnt/backup">/mnt/backup</xliff:g></string>
     <!-- Snankbar to indicate recovery error due to backup [CHAR LIMIT=none] -->
-    <string name="settings_recovery_error_due_to_backup">Recovery failed because backup failed</string>
+    <string name="settings_recovery_error_due_to_backup">Failed to recover due to a backup error</string>
     <!-- Snankbar to indicate recovery error [CHAR LIMIT=none] -->
     <string name="settings_recovery_error">Recovery failed</string>
     <!-- Snankbar to indicate recovery error during removing backup [CHAR LIMIT=none] -->
-    <string name="settings_recovery_error_during_removing_backup">Cannot remove backup file</string>
+    <string name="settings_recovery_error_during_removing_backup">Failed to remove backup data</string>
     <!-- Settings menu title for removing backup data [CHAR LIMIT=none] -->
     <string name="settings_recovery_remove_backup_title">Remove backup data</string>
     <!-- Settings menu sub title for removing backup data(/mnt/backup is the path which is supposed not to be translated) [CHAR LIMIT=none] -->
-    <string name="settings_recovery_remove_backup_sub_title">Clean up <xliff:g id="path" example="/mnt/backup">/mnt/backup</xliff:g></string>
+    <string name="settings_recovery_remove_backup_sub_title">Remove <xliff:g id="path" example="/mnt/backup">/mnt/backup</xliff:g></string>
 
     <!-- Error page that shows error page [CHAR LIMIT=none] -->
-    <string name="error_title">Unrecoverable Error</string>
+    <string name="error_title">Unrecoverable error</string>
     <!-- Error page that shows error page [CHAR LIMIT=none] -->
-    <string name="error_desc">Failed to recover from an error.\nYou can try restart the app, or try one of recovery option.</string>
+    <string name="error_desc">Failed to recover from an error.\nYou can try restarting terminal or try one of the recovery options.</string>
     <!-- Error page that shows detailed error code (error reason) for bugreport. [CHAR LIMIT=none] -->
     <string name="error_code">Error code: <xliff:g id="error_code" example="ACCESS_DENIED">%s</xliff:g></string>
 
@@ -129,10 +135,10 @@
     <!-- Notification title for foreground service notification [CHAR LIMIT=none] -->
     <string name="service_notification_title">Terminal is running</string>
     <!-- Notification content for foreground service notification [CHAR LIMIT=none] -->
-    <string name="service_notification_content">Click to open the terminal</string>
+    <string name="service_notification_content">Click to open terminal</string>
     <!-- Notification action button for closing the virtual machine [CHAR LIMIT=20] -->
     <string name="service_notification_quit_action">Close</string>
 
-    <!-- VirGL is the name of hardware acceleration for VM, the name is supposed not to be translated. [CHAR LIMIT=20] -->
-    <string name="virgl_enabled">VirGL is enabled</string>
+    <!-- This string is for toast message to notify that VirGL is enabled. [CHAR LIMIT=40] -->
+    <string name="virgl_enabled"><xliff:g>VirGL</xliff:g> is enabled</string>
 </resources>
diff --git a/android/TerminalApp/res/values/styles.xml b/android/TerminalApp/res/values/styles.xml
index ee80862..3fb8e7d 100644
--- a/android/TerminalApp/res/values/styles.xml
+++ b/android/TerminalApp/res/values/styles.xml
@@ -14,7 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<resources>
+<resources xmlns:tools="http://schemas.android.com/tools">
     <style name="ModifierKeyStyle" parent="@style/Widget.Material3.Button.TextButton">
         <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
@@ -24,4 +24,7 @@
         <item name="android:paddingHorizontal">0dp</item>
         <item name="android:hapticFeedbackEnabled">true</item>
     </style>
-</resources>
\ No newline at end of file
+    <style name="VmTerminalAppTheme" parent="@style/Theme.Material3.DayNight.NoActionBar">
+        <item name="android:windowLightStatusBar" tools:targetApi="m">?android:attr/isLightTheme</item>
+    </style>
+</resources>
diff --git a/android/TerminalApp/res/xml/main_split_config.xml b/android/TerminalApp/res/xml/main_split_config.xml
index c2da907..437e75a 100644
--- a/android/TerminalApp/res/xml/main_split_config.xml
+++ b/android/TerminalApp/res/xml/main_split_config.xml
@@ -45,7 +45,7 @@
         window:splitLayoutDirection="locale"
         window:splitMaxAspectRatioInPortrait="alwaysAllow"
         window:splitMinWidthDp="@integer/split_min_width"
-        window:splitRatio="@dimen/activity_split_ratio">
+        window:splitRatio="@dimen/activity_split_ratio"
         window:stickyPlaceholder="false">
         <ActivityFilter
             window:activityName="com.android.virtualization.terminal.SettingsActivity"/>
diff --git a/android/compos_verify/verify.rs b/android/compos_verify/verify.rs
index b94ebbc..a3f18d5 100644
--- a/android/compos_verify/verify.rs
+++ b/android/compos_verify/verify.rs
@@ -124,7 +124,6 @@
         &idsig_manifest_ext_apk,
         &VmParameters {
             name: String::from("ComposVerify"),
-            os: String::from("microdroid"),
             cpu_topology: VmCpuTopology::OneCpu, // This VM runs very little work at boot
             debug_mode: args.debug,
             ..Default::default()
diff --git a/android/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl b/android/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
index 3748899..dde75e1 100644
--- a/android/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
+++ b/android/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
@@ -48,6 +48,5 @@
      * callback, unless the returned ICompilationTask is cancelled. The caller should maintain
      * a reference to the ICompilationTask until compilation completes or is cancelled.
      */
-    ICompilationTask startTestCompile(
-            ApexSource apexSource, ICompilationTaskCallback callback, String os);
+    ICompilationTask startTestCompile(ApexSource apexSource, ICompilationTaskCallback callback);
 }
diff --git a/android/composd/src/instance_manager.rs b/android/composd/src/instance_manager.rs
index cb1f7e4..9e94035 100644
--- a/android/composd/src/instance_manager.rs
+++ b/android/composd/src/instance_manager.rs
@@ -46,12 +46,11 @@
         self.start_instance(CURRENT_INSTANCE_DIR, vm_parameters)
     }
 
-    pub fn start_test_instance(&self, prefer_staged: bool, os: &str) -> Result<CompOsInstance> {
+    pub fn start_test_instance(&self, prefer_staged: bool) -> Result<CompOsInstance> {
         let mut vm_parameters = new_vm_parameters()?;
         vm_parameters.name = String::from("ComposdTest");
         vm_parameters.debug_mode = true;
         vm_parameters.prefer_staged = prefer_staged;
-        vm_parameters.os = os.to_owned();
         self.start_instance(TEST_INSTANCE_DIR, vm_parameters)
     }
 
diff --git a/android/composd/src/service.rs b/android/composd/src/service.rs
index 3cc40af..49cfd3a 100644
--- a/android/composd/src/service.rs
+++ b/android/composd/src/service.rs
@@ -60,7 +60,6 @@
         &self,
         apex_source: ApexSource,
         callback: &Strong<dyn ICompilationTaskCallback>,
-        os: &str,
     ) -> binder::Result<Strong<dyn ICompilationTask>> {
         check_permissions()?;
         let prefer_staged = match apex_source {
@@ -68,7 +67,7 @@
             ApexSource::PreferStaged => true,
             _ => unreachable!("Invalid ApexSource {:?}", apex_source),
         };
-        to_binder_result(self.do_start_test_compile(prefer_staged, callback, os))
+        to_binder_result(self.do_start_test_compile(prefer_staged, callback))
     }
 }
 
@@ -94,12 +93,9 @@
         &self,
         prefer_staged: bool,
         callback: &Strong<dyn ICompilationTaskCallback>,
-        os: &str,
     ) -> Result<Strong<dyn ICompilationTask>> {
-        let comp_os = self
-            .instance_manager
-            .start_test_instance(prefer_staged, os)
-            .context("Starting CompOS")?;
+        let comp_os =
+            self.instance_manager.start_test_instance(prefer_staged).context("Starting CompOS")?;
 
         let target_dir_name = TEST_ARTIFACTS_SUBDIR.to_owned();
         let task = OdrefreshTask::start(
diff --git a/android/composd_cmd/composd_cmd.rs b/android/composd_cmd/composd_cmd.rs
index 6281bd0..6d096a1 100644
--- a/android/composd_cmd/composd_cmd.rs
+++ b/android/composd_cmd/composd_cmd.rs
@@ -46,10 +46,6 @@
         /// If any APEX is staged, prefer the staged version.
         #[clap(long)]
         prefer_staged: bool,
-
-        /// OS for the VM.
-        #[clap(long, default_value = "microdroid")]
-        os: String,
     },
 }
 
@@ -60,7 +56,7 @@
 
     match action {
         Actions::StagedApexCompile {} => run_staged_apex_compile()?,
-        Actions::TestCompile { prefer_staged, os } => run_test_compile(prefer_staged, &os)?,
+        Actions::TestCompile { prefer_staged } => run_test_compile(prefer_staged)?,
     }
 
     println!("All Ok!");
@@ -120,9 +116,9 @@
     run_async_compilation(|service, callback| service.startStagedApexCompile(callback))
 }
 
-fn run_test_compile(prefer_staged: bool, os: &str) -> Result<()> {
+fn run_test_compile(prefer_staged: bool) -> Result<()> {
     let apex_source = if prefer_staged { ApexSource::PreferStaged } else { ApexSource::NoStaged };
-    run_async_compilation(|service, callback| service.startTestCompile(apex_source, callback, os))
+    run_async_compilation(|service, callback| service.startTestCompile(apex_source, callback))
 }
 
 fn run_async_compilation<F>(start_compile_fn: F) -> Result<()>
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 9a733b6..55de0af 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -445,20 +445,25 @@
         let context = EarlyVmContext::new(cid, temp_dir.clone())
             .context(format!("Can't create early vm contexts for {cid}"))
             .or_service_specific_exception(-1)?;
-        let service = VirtualMachineService::new_binder(self.state.clone(), cid).as_binder();
 
-        // Start VM service listening for connections from the new CID on port=CID.
-        let port = cid;
-        let (vm_server, _) = RpcServer::new_vsock(service, cid, port)
-            .context(format!("Could not start RpcServer on port {port}"))
-            .or_service_specific_exception(-1)?;
-        vm_server.start();
-        Ok((VmContext::new(Strong::new(Box::new(context)), vm_server), cid, temp_dir))
+        if requires_vm_service(config) {
+            // Start VM service listening for connections from the new CID on port=CID.
+            let service = VirtualMachineService::new_binder(self.state.clone(), cid).as_binder();
+            let port = cid;
+            let (vm_server, _) = RpcServer::new_vsock(service, cid, port)
+                .context(format!("Could not start RpcServer on port {port}"))
+                .or_service_specific_exception(-1)?;
+            vm_server.start();
+            Ok((VmContext::new(Strong::new(Box::new(context)), Some(vm_server)), cid, temp_dir))
+        } else {
+            Ok((VmContext::new(Strong::new(Box::new(context)), None), cid, temp_dir))
+        }
     }
 
     fn create_vm_context(
         &self,
         requester_debug_pid: pid_t,
+        config: &VirtualMachineConfig,
     ) -> binder::Result<(VmContext, Cid, PathBuf)> {
         const NUM_ATTEMPTS: usize = 5;
 
@@ -466,6 +471,12 @@
             let vm_context = GLOBAL_SERVICE.allocateGlobalVmContext(requester_debug_pid)?;
             let cid = vm_context.getCid()? as Cid;
             let temp_dir: PathBuf = vm_context.getTemporaryDirectory()?.into();
+
+            // We don't need to start the VM service for custom VMs.
+            if !requires_vm_service(config) {
+                return Ok((VmContext::new(vm_context, None), cid, temp_dir));
+            }
+
             let service = VirtualMachineService::new_binder(self.state.clone(), cid).as_binder();
 
             // Start VM service listening for connections from the new CID on port=CID.
@@ -473,7 +484,7 @@
             match RpcServer::new_vsock(service, cid, port) {
                 Ok((vm_server, _)) => {
                     vm_server.start();
-                    return Ok((VmContext::new(vm_context, vm_server), cid, temp_dir));
+                    return Ok((VmContext::new(vm_context, Some(vm_server)), cid, temp_dir));
                 }
                 Err(err) => {
                     warn!("Could not start RpcServer on port {}: {}", port, err);
@@ -509,7 +520,7 @@
         let (vm_context, cid, temporary_directory) = if cfg!(early) {
             self.create_early_vm_context(config)?
         } else {
-            self.create_vm_context(requester_debug_pid)?
+            self.create_vm_context(requester_debug_pid, config)?
         };
 
         if is_custom_config(config) {
@@ -820,6 +831,17 @@
     }
 }
 
+/// Returns whether a VM config requires VirtualMachineService running on the host. Only Microdroid
+/// VM (i.e. AppConfig) requires it. However, a few Microdroid tests use RawConfig for Microdroid
+/// VM. To handle the exceptional case, we use name as a second criteria; if the name is
+/// "microdroid" we run VirtualMachineService
+fn requires_vm_service(config: &VirtualMachineConfig) -> bool {
+    match config {
+        VirtualMachineConfig::AppConfig(_) => true,
+        VirtualMachineConfig::RawConfig(config) => config.name == "microdroid",
+    }
+}
+
 fn extract_vendor_hashtree_digest(config: &VirtualMachineConfig) -> Result<Option<Vec<u8>>> {
     let VirtualMachineConfig::AppConfig(config) = config else {
         return Ok(None);
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index 46f4e80..1ccabec 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -360,12 +360,15 @@
     #[allow(dead_code)] // Keeps the global context alive
     pub(crate) global_context: Strong<dyn IGlobalVmContext>,
     #[allow(dead_code)] // Keeps the server alive
-    vm_server: RpcServer,
+    vm_server: Option<RpcServer>,
 }
 
 impl VmContext {
     /// Construct new VmContext.
-    pub fn new(global_context: Strong<dyn IGlobalVmContext>, vm_server: RpcServer) -> VmContext {
+    pub fn new(
+        global_context: Strong<dyn IGlobalVmContext>,
+        vm_server: Option<RpcServer>,
+    ) -> VmContext {
         VmContext { global_context, vm_server }
     }
 }
@@ -655,7 +658,9 @@
 
         // Now that the VM has been killed, shut down the VirtualMachineService
         // server to eagerly free up the server threads.
-        self.vm_context.vm_server.shutdown()?;
+        if let Some(vm_server) = &self.vm_context.vm_server {
+            vm_server.shutdown()?;
+        }
 
         Ok(())
     }
diff --git a/android/vm/vm_shell.sh b/android/vm/vm_shell.sh
index b73a9dc..60d9329 100755
--- a/android/vm/vm_shell.sh
+++ b/android/vm/vm_shell.sh
@@ -30,7 +30,7 @@
     echo "        /apex/com.android.virt/bin/vm run-microdroid binary."
     echo ""
     echo "        E.g.:"
-    echo "            vm_shell start-microdroid -- --cpu 5"
+    echo "            vm_shell start-microdroid -- --protected --debug full"
     echo ""
     echo "        --auto-connect - automatically connects to the started VMs"
     echo ""
diff --git a/build/debian/build.sh b/build/debian/build.sh
index 1e43b60..bd918dd 100755
--- a/build/debian/build.sh
+++ b/build/debian/build.sh
@@ -12,6 +12,7 @@
 	echo "-h         Print usage and this help message and exit."
 	echo "-a ARCH    Architecture of the image [default is aarch64]"
 	echo "-r         Release mode build"
+	echo "-w         Save temp work directory (for debugging)"
 }
 
 check_sudo() {
@@ -22,7 +23,7 @@
 }
 
 parse_options() {
-	while getopts "hra:" option; do
+	while getopts "a:hrw" option; do
 		case ${option} in
 			h)
 				show_help
@@ -40,6 +41,9 @@
 			r)
 				mode=release
 				;;
+			w)
+				save_workdir=1
+				;;
 			*)
 				echo "Invalid option: $OPTARG"
 				exit
@@ -51,6 +55,16 @@
 	fi
 }
 
+prepare_build_id() {
+	local file=${workdir}/build_id
+	if [ -z "${KOKORO_BUILD_NUMBER}" ]; then
+		echo eng-$(hostname)-$(date --utc) > ${file}
+	else
+		echo ${KOKOR_BUILD_NUMBER} > ${file}
+	fi
+	echo ${file}
+}
+
 install_prerequisites() {
 	apt update
 	packages=(
@@ -203,7 +217,7 @@
 }
 
 clean_up() {
-	rm -rf "${workdir}"
+	[ "$save_workdir" -eq 0 ] || rm -rf "${workdir}"
 }
 
 set -e
@@ -211,6 +225,7 @@
 
 built_image=image.raw
 workdir=$(mktemp -d)
+build_id=$(prepare_build_id)
 debian_cloud_image=${workdir}/debian_cloud_image
 debian_version=bookworm
 config_space=${debian_cloud_image}/config_space/${debian_version}
@@ -218,6 +233,8 @@
 arch=aarch64
 debian_arch=arm64
 mode=debug
+save_workdir=0
+
 parse_options "$@"
 check_sudo
 install_prerequisites
@@ -238,6 +255,7 @@
 	)
 # TODO(b/365955006): remove these lines when uboot supports x86_64 EFI application
 elif [[ "$arch" == "x86_64" ]]; then
+	rm -f vmlinuz initrd.img
 	virt-get-kernel -a "${built_image}"
 	mv vmlinuz* vmlinuz
 	mv initrd.img* initrd.img
@@ -251,4 +269,4 @@
 fi
 
 # --sparse option isn't supported in apache-commons-compress
-tar czv -f images.tar.gz "${images[@]}" vm_config.json
+tar czv -f images.tar.gz ${build_id} "${images[@]}" vm_config.json
diff --git a/build/debian/build_in_container.sh b/build/debian/build_in_container.sh
index d5680e0..7fd4c00 100755
--- a/build/debian/build_in_container.sh
+++ b/build/debian/build_in_container.sh
@@ -4,7 +4,9 @@
 
 arch=aarch64
 release_flag=
-while getopts "ra:" option; do
+save_workdir_flag=
+
+while getopts "a:rw" option; do
   case ${option} in
     a)
       if [[ "$OPTARG" != "aarch64" && "$OPTARG" != "x86_64" ]]; then
@@ -16,6 +18,9 @@
     r)
       release_flag="-r"
       ;;
+    w)
+      save_workdir_flag="-w"
+      ;;
     *)
       echo "Invalid option: $OPTARG"
       exit
@@ -23,6 +28,8 @@
   esac
 done
 
-docker run --privileged -it --workdir /root/Virtualization/build/debian -v \
-  "$ANDROID_BUILD_TOP/packages/modules/Virtualization:/root/Virtualization" -v \
-  /dev:/dev ubuntu:22.04 /root/Virtualization/build/debian/build.sh -a "$arch" $release_flag
+docker run --privileged -it -v /dev:/dev \
+  -v "$ANDROID_BUILD_TOP/packages/modules/Virtualization:/root/Virtualization" \
+  --workdir /root/Virtualization/build/debian \
+  ubuntu:22.04 \
+  bash -c "/root/Virtualization/build/debian/build.sh -a $arch $release_flag $save_workdir_flag || bash"
diff --git a/build/debian/release.sh b/build/debian/release.sh
new file mode 100755
index 0000000..437f9c8
--- /dev/null
+++ b/build/debian/release.sh
@@ -0,0 +1,101 @@
+#!/bin/bash
+
+# This is a script to release the Debian image built by Kokoro to Lorry.
+
+set -e
+
+show_help() {
+	echo "Usage: $0 [OPTION]..."
+	echo "Fetches a debian image from Placer and releases it to /android/ferrochrome/ARCH/TAG"
+	echo "Options:"
+	echo "-h            Print usage and this help message and exit."
+	echo "-a ARCH       Architecture of the image. Defaults to all supported architectures."
+	echo "-b BUILD_ID   Build ID to fetch. If omitted, latest build ID is selected."
+	echo "-t TAG        Tag name to attach to the release. Defaults to BUILD_ID."
+}
+
+parse_opt() {
+	while getopts "ha:b:t:" option; do
+		case ${option} in
+			h)
+				show_help
+				exit;;
+			a)
+				if [[ "$OPTARG" != "aarch64" && "$OPTARG" != "x86_64" ]]; then
+					echo "Invalid architecture: $OPTARG"
+					exit
+				fi
+				arch="$OPTARG"
+				;;
+			b)
+				build_id="$OPTARG"
+				;;
+			t)
+				tag="$OPTARG"
+				;;
+			*)
+				echo "Invalid option: $OPTARG"
+				exit
+				;;
+		esac
+	done
+
+	if [ "${build_id}" != "latest" ]; then
+		echo "Build ID is ambiguous when architecture is not set"
+		exit
+	fi
+}
+
+arch=all
+build_id=latest
+tag=
+placer_url="/placer/test/home/kokoro-dedicated-qa/build_artifacts/qa/android-ferrochrome"
+image_filename="images.tar.gz"
+
+get_build_id() {
+	local arch=$1
+	local build_id=$2
+	if [ "${build_id}" == "latest" ]; then
+		local pattern=${placer_url}/${arch}/continuous
+		build_id=$(basename $(fileutil ls ${pattern} | sort -V | tail -1))
+	fi
+	echo ${build_id}
+}
+
+get_image_path() {
+	local arch=$1
+	local build_id=$2
+	local pattern=${placer_url}/${arch}/continuous/${build_id}/*/${image_filename}
+	image=$(fileutil ls ${pattern} | tail -1)
+	if [ $? -ne 0 ]; then
+		echo "Cannot find image"
+		exit
+	fi
+	echo ${image}
+}
+
+do_release() {
+	local arch=$1
+	local build_id=$2
+
+	build_id=$(get_build_id ${arch} ${build_id})
+	echo "Using build ID ${build_id} for ${arch}"
+	local image=$(get_image_path ${arch} ${build_id})
+
+	local tag=${tag:-${build_id}}
+	local serving_url=/android/ferrochrome/${arch}/${tag}/${image_filename}
+	echo "Releasing ${image} to ${serving_url}"
+
+	local request='payload : { url_path: '"\"${serving_url}\""' source_path : '"\"${image}\""' }'
+	local id=$(stubby call blade:download-lorry-api LorryService.CreatePayloads "${request}" | cut -d\  -f2)
+	echo "Done. Visit https://lorry.corp.google.com/view/${id} to get an approval for the release."
+}
+
+parse_opt "$@"
+
+if [ "${arch}" == "all" ]; then
+	do_release aarch64 ${build_id}
+	do_release x86_64 ${build_id}
+else
+	do_release ${arch} ${build_id}
+fi
diff --git a/guest/pvmfw/README.md b/guest/pvmfw/README.md
index 50fe3d3..8c8314d 100644
--- a/guest/pvmfw/README.md
+++ b/guest/pvmfw/README.md
@@ -450,6 +450,18 @@
 
 [soong-udroid]: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Virtualization/microdroid/Android.bp;l=425;drc=b94a5cf516307c4279f6c16a63803527a8affc6d
 
+#### VBMeta Properties
+
+AVF defines special keys for AVB VBMeta descriptor properties that pvmfw
+recognizes, allowing VM owners to ensure that pvmfw performs its role in a way
+that is compatible with their guest kernel. These are:
+
+- `"com.android.virt.cap"`: a `|`-separated list of "capabilities" from
+  - `remote_attest`: pvmfw uses a hard-coded index for rollback protection
+  - `secretkeeper_protection`: pvmfw defers rollback protection to the guest
+  - `supports_uefi_boot`: pvmfw boots the VM as a EFI payload (experimental)
+  - `trusty_security_vm`: pvmfw skips rollback protection
+
 ## Development
 
 For faster iteration, you can build pvmfw, adb-push it to the device, and use
diff --git a/guest/pvmfw/src/fdt.rs b/guest/pvmfw/src/fdt.rs
index 6bbb05e..027f163 100644
--- a/guest/pvmfw/src/fdt.rs
+++ b/guest/pvmfw/src/fdt.rs
@@ -1140,10 +1140,15 @@
         RebootReason::InvalidFdt
     })?;
 
-    let swiotlb_info = SwiotlbInfo::new_from_fdt(fdt).map_err(|e| {
-        error!("Failed to read swiotlb info from DT: {e}");
-        RebootReason::InvalidFdt
-    })?;
+    let swiotlb_info = SwiotlbInfo::new_from_fdt(fdt)
+        .map_err(|e| {
+            error!("Failed to read swiotlb info from DT: {e}");
+            RebootReason::InvalidFdt
+        })?
+        .ok_or_else(|| {
+            error!("Swiotlb info missing from DT");
+            RebootReason::InvalidFdt
+        })?;
     validate_swiotlb_info(&swiotlb_info, &memory_range)?;
 
     let device_assignment = match vm_dtbo {
diff --git a/guest/rialto/src/main.rs b/guest/rialto/src/main.rs
index 0b79e1e..ec26e0f 100644
--- a/guest/rialto/src/main.rs
+++ b/guest/rialto/src/main.rs
@@ -117,7 +117,7 @@
         MEMORY.lock().as_mut().unwrap().init_dynamic_shared_pool(granule).inspect_err(|_| {
             error!("Failed to initialize dynamically shared pool.");
         })?;
-    } else if let Ok(swiotlb_info) = SwiotlbInfo::new_from_fdt(fdt) {
+    } else if let Ok(Some(swiotlb_info)) = SwiotlbInfo::new_from_fdt(fdt) {
         let range = swiotlb_info.fixed_range().ok_or_else(|| {
             error!("Pre-shared pool range not specified in swiotlb node");
             Error::from(FdtError::BadValue)
diff --git a/guest/vmbase_example/src/layout.rs b/guest/vmbase_example/src/layout.rs
index 55c7283..4e87e4e 100644
--- a/guest/vmbase_example/src/layout.rs
+++ b/guest/vmbase_example/src/layout.rs
@@ -14,14 +14,11 @@
 
 //! Memory layout.
 
-use aarch64_paging::paging::{MemoryRegion, VirtualAddress};
+use aarch64_paging::paging::VirtualAddress;
 use core::ops::Range;
 use log::info;
 use vmbase::{layout, memory::PAGE_SIZE};
 
-pub const DEVICE_REGION: MemoryRegion =
-    MemoryRegion::new(layout::crosvm::MMIO_START, layout::crosvm::MMIO_END);
-
 /// Writable data region for the stack.
 pub fn boot_stack_range() -> Range<VirtualAddress> {
     layout::stack_range(40 * PAGE_SIZE)
diff --git a/guest/vmbase_example/src/main.rs b/guest/vmbase_example/src/main.rs
index 1466d1e..c7ef061 100644
--- a/guest/vmbase_example/src/main.rs
+++ b/guest/vmbase_example/src/main.rs
@@ -23,8 +23,8 @@
 
 extern crate alloc;
 
-use crate::layout::{boot_stack_range, print_addresses, DEVICE_REGION};
-use crate::pci::{check_pci, get_bar_region};
+use crate::layout::{boot_stack_range, print_addresses};
+use crate::pci::{check_pci, get_bar_region, get_cam_region};
 use aarch64_paging::paging::VirtualAddress;
 use aarch64_paging::MapError;
 use alloc::{vec, vec::Vec};
@@ -37,10 +37,9 @@
     bionic, configure_heap,
     fdt::pci::PciInfo,
     generate_image_header,
-    layout::{crosvm::FDT_MAX_SIZE, rodata_range, scratch_range, text_range},
+    layout::{console_uart_page, crosvm::FDT_MAX_SIZE, rodata_range, scratch_range, text_range},
     linker, logger, main,
     memory::{PageTable, SIZE_64KB},
-    util::RangeExt as _,
 };
 
 static INITIALISED_DATA: [u32; 4] = [1, 2, 3, 4];
@@ -52,7 +51,7 @@
 configure_heap!(SIZE_64KB);
 
 fn init_page_table(page_table: &mut PageTable) -> Result<(), MapError> {
-    page_table.map_device(&DEVICE_REGION)?;
+    page_table.map_device(&console_uart_page().into())?;
     page_table.map_code(&text_range().into())?;
     page_table.map_rodata(&rodata_range().into())?;
     page_table.map_data(&scratch_range().into())?;
@@ -99,13 +98,10 @@
 
     check_alloc();
 
+    let cam_region = get_cam_region(&pci_info);
+    page_table.map_device(&cam_region).unwrap();
     let bar_region = get_bar_region(&pci_info);
-    if bar_region.is_within(&DEVICE_REGION) {
-        // Avoid a MapError::BreakBeforeMakeViolation.
-        info!("BAR region is within already mapped device region: skipping page table ops.");
-    } else {
-        page_table.map_device(&bar_region).unwrap();
-    }
+    page_table.map_device(&bar_region).unwrap();
 
     check_data();
     check_dice();
diff --git a/guest/vmbase_example/src/pci.rs b/guest/vmbase_example/src/pci.rs
index 563f24a..379425d 100644
--- a/guest/vmbase_example/src/pci.rs
+++ b/guest/vmbase_example/src/pci.rs
@@ -120,6 +120,11 @@
     MemoryRegion::new(pci_info.bar_range.start as usize, pci_info.bar_range.end as usize)
 }
 
+/// Gets the PCI CAM memory region.
+pub fn get_cam_region(pci_info: &PciInfo) -> MemoryRegion {
+    MemoryRegion::new(pci_info.cam_range.start, pci_info.cam_range.end)
+}
+
 struct HalImpl;
 
 /// SAFETY: See the 'Implementation Safety' comments on methods below for how they fulfill the
diff --git a/libs/libcompos_common/compos_client.rs b/libs/libcompos_common/compos_client.rs
index 6872582..316eaa9 100644
--- a/libs/libcompos_common/compos_client.rs
+++ b/libs/libcompos_common/compos_client.rs
@@ -58,8 +58,6 @@
 pub struct VmParameters {
     /// The name of VM for identifying.
     pub name: String,
-    /// The OS of VM.
-    pub os: String,
     /// Whether the VM should be debuggable.
     pub debug_mode: bool,
     /// CPU topology of the VM. Defaults to 1 vCPU.
@@ -131,7 +129,6 @@
 
         let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
             name: parameters.name.clone(),
-            osName: parameters.os.clone(),
             apk: Some(apk_fd),
             idsig: Some(idsig_fd),
             instanceId: instance_id,
diff --git a/libs/libvmbase/sections.ld b/libs/libvmbase/sections.ld
index 7d464bc..5ca5ff4 100644
--- a/libs/libvmbase/sections.ld
+++ b/libs/libvmbase/sections.ld
@@ -96,11 +96,16 @@
 		bss_end = .;
 	} >writable_data
 
-	init_stack_pointer = ORIGIN(writable_data) + LENGTH(writable_data);
+	/* Left unmapped, to catch overflows of the stack. */
+	.stack_guard_page (NOLOAD) : ALIGN(4096) {
+		. += 4096;
+	} >writable_data
+
+	/* Stack, mapped read-write (possibly partially). */
 	.stack (NOLOAD) : ALIGN(4096) {
-		. += 4096; /* Ensure we have one guard page for overflow. */
 		stack_limit = .;
-		. = init_stack_pointer;
+		. = ALIGN(LENGTH(writable_data));
+		init_stack_pointer = .;
 	} >writable_data
 
 	/* Make our Bionic stack protector compatible with mainline LLVM */
diff --git a/libs/libvmbase/src/fdt.rs b/libs/libvmbase/src/fdt.rs
index ff0eaf0..aaf354e 100644
--- a/libs/libvmbase/src/fdt.rs
+++ b/libs/libvmbase/src/fdt.rs
@@ -33,20 +33,20 @@
 
 impl SwiotlbInfo {
     /// Creates a `SwiotlbInfo` struct from the given device tree.
-    pub fn new_from_fdt(fdt: &Fdt) -> libfdt::Result<SwiotlbInfo> {
-        let node =
-            fdt.compatible_nodes(cstr!("restricted-dma-pool"))?.next().ok_or(FdtError::NotFound)?;
-
+    pub fn new_from_fdt(fdt: &Fdt) -> libfdt::Result<Option<SwiotlbInfo>> {
+        let Some(node) = fdt.compatible_nodes(cstr!("restricted-dma-pool"))?.next() else {
+            return Ok(None);
+        };
         let (addr, size, align) = if let Some(mut reg) = node.reg()? {
-            let reg = reg.next().ok_or(FdtError::NotFound)?;
-            let size = reg.size.ok_or(FdtError::NotFound)?;
+            let reg = reg.next().ok_or(FdtError::BadValue)?;
+            let size = reg.size.ok_or(FdtError::BadValue)?;
             (Some(reg.addr.try_into().unwrap()), size.try_into().unwrap(), None)
         } else {
             let size = node.getprop_u64(cstr!("size"))?.ok_or(FdtError::NotFound)?;
             let align = node.getprop_u64(cstr!("alignment"))?.ok_or(FdtError::NotFound)?;
             (None, size.try_into().unwrap(), Some(align.try_into().unwrap()))
         };
-        Ok(Self { addr, size, align })
+        Ok(Some(Self { addr, size, align }))
     }
 
     /// Returns the fixed range of memory mapped by the SWIOTLB buffer, if available.
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index fefedc9..630df87 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -405,6 +405,7 @@
                         VIRT_APEX + "bin/vm run",
                         "--console " + CONSOLE_PATH,
                         "--log " + LOG_PATH,
+                        "--name " + "microdroid", // to still be seen as microdroid vm
                         configPath);
 
         PipedInputStream pis = new PipedInputStream();