Merge "build.sh: Add support for x86_64 architecture in the Debian image build script." into main
diff --git a/android/TerminalApp/Android.bp b/android/TerminalApp/Android.bp
index 1a7c581..d91af2f 100644
--- a/android/TerminalApp/Android.bp
+++ b/android/TerminalApp/Android.bp
@@ -4,10 +4,16 @@
 
 android_app {
     name: "VmTerminalApp",
-    srcs: ["java/**/*.java"],
+    srcs: [
+        "java/**/*.java",
+        "java/**/*.kt",
+    ],
     resource_dirs: ["res"],
     static_libs: [
         "vm_launcher_lib",
+        "androidx-constraintlayout_constraintlayout",
+        "com.google.android.material_material",
+        "androidx.window_window",
     ],
     platform_apis: true,
     privileged: true,
diff --git a/android/TerminalApp/AndroidManifest.xml b/android/TerminalApp/AndroidManifest.xml
index e338c49..f09412e 100644
--- a/android/TerminalApp/AndroidManifest.xml
+++ b/android/TerminalApp/AndroidManifest.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.virtualization.terminal" >
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.android.virtualization.terminal">
 
     <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
     <uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
@@ -9,9 +10,11 @@
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
 
     <uses-feature android:name="android.software.virtualization_framework" android:required="true" />
+
     <application
 	android:label="@string/app_name"
         android:icon="@mipmap/ic_launcher"
+        android:theme="@style/Theme.Material3.DayNight.NoActionBar"
         android:usesCleartextTraffic="true">
         <activity android:name=".MainActivity"
                   android:configChanges="orientation|screenSize|keyboard|keyboardHidden|navigation|uiMode|screenLayout|smallestScreenSize"
@@ -21,6 +24,26 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+        <activity android:name=".SettingsActivity">
+        </activity>
+        <activity android:name=".SettingsDiskResizeActivity">
+        </activity>
+        <activity android:name=".SettingsPortForwardingActivity">
+        </activity>
+        <activity android:name=".SettingsRecoveryActivity">
+        </activity>
+        <property
+            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
+            android:value="true" />
+        <provider
+            android:name="androidx.startup.InitializationProvider"
+            android:authorities="${applicationId}.androidx-startup"
+            android:exported="false"
+            tools:node="merge">
+            <meta-data
+                android:name="${applicationId}.SplitInitializer"
+                android:value="androidx.startup" />
+        </provider>
         <activity-alias
             android:name=".MainActivityAlias"
             android:targetActivity="com.android.virtualization.terminal.MainActivity"
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index a6723fb..846f975 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -15,9 +15,9 @@
  */
 package com.android.virtualization.terminal;
 
-import android.app.Activity;
 import android.content.ClipData;
 import android.content.ClipboardManager;
+import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -30,9 +30,14 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import androidx.appcompat.app.AppCompatActivity;
+
 import com.android.virtualization.vmlauncher.VmLauncherServices;
 
-public class MainActivity extends Activity implements VmLauncherServices.VmLauncherServiceCallback {
+import com.google.android.material.appbar.MaterialToolbar;
+
+public class MainActivity extends AppCompatActivity implements
+        VmLauncherServices.VmLauncherServiceCallback {
     private static final String TAG = "VmTerminalApp";
     private String mVmIpAddr;
     private WebView mWebView;
@@ -44,6 +49,9 @@
         VmLauncherServices.startVmLauncherService(this, this);
 
         setContentView(R.layout.activity_headless);
+
+        MaterialToolbar toolbar = (MaterialToolbar) findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
         mWebView = (WebView) findViewById(R.id.webview);
         mWebView.getSettings().setDatabaseEnabled(true);
         mWebView.getSettings().setDomStorageEnabled(true);
@@ -101,7 +109,7 @@
     }
 
     @Override
-    public boolean onMenuItemSelected(int featureId, MenuItem item) {
+    public boolean onOptionsItemSelected(MenuItem item) {
         int id = item.getItemId();
         if (id == R.id.copy_ip_addr) {
             // TODO(b/340126051): remove this menu item when port forwarding is supported.
@@ -111,7 +119,12 @@
         } else if (id == R.id.stop_vm) {
             VmLauncherServices.stopVmLauncherService(this);
             return true;
+
+        } else if (id == R.id.menu_item_settings) {
+            Intent intent = new Intent(this, SettingsActivity.class);
+            this.startActivity(intent);
+            return true;
         }
-        return super.onMenuItemSelected(featureId, item);
+        return super.onOptionsItemSelected(item);
     }
 }
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt
new file mode 100644
index 0000000..dccfea3
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+
+class SettingsActivity : AppCompatActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.settings_activity)
+
+        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)
+        recyclerView.layoutManager = LinearLayoutManager(this)
+        recyclerView.adapter = settingsListItemAdapter
+    }
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
new file mode 100644
index 0000000..4be291f
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.os.Bundle
+import android.widget.TextView
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.isVisible
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.slider.Slider
+
+class SettingsDiskResizeActivity : AppCompatActivity() {
+    private var diskSize: Float = 104F
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.settings_disk_resize)
+        val diskSizeText = findViewById<TextView>(R.id.settings_disk_resize_disk_size)
+        val diskSizeSlider = findViewById<Slider>(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)
+        diskSizeText.text = diskSize.toInt().toString()
+        diskSizeSlider.value = diskSize
+
+        diskSizeSlider.addOnChangeListener { _, value, _ ->
+            diskSizeText.text = value.toInt().toString()
+            cancelButton.isVisible = true
+            resizeButton.isVisible = true
+        }
+        cancelButton.setOnClickListener {
+            diskSizeSlider.value = diskSize
+            cancelButton.isVisible = false
+            resizeButton.isVisible = false
+        }
+
+        resizeButton.setOnClickListener {
+            diskSize = diskSizeSlider.value
+            cancelButton.isVisible = false
+            resizeButton.isVisible = false
+            Toast.makeText(this@SettingsDiskResizeActivity, R.string.settings_disk_resize_resize_message, Toast.LENGTH_SHORT)
+                .show()
+        }
+    }
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItem.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItem.kt
new file mode 100644
index 0000000..e1723a7
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItem.kt
@@ -0,0 +1,28 @@
+/*
+ * 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
+
+enum class SettingsItemEnum {
+    DiskResize, PortForwarding, Recovery
+}
+
+class SettingsItem(
+    val title: String,
+    val subTitle: String,
+    val icon: Int,
+    val settingsItemEnum: SettingsItemEnum
+) {
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt
new file mode 100644
index 0000000..86f5c92
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.content.Intent
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.card.MaterialCardView
+
+class SettingsItemAdapter(private val dataSet: Array<SettingsItem>) :
+    RecyclerView.Adapter<SettingsItemAdapter.ViewHolder>() {
+
+    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+        val card: MaterialCardView = view.findViewById(R.id.settings_list_item_card)
+        val icon: ImageView = view.findViewById(R.id.settings_list_item_icon)
+        val title: TextView = view.findViewById(R.id.settings_list_item_title)
+        val subTitle: TextView = view.findViewById(R.id.settings_list_item_sub_title)
+    }
+
+    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
+        val view = LayoutInflater.from(viewGroup.context)
+            .inflate(R.layout.settings_list_item, viewGroup, false)
+        return ViewHolder(view)
+    }
+
+    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
+        viewHolder.icon.setImageResource(dataSet[position].icon)
+        viewHolder.title.text = dataSet[position].title
+        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
+                }
+            )
+            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
new file mode 100644
index 0000000..6c36cc8
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+
+class SettingsPortForwardingActivity : AppCompatActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.settings_port_forwarding)
+
+        val settingsPortForwardingItems = arrayOf(
+            SettingsPortForwardingItem(8080, true),
+            SettingsPortForwardingItem(443, false),
+            SettingsPortForwardingItem(80, false)
+        )
+
+        val settingsPortForwardingAdapter =
+            SettingsPortForwardingAdapter(settingsPortForwardingItems)
+
+        val recyclerView: RecyclerView = findViewById(R.id.settings_port_forwarding_recycler_view)
+        recyclerView.layoutManager = LinearLayoutManager(this)
+        recyclerView.adapter = settingsPortForwardingAdapter
+    }
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingAdapter.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingAdapter.kt
new file mode 100644
index 0000000..1fa38e3
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingAdapter.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.materialswitch.MaterialSwitch
+
+class SettingsPortForwardingAdapter(private val dataSet: Array<SettingsPortForwardingItem>) :
+    RecyclerView.Adapter<SettingsPortForwardingAdapter.ViewHolder>() {
+
+    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+        val enabledSwitch: MaterialSwitch =
+            view.findViewById(R.id.settings_port_forwarding_item_enabled_switch)
+        val port: TextView = view.findViewById(R.id.settings_port_forwarding_item_port)
+    }
+
+    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
+        val view = LayoutInflater.from(viewGroup.context)
+            .inflate(R.layout.settings_port_forwarding_item, viewGroup, false)
+        return ViewHolder(view)
+    }
+
+    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
+        viewHolder.port.text = dataSet[position].port.toString()
+        viewHolder.enabledSwitch.isChecked = dataSet[position].enabled
+    }
+
+    override fun getItemCount() = dataSet.size
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingItem.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingItem.kt
new file mode 100644
index 0000000..599e377
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingItem.kt
@@ -0,0 +1,18 @@
+/*
+ * 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
+
+class SettingsPortForwardingItem(val port: Int, val enabled: Boolean) {}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt
new file mode 100644
index 0000000..7256015
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.os.Bundle
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import com.google.android.material.card.MaterialCardView
+
+class SettingsRecoveryActivity : AppCompatActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.settings_recovery)
+        val resetCard = findViewById<MaterialCardView>(R.id.settings_recovery_reset_card)
+        resetCard.setOnClickListener {
+            Toast.makeText(this@SettingsRecoveryActivity, R.string.settings_recovery_reset_message, Toast.LENGTH_SHORT).show()
+        }
+    }
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt
new file mode 100644
index 0000000..cb917bd
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.content.Context
+import androidx.startup.Initializer
+import androidx.window.embedding.RuleController
+
+class SplitInitializer : Initializer<RuleController> {
+
+    override fun create(context: Context): RuleController {
+        return RuleController.getInstance(context).apply {
+            setRules(RuleController.parseRules(context, R.xml.main_split_config))
+        }
+    }
+
+    override fun dependencies(): List<Class<out Initializer<*>>> {
+        return emptyList()
+    }
+}
\ No newline at end of file
diff --git a/android/TerminalApp/res/drawable/baseline_call_missed_outgoing_24.xml b/android/TerminalApp/res/drawable/baseline_call_missed_outgoing_24.xml
new file mode 100644
index 0000000..597c317
--- /dev/null
+++ b/android/TerminalApp/res/drawable/baseline_call_missed_outgoing_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+
+    <path android:fillColor="@android:color/white" android:pathData="M3,8.41l9,9l7,-7V15h2V7h-8v2h4.59L12,14.59L4.41,7L3,8.41z"/>
+
+</vector>
diff --git a/android/TerminalApp/res/drawable/baseline_settings_backup_restore_24.xml b/android/TerminalApp/res/drawable/baseline_settings_backup_restore_24.xml
new file mode 100644
index 0000000..22b23ba
--- /dev/null
+++ b/android/TerminalApp/res/drawable/baseline_settings_backup_restore_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+
+    <path android:fillColor="@android:color/white" android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12,3c-4.97,0 -9,4.03 -9,9L0,12l4,4 4,-4L5,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z"/>
+
+</vector>
diff --git a/android/TerminalApp/res/drawable/baseline_storage_24.xml b/android/TerminalApp/res/drawable/baseline_storage_24.xml
new file mode 100644
index 0000000..6e52e3f
--- /dev/null
+++ b/android/TerminalApp/res/drawable/baseline_storage_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+
+    <path android:fillColor="@android:color/white" android:pathData="M2,20h20v-4L2,16v4zM4,17h2v2L4,19v-2zM2,4v4h20L22,4L2,4zM6,7L4,7L4,5h2v2zM2,14h20v-4L2,10v4zM4,11h2v2L4,13v-2z"/>
+
+</vector>
diff --git a/android/TerminalApp/res/layout/activity_headless.xml b/android/TerminalApp/res/layout/activity_headless.xml
index 3fe5271..f786a0f 100644
--- a/android/TerminalApp/res/layout/activity_headless.xml
+++ b/android/TerminalApp/res/layout/activity_headless.xml
@@ -7,6 +7,11 @@
     android:orientation="vertical"
     android:fitsSystemWindows="true"
     tools:context=".MainActivity">
+    <com.google.android.material.appbar.MaterialToolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="?attr/actionBarSize"
+        app:layout_constraintTop_toTopOf="parent"/>
     <TextView
         android:id="@+id/ip_addr_textview"
         android:layout_width="wrap_content"
diff --git a/android/TerminalApp/res/layout/settings_activity.xml b/android/TerminalApp/res/layout/settings_activity.xml
new file mode 100644
index 0000000..b1acf23
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_activity.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:fitsSystemWindows="true">
+
+    <com.google.android.material.search.SearchBar
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/settings_list_recycler_view"
+        android:layout_marginHorizontal="16dp"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+</LinearLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/settings_disk_resize.xml b/android/TerminalApp/res/layout/settings_disk_resize.xml
new file mode 100644
index 0000000..3c09f52
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_disk_resize.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="vertical"
+    android:layout_marginStart="24dp"
+    android:layout_marginEnd="24dp"
+    android:layout_marginTop="24dp"
+    android:fitsSystemWindows="true">
+
+    <TextView
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:text="@string/settings_disk_resize_title"
+        android:textSize="48sp"
+        android:layout_marginBottom="24dp"/>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <TextView
+            android:id="@+id/settings_disk_resize_disk_size"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:textSize="36sp"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintBottom_toTopOf="@+id/settings_disk_resize_disk_size_slider"/>
+
+        <TextView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/settings_disk_resize_resize_gb_assigned"
+            android:textSize="14sp"
+            app:layout_constraintLeft_toRightOf="@+id/settings_disk_resize_disk_size"
+            app:layout_constraintBottom_toTopOf="@+id/settings_disk_resize_disk_size_slider"/>
+
+        <TextView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/settings_disk_resize_resize_gb_total"
+            android:textSize="14sp"
+            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintBottom_toTopOf="@+id/settings_disk_resize_disk_size_slider"/>
+
+        <com.google.android.material.slider.Slider
+            android:id="@+id/settings_disk_resize_disk_size_slider"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_marginBottom="36dp"
+            app:tickVisible="false"
+            android:valueFrom="0"
+            android:valueTo="256"
+            android:stepSize="4"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent" />
+
+        <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:text="@string/settings_disk_resize_resize_cancel"
+            android:visibility="invisible"
+            android:layout_marginHorizontal="8dp"
+            app:layout_constraintTop_toTopOf="@+id/settings_disk_resize_disk_size_slider"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintRight_toLeftOf="@+id/settings_disk_resize_resize_button" />
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/settings_disk_resize_resize_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/settings_disk_resize_resize_restart_vm_to_apply"
+            android:visibility="invisible"
+            android:layout_marginHorizontal="8dp"
+            app:layout_constraintTop_toTopOf="@+id/settings_disk_resize_disk_size_slider"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintRight_toRightOf="parent" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/settings_list_item.xml b/android/TerminalApp/res/layout/settings_list_item.xml
new file mode 100644
index 0000000..89f2d82
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_list_item.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:gravity="center_vertical"
+    android:layout_height="wrap_content">
+
+    <com.google.android.material.card.MaterialCardView
+        android:id="@+id/settings_list_item_card"
+        app:strokeWidth="0dp"
+        app:cardCornerRadius="28dp"
+        app:checkedIcon="@null"
+        android:focusable="true"
+        android:checkable="true"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="88dp"
+            android:layout_marginStart="24dp"
+            android:layout_marginEnd="16dp">
+
+            <com.google.android.material.imageview.ShapeableImageView
+                android:id="@+id/settings_list_item_icon"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_marginEnd="24dp"
+                android:scaleType="centerCrop"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintLeft_toLeftOf="parent" />
+
+            <TextView
+                android:id="@+id/settings_list_item_title"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="20dp"
+                android:layout_marginStart="24dp"
+                android:textSize="20sp"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toTopOf="@+id/settings_list_item_sub_title"
+                app:layout_constraintStart_toEndOf="@id/settings_list_item_icon"
+                app:layout_constraintEnd_toEndOf="parent" />
+
+            <TextView
+                android:id="@+id/settings_list_item_sub_title"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:textSize="14sp"
+                android:layout_marginBottom="20dp"
+                android:layout_marginStart="24dp"
+                app:layout_constraintTop_toBottomOf="@+id/settings_list_item_title"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toEndOf="@id/settings_list_item_icon"
+                app:layout_constraintEnd_toEndOf="parent" />
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </com.google.android.material.card.MaterialCardView>
+</FrameLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/settings_port_forwarding.xml b/android/TerminalApp/res/layout/settings_port_forwarding.xml
new file mode 100644
index 0000000..1d68907
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_port_forwarding.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:layout_marginStart="24dp"
+    android:layout_marginEnd="24dp"
+    android:layout_marginTop="24dp"
+    android:fitsSystemWindows="true">
+
+    <TextView
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:text="@string/settings_port_forwarding_title"
+        android:textSize="48sp"
+        android:layout_marginBottom="24dp"/>
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/settings_port_forwarding_recycler_view"
+        android:layout_marginHorizontal="16dp"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+</LinearLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/settings_port_forwarding_item.xml b/android/TerminalApp/res/layout/settings_port_forwarding_item.xml
new file mode 100644
index 0000000..9e5981e
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_port_forwarding_item.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    app:layout_constraintCircleRadius="@dimen/material_emphasis_medium"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <TextView
+        android:id="@+id/settings_port_forwarding_item_port"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"/>
+
+    <com.google.android.material.materialswitch.MaterialSwitch
+        android:id="@+id/settings_port_forwarding_item_enabled_switch"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintRight_toRightOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/settings_recovery.xml b/android/TerminalApp/res/layout/settings_recovery.xml
new file mode 100644
index 0000000..e18c8a6
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_recovery.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="vertical"
+    android:layout_marginEnd="24dp"
+    android:layout_marginTop="24dp"
+    android:fitsSystemWindows="true">
+
+    <TextView
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:text="@string/settings_recovery_title"
+        android:textSize="48sp"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="24dp"/>
+
+    <com.google.android.material.card.MaterialCardView
+        android:id="@+id/settings_recovery_reset_card"
+        app:strokeWidth="0dp"
+        app:cardCornerRadius="0dp"
+        app:checkedIcon="@null"
+        android:focusable="true"
+        android:checkable="true"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="88dp"
+            android:layout_marginEnd="16dp"
+            android:layout_marginStart="24dp">
+
+            <TextView
+                android:id="@+id/settings_recovery_reset_title"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="20dp"
+                android:layout_marginStart="24dp"
+                android:textSize="20sp"
+                android:text="@string/settings_recovery_reset_title"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toTopOf="@+id/settings_recovery_reset_sub_title"
+                app:layout_constraintLeft_toLeftOf="parent" />
+
+            <TextView
+                android:id="@+id/settings_recovery_reset_sub_title"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:textSize="14sp"
+                android:layout_marginBottom="20dp"
+                android:layout_marginStart="24dp"
+                android:text="@string/settings_recovery_reset_sub_title"
+                app:layout_constraintTop_toBottomOf="@+id/settings_recovery_reset_title"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintLeft_toLeftOf="parent" />
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </com.google.android.material.card.MaterialCardView>
+</LinearLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/menu/main_menu.xml b/android/TerminalApp/res/menu/main_menu.xml
index cc34cda..9c83923 100644
--- a/android/TerminalApp/res/menu/main_menu.xml
+++ b/android/TerminalApp/res/menu/main_menu.xml
@@ -4,4 +4,6 @@
         android:title="Copy the IP address"/>
     <item android:id="@+id/stop_vm"
         android:title="Stop the existing VM instance"/>
+    <item android:id="@+id/menu_item_settings"
+        android:title="Settings"/>
 </menu>
\ No newline at end of file
diff --git a/android/TerminalApp/res/values/dimens.xml b/android/TerminalApp/res/values/dimens.xml
new file mode 100644
index 0000000..e6ed461
--- /dev/null
+++ b/android/TerminalApp/res/values/dimens.xml
@@ -0,0 +1,3 @@
+<resources>
+    <dimen name="activity_split_ratio">0.3</dimen>
+</resources>
\ No newline at end of file
diff --git a/android/TerminalApp/res/values/integers.xml b/android/TerminalApp/res/values/integers.xml
new file mode 100644
index 0000000..0c7d2b9
--- /dev/null
+++ b/android/TerminalApp/res/values/integers.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <integer name="split_min_width">720</integer>
+</resources>
\ No newline at end of file
diff --git a/android/TerminalApp/res/values/strings.xml b/android/TerminalApp/res/values/strings.xml
index 79da7cd..c3a3348 100644
--- a/android/TerminalApp/res/values/strings.xml
+++ b/android/TerminalApp/res/values/strings.xml
@@ -20,4 +20,21 @@
     <string name="vm_creation_message">Virtual machine is booting. Please wait.</string>
     <string name="vm_stop_message">Virtual machine is stopped. Exiting.</string>
     <string name="vm_error_message">Virtual machine crashed. Exiting.</string>
+
+    <string name="settings_disk_resize_title">Disk Resize</string>
+    <string name="settings_disk_resize_sub_title">Resize / Rootfs</string>
+    <string name="settings_disk_resize_resize_message">Disk size set</string>
+    <string name="settings_disk_resize_resize_gb_assigned">GB Assigned</string>
+    <string name="settings_disk_resize_resize_gb_total">256 GB total</string>
+    <string name="settings_disk_resize_resize_cancel">Cancel</string>
+    <string name="settings_disk_resize_resize_restart_vm_to_apply">Restart VM to apply</string>
+
+    <string name="settings_port_forwarding_title">Port Forwarding</string>
+    <string name="settings_port_forwarding_sub_title">Configure port forwarding</string>
+
+    <string name="settings_recovery_title">Recovery</string>
+    <string name="settings_recovery_sub_title">Partition Recovery options</string>
+    <string name="settings_recovery_reset_title">Change to Initial version</string>
+    <string name="settings_recovery_reset_sub_title">Remove all</string>
+    <string name="settings_recovery_reset_message">VM reset</string>
 </resources>
diff --git a/android/TerminalApp/res/xml/main_split_config.xml b/android/TerminalApp/res/xml/main_split_config.xml
new file mode 100644
index 0000000..f51e7ea
--- /dev/null
+++ b/android/TerminalApp/res/xml/main_split_config.xml
@@ -0,0 +1,37 @@
+<resources xmlns:window="http://schemas.android.com/apk/res-auto">
+
+    <!-- Define a split for the named activities. -->
+    <ActivityRule window:alwaysExpand="true">
+        <ActivityFilter window:activityName=".MainActivity" />
+    </ActivityRule>
+
+    <SplitPairRule
+        window:clearTop="true"
+        window:finishPrimaryWithSecondary="adjacent"
+        window:finishSecondaryWithPrimary="always"
+        window:splitLayoutDirection="locale"
+        window:splitMaxAspectRatioInPortrait="alwaysAllow"
+        window:splitMinWidthDp="@integer/split_min_width"
+        window:splitRatio="@dimen/activity_split_ratio">
+        <SplitPairFilter
+            window:primaryActivityName="com.android.virtualization.terminal.SettingsActivity"
+            window:secondaryActivityName="com.android.virtualization.terminal.SettingsDiskResizeActivity" />
+        <SplitPairFilter
+            window:primaryActivityName="com.android.virtualization.terminal.SettingsActivity"
+            window:secondaryActivityName="com.android.virtualization.terminal.SettingsPortForwardingActivity" />
+        <SplitPairFilter
+            window:primaryActivityName="com.android.virtualization.terminal.SettingsActivity"
+            window:secondaryActivityName="com.android.virtualization.terminal.SettingsRecoveryActivity" />
+    </SplitPairRule>
+
+    <SplitPlaceholderRule
+        window:placeholderActivityName="com.android.virtualization.terminal.SettingsDiskResizeActivity"
+        window:splitLayoutDirection="locale"
+        window:splitMaxAspectRatioInPortrait="alwaysAllow"
+        window:splitMinWidthDp="@integer/split_min_width"
+        window:splitRatio="@dimen/activity_split_ratio">
+        window:stickyPlaceholder="false">
+        <ActivityFilter
+            window:activityName="com.android.virtualization.terminal.SettingsActivity"/>
+    </SplitPlaceholderRule>
+</resources>
\ No newline at end of file
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 87fb611..23652d2 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -33,7 +33,7 @@
     CpuTopology::CpuTopology,
     DiskImage::DiskImage,
     InputDevice::InputDevice,
-    IVirtualMachine::{BnVirtualMachine, IVirtualMachine},
+    IVirtualMachine::{self, BnVirtualMachine},
     IVirtualMachineCallback::IVirtualMachineCallback,
     IVirtualizationService::IVirtualizationService,
     Partition::Partition,
@@ -62,9 +62,8 @@
 use apkverify::{HashAlgorithm, V4Signature};
 use avflog::LogResult;
 use binder::{
-    self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor,
-    Status, StatusCode, Strong,
-    IntoBinderResult,
+    self, wait_for_interface, Accessor, BinderFeatures, ConnectionInfo, ExceptionCode, Interface, ParcelFileDescriptor,
+    SpIBinder, Status, StatusCode, Strong, IntoBinderResult,
 };
 use cstr::cstr;
 use glob::glob;
@@ -90,7 +89,7 @@
 use std::sync::{Arc, Mutex, Weak, LazyLock};
 use vbmeta::VbMetaImage;
 use vmconfig::{VmConfig, get_debug_level};
-use vsock::VsockStream;
+use vsock::{VsockAddr, VsockStream};
 use zip::ZipArchive;
 
 /// The unique ID of a VM used (together with a port number) for vsock communication.
@@ -98,6 +97,9 @@
 
 pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
 
+/// Vsock privileged ports are below this number.
+const VSOCK_PRIV_PORT_MAX: u32 = 1024;
+
 /// The size of zero.img.
 /// Gaps in composite disk images are filled with a shared zero.img.
 const ZERO_FILLER_SIZE: u64 = 4096;
@@ -221,7 +223,8 @@
         console_out_fd: Option<&ParcelFileDescriptor>,
         console_in_fd: Option<&ParcelFileDescriptor>,
         log_fd: Option<&ParcelFileDescriptor>,
-    ) -> binder::Result<Strong<dyn IVirtualMachine>> {
+        dump_dt_fd: Option<&ParcelFileDescriptor>,
+    ) -> binder::Result<Strong<dyn IVirtualMachine::IVirtualMachine>> {
         let mut is_protected = false;
         let ret = self.create_vm_internal(
             config,
@@ -229,6 +232,7 @@
             console_in_fd,
             log_fd,
             &mut is_protected,
+            dump_dt_fd,
         );
         write_vm_creation_stats(config, is_protected, &ret);
         ret
@@ -485,7 +489,8 @@
         console_in_fd: Option<&ParcelFileDescriptor>,
         log_fd: Option<&ParcelFileDescriptor>,
         is_protected: &mut bool,
-    ) -> binder::Result<Strong<dyn IVirtualMachine>> {
+        dump_dt_fd: Option<&ParcelFileDescriptor>,
+    ) -> binder::Result<Strong<dyn IVirtualMachine::IVirtualMachine>> {
         let requester_uid = get_calling_uid();
         let requester_debug_pid = get_calling_pid();
 
@@ -527,6 +532,7 @@
             clone_or_prepare_logger_fd(console_out_fd, format!("Console({})", cid))?;
         let console_in_fd = console_in_fd.map(clone_file).transpose()?;
         let log_fd = clone_or_prepare_logger_fd(log_fd, format!("Log({})", cid))?;
+        let dump_dt_fd = dump_dt_fd.map(clone_file).transpose()?;
 
         // Counter to generate unique IDs for temporary image files.
         let mut next_temporary_image_id = 0;
@@ -744,6 +750,7 @@
             audio_config,
             no_balloon: config.noBalloon,
             usb_config,
+            dump_dt_fd,
         };
         let instance = Arc::new(
             VmInstance::new(
@@ -1326,14 +1333,14 @@
 }
 
 impl VirtualMachine {
-    fn create(instance: Arc<VmInstance>) -> Strong<dyn IVirtualMachine> {
+    fn create(instance: Arc<VmInstance>) -> Strong<dyn IVirtualMachine::IVirtualMachine> {
         BnVirtualMachine::new_binder(VirtualMachine { instance }, BinderFeatures::default())
     }
 }
 
 impl Interface for VirtualMachine {}
 
-impl IVirtualMachine for VirtualMachine {
+impl IVirtualMachine::IVirtualMachine for VirtualMachine {
     fn getCid(&self) -> binder::Result<i32> {
         // Don't check permission. The owner of the VM might have passed this binder object to
         // others.
@@ -1394,19 +1401,48 @@
 
     fn connectVsock(&self, port: i32) -> binder::Result<ParcelFileDescriptor> {
         if !matches!(&*self.instance.vm_state.lock().unwrap(), VmState::Running { .. }) {
-            return Err(anyhow!("VM is not running")).or_service_specific_exception(-1);
+            return Err(Status::new_service_specific_error_str(
+                IVirtualMachine::ERROR_UNEXPECTED,
+                Some("Virtual Machine is not running"),
+            ));
         }
         let port = port as u32;
-        if port < 1024 {
-            return Err(anyhow!("Can't connect to privileged port {port}"))
-                .or_service_specific_exception(-1);
+        if port < VSOCK_PRIV_PORT_MAX {
+            return Err(Status::new_service_specific_error_str(
+                IVirtualMachine::ERROR_UNEXPECTED,
+                Some("Can't connect to privileged port {port}"),
+            ));
         }
         let stream = VsockStream::connect_with_cid_port(self.instance.cid, port)
             .context("Failed to connect")
-            .or_service_specific_exception(-1)?;
+            .or_service_specific_exception(IVirtualMachine::ERROR_UNEXPECTED)?;
         Ok(vsock_stream_to_pfd(stream))
     }
 
+    fn createAccessorBinder(&self, name: &str, port: i32) -> binder::Result<SpIBinder> {
+        if !matches!(&*self.instance.vm_state.lock().unwrap(), VmState::Running { .. }) {
+            return Err(Status::new_service_specific_error_str(
+                IVirtualMachine::ERROR_UNEXPECTED,
+                Some("Virtual Machine is not running"),
+            ));
+        }
+        let port = port as u32;
+        if port < VSOCK_PRIV_PORT_MAX {
+            return Err(Status::new_service_specific_error_str(
+                IVirtualMachine::ERROR_UNEXPECTED,
+                Some("Can't connect to privileged port {port}"),
+            ));
+        }
+        let cid = self.instance.cid;
+        let get_connection_info =
+            move |_instance: &str| Some(ConnectionInfo::Vsock(VsockAddr::new(cid, port)));
+        let accessor = Accessor::new(name, get_connection_info);
+        accessor
+            .as_binder()
+            .context("The newly created Accessor should always have a binder")
+            .or_service_specific_exception(IVirtualMachine::ERROR_UNEXPECTED)
+    }
+
     fn setHostConsoleName(&self, ptsname: &str) -> binder::Result<()> {
         self.instance.vm_context.global_context.setHostConsoleName(ptsname)
     }
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index b2be736..25271f8 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -135,6 +135,7 @@
     pub audio_config: Option<AudioConfig>,
     pub no_balloon: bool,
     pub usb_config: UsbConfig,
+    pub dump_dt_fd: Option<File>,
 }
 
 #[derive(Debug)]
@@ -985,6 +986,11 @@
     // Keep track of what file descriptors should be mapped to the crosvm process.
     let mut preserved_fds = config.indirect_files.into_iter().map(|f| f.into()).collect();
 
+    if let Some(dump_dt_fd) = config.dump_dt_fd {
+        let dump_dt_fd = add_preserved_fd(&mut preserved_fds, dump_dt_fd);
+        command.arg("--dump-device-tree-blob").arg(dump_dt_fd);
+    }
+
     // Setup the serial devices.
     // 1. uart device: used as the output device by bootloaders and as early console by linux
     // 2. uart device: used to report the reason for the VM failing.
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
index afa25e2..e52222a 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
@@ -19,6 +19,13 @@
 import android.system.virtualizationservice.VirtualMachineState;
 
 interface IVirtualMachine {
+    /**
+     * Encountered an unexpected error. This is an implementation detail and the client
+     * can do nothing about it.
+     * This is used as a Service Specific Exception.
+     */
+    const int ERROR_UNEXPECTED = -1;
+
     /** Get the CID allocated to the VM. */
     int getCid();
 
@@ -48,6 +55,19 @@
     /** Open a vsock connection to the CID of the VM on the given port. */
     ParcelFileDescriptor connectVsock(int port);
 
+    /**
+     * Create an Accessor in libbinder that will open a vsock connection
+     * to the CID of the VM on the given port.
+     *
+     * \param instance name of the service that the accessor is responsible for.
+     *        This is the same instance that we expect clients to use when trying
+     *        to get the service with the ServiceManager APIs.
+     *
+     * \return IBinder of the IAccessor on success, or throws a service specific exception
+     *         on error. See the ERROR_* values above.
+     */
+    IBinder createAccessorBinder(String instance, int port);
+
     /** Set the name of the peer end (ptsname) of the host console. */
     void setHostConsoleName(in @utf8InCpp String pathname);
 
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
index 234d8d0..0c3f6b7 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
@@ -35,11 +35,14 @@
      * `consoleInFd` is provided then console input to the VM will be read from it. If `osLogFd` is
      * provided then the OS-level logs will be sent to it. `osLogFd` is supported only when the OS
      * running in the VM has the logging system. In case of Microdroid, the logging system is logd.
+     * `dumpDtFd` is the file where to dump the VM's device tree. It is only used in
+     * debugging/testing.
      */
     IVirtualMachine createVm(in VirtualMachineConfig config,
             in @nullable ParcelFileDescriptor consoleOutFd,
             in @nullable ParcelFileDescriptor consoleInFd,
-            in @nullable ParcelFileDescriptor osLogFd);
+            in @nullable ParcelFileDescriptor osLogFd,
+            in @nullable ParcelFileDescriptor dumpDtFd);
 
     /**
      * Allocate an instance_id to the (newly created) VM.
diff --git a/android/vm/src/main.rs b/android/vm/src/main.rs
index 609bbdf..81ca8fa 100644
--- a/android/vm/src/main.rs
+++ b/android/vm/src/main.rs
@@ -114,6 +114,10 @@
     #[cfg(debuggable_vms_improvements)]
     #[arg(long)]
     enable_earlycon: bool,
+
+    /// Path to file to dump VM device tree.
+    #[arg(long)]
+    dump_device_tree: Option<PathBuf>,
 }
 
 impl DebugConfig {
diff --git a/android/vm/src/run.rs b/android/vm/src/run.rs
index 823546f..0e1f4cc 100644
--- a/android/vm/src/run.rs
+++ b/android/vm/src/run.rs
@@ -203,6 +203,7 @@
         config.debug.console.as_ref().map(|p| p.as_ref()),
         config.debug.console_in.as_ref().map(|p| p.as_ref()),
         config.debug.log.as_ref().map(|p| p.as_ref()),
+        config.debug.dump_device_tree.as_ref().map(|p| p.as_ref()),
     )
 }
 
@@ -284,6 +285,7 @@
         config.debug.console.as_ref().map(|p| p.as_ref()),
         config.debug.console_in.as_ref().map(|p| p.as_ref()),
         config.debug.log.as_ref().map(|p| p.as_ref()),
+        config.debug.dump_device_tree.as_ref().map(|p| p.as_ref()),
     )
 }
 
@@ -306,6 +308,7 @@
     console_out_path: Option<&Path>,
     console_in_path: Option<&Path>,
     log_path: Option<&Path>,
+    dump_device_tree: Option<&Path>,
 ) -> Result<(), Error> {
     let console_out = if let Some(console_out_path) = console_out_path {
         Some(File::create(console_out_path).with_context(|| {
@@ -330,9 +333,17 @@
     } else {
         Some(duplicate_fd(io::stdout())?)
     };
+    let dump_dt = if let Some(dump_device_tree) = dump_device_tree {
+        Some(File::create(dump_device_tree).with_context(|| {
+            format!("Failed to open file to dump device tree: {:?}", dump_device_tree)
+        })?)
+    } else {
+        None
+    };
     let callback = Box::new(Callback {});
-    let vm = VmInstance::create(service, config, console_out, console_in, log, Some(callback))
-        .context("Failed to create VM")?;
+    let vm =
+        VmInstance::create(service, config, console_out, console_in, log, dump_dt, Some(callback))
+            .context("Failed to create VM")?;
     vm.start().context("Failed to start VM")?;
 
     let debug_level = get_debug_level(config).unwrap_or(DebugLevel::NONE);
diff --git a/android/vm_demo_native/main.cpp b/android/vm_demo_native/main.cpp
index bc42036..d7ff02e 100644
--- a/android/vm_demo_native/main.cpp
+++ b/android/vm_demo_native/main.cpp
@@ -226,8 +226,10 @@
     ScopedFileDescriptor console_out_fd(fcntl(fileno(stdout), F_DUPFD_CLOEXEC));
     ScopedFileDescriptor console_in_fd(fcntl(fileno(stdin), F_DUPFD_CLOEXEC));
     ScopedFileDescriptor log_fd(fcntl(fileno(stdout), F_DUPFD_CLOEXEC));
+    ScopedFileDescriptor dump_dt_fd(-1);
 
-    ScopedAStatus ret = service.createVm(config, console_out_fd, console_in_fd, log_fd, &vm);
+    ScopedAStatus ret =
+            service.createVm(config, console_out_fd, console_in_fd, log_fd, dump_dt_fd, &vm);
     if (!ret.isOk()) {
         return Error() << "Failed to create VM";
     }
diff --git a/guest/rialto/tests/test.rs b/guest/rialto/tests/test.rs
index 582b69e..7ec5647 100644
--- a/guest/rialto/tests/test.rs
+++ b/guest/rialto/tests/test.rs
@@ -335,6 +335,14 @@
     let virtmgr = vmclient::VirtualizationService::new().context("Failed to spawn VirtMgr")?;
     let service = virtmgr.connect().context("Failed to connect to VirtMgr")?;
     info!("Connected to VirtMgr for service VM");
-    VmInstance::create(service.as_ref(), &config, console, /* consoleIn */ None, log, None)
-        .context("Failed to create VM")
+    VmInstance::create(
+        service.as_ref(),
+        &config,
+        console,
+        /* consoleIn */ None,
+        log,
+        /* dump_dt */ None,
+        None,
+    )
+    .context("Failed to create VM")
 }
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
index 3b16a8a..b278610 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
@@ -1578,7 +1578,8 @@
                                 : createVirtualMachineConfigForAppFrom(vmConfig, service);
 
                 mVirtualMachine =
-                        service.createVm(vmConfigParcel, consoleOutFd, consoleInFd, mLogWriter);
+                        service.createVm(
+                                vmConfigParcel, consoleOutFd, consoleInFd, mLogWriter, null);
                 mVirtualMachine.registerCallback(new CallbackTranslator(service));
                 if (mMemoryManagementCallbacks != null) {
                     mContext.registerComponentCallbacks(mMemoryManagementCallbacks);
diff --git a/libs/libcompos_common/compos_client.rs b/libs/libcompos_common/compos_client.rs
index 107f8d0..316eaa9 100644
--- a/libs/libcompos_common/compos_client.rs
+++ b/libs/libcompos_common/compos_client.rs
@@ -152,6 +152,7 @@
             console_fd,
             /* console_in_fd */ None,
             log_fd,
+            /* dump_dt */ None,
             Some(callback),
         )
         .context("Failed to create VM")?;
diff --git a/libs/libservice_vm_manager/src/lib.rs b/libs/libservice_vm_manager/src/lib.rs
index d7b4dd6..0f322bb 100644
--- a/libs/libservice_vm_manager/src/lib.rs
+++ b/libs/libservice_vm_manager/src/lib.rs
@@ -244,8 +244,9 @@
     let console_out = Some(android_log_fd()?);
     let console_in = None;
     let log = Some(android_log_fd()?);
+    let dump_dt = None;
     let callback = None;
-    VmInstance::create(service.as_ref(), &config, console_out, console_in, log, callback)
+    VmInstance::create(service.as_ref(), &config, console_out, console_in, log, dump_dt, callback)
         .context("Failed to create service VM")
 }
 
diff --git a/libs/libvmclient/src/lib.rs b/libs/libvmclient/src/lib.rs
index ce7d5a5..13630c0 100644
--- a/libs/libvmclient/src/lib.rs
+++ b/libs/libvmclient/src/lib.rs
@@ -208,14 +208,21 @@
         console_out: Option<File>,
         console_in: Option<File>,
         log: Option<File>,
+        dump_dt: Option<File>,
         callback: Option<Box<dyn VmCallback + Send + Sync>>,
     ) -> BinderResult<Self> {
         let console_out = console_out.map(ParcelFileDescriptor::new);
         let console_in = console_in.map(ParcelFileDescriptor::new);
         let log = log.map(ParcelFileDescriptor::new);
+        let dump_dt = dump_dt.map(ParcelFileDescriptor::new);
 
-        let vm =
-            service.createVm(config, console_out.as_ref(), console_in.as_ref(), log.as_ref())?;
+        let vm = service.createVm(
+            config,
+            console_out.as_ref(),
+            console_in.as_ref(),
+            log.as_ref(),
+            dump_dt.as_ref(),
+        )?;
 
         let cid = vm.getCid()?;
 
diff --git a/microfuchsia/microfuchsiad/src/instance_starter.rs b/microfuchsia/microfuchsiad/src/instance_starter.rs
index 15fcc06..61a024f 100644
--- a/microfuchsia/microfuchsiad/src/instance_starter.rs
+++ b/microfuchsia/microfuchsiad/src/instance_starter.rs
@@ -90,6 +90,7 @@
             console_out,
             console_in,
             /* log= */ None,
+            /* dump_dt= */ None,
             None,
         )
         .context("Failed to create VM")?;
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index d0838a6..1f86d6b 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -26,6 +26,7 @@
         ":microdroid_general_sepolicy.conf",
         ":test.com.android.virt.pem",
         ":test2.com.android.virt.pem",
+        "java/**/goldens/dt_dump_*",
     ],
     data_native_bins: [
         "sepolicy-analyze",
@@ -38,6 +39,7 @@
         "lz4",
         "sign_virt_apex",
         "simg2img",
+        "dtc",
     ],
     // java_test_host doesn't have data_native_libs but jni_libs can be used to put
     // native modules under ./lib directory.
@@ -48,6 +50,7 @@
         "libcrypto_utils",
         "libcrypto",
         "libext4_utils",
+        "libfdt",
         "liblog",
         "liblp",
         "libsparse",
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 2d55d66..b8b4312 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -1352,6 +1352,134 @@
         }
     }
 
+    @Test
+    @Parameters(method = "gkiVersions")
+    @TestCaseName("{method}_gki_{0}")
+    public void microdroidDeviceTreeCompat(String gki) throws Exception {
+        assumeArm64Supported();
+        final String configPath = "assets/vm_config.json";
+        // Preconditions
+        assumeKernelSupported(gki);
+        int mem_size = 256;
+        assertTrue("Memory size too small", mem_size >= minMemorySize());
+
+        // Start the VM with the dump DT option.
+        mMicrodroidDevice =
+                MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+                        .debugLevel("full")
+                        .memoryMib(mem_size)
+                        .cpuTopology("one_cpu")
+                        .protectedVm(false)
+                        .gki(sGkiVersions.get(gki))
+                        .name("test_device_tree")
+                        .dumpDt("/data/local/tmp/dump_dt.dtb")
+                        .build(getAndroidDevice());
+        assertThat(mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT)).isTrue();
+
+        File goldenDt = findTestFile("dt_dump_golden.dts");
+        testGoldenDeviceTree(goldenDt.getAbsolutePath());
+    }
+
+    @Test
+    @Parameters(method = "gkiVersions")
+    @TestCaseName("{method}_gki_{0}")
+    public void microdroidProtectedDeviceTreeCompat(String gki) throws Exception {
+        assumeArm64Supported();
+        final String configPath = "assets/vm_config.json";
+        // Preconditions
+        assumeKernelSupported(gki);
+        assumeVmTypeSupported(true);
+        int mem_size = 256;
+        assertTrue("Memory size too small", mem_size >= minMemorySize());
+
+        // Start the VM with the dump DT option.
+        mMicrodroidDevice =
+                MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+                        .debugLevel("full")
+                        .memoryMib(mem_size)
+                        .cpuTopology("one_cpu")
+                        .protectedVm(true)
+                        .gki(sGkiVersions.get(gki))
+                        .name("test_device_tree")
+                        .dumpDt("/data/local/tmp/dump_dt.dtb")
+                        .build(getAndroidDevice());
+        assertThat(mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT)).isTrue();
+
+        File goldenDt = findTestFile("dt_dump_protected_golden.dts");
+        testGoldenDeviceTree(goldenDt.getAbsolutePath());
+    }
+
+    private void testGoldenDeviceTree(String goldenDt) throws Exception {
+        // Pull the device tree to host.
+        TestDevice device = getAndroidDevice();
+        boolean disableRoot = !device.isAdbRoot();
+        device.enableAdbRoot();
+        assumeTrue("adb root is not enabled", device.isAdbRoot());
+
+        // Pull DT from device
+        File dtb_from_device = device.pullFile("/data/local/tmp/dump_dt.dtb");
+        if (disableRoot) {
+            device.disableAdbRoot();
+        }
+
+        File dtc = findTestFile("dtc");
+
+        // Create temp file for Device tree conversion
+        File dt_dump_dts = File.createTempFile("dt_dump", "dts");
+        dt_dump_dts.delete();
+        String dt_dump_dts_path = dt_dump_dts.getAbsolutePath();
+        // Convert DT to text format.
+        CommandResult dtb_to_dts =
+                RunUtil.getDefault()
+                        .runTimedCmd(
+                                3000,
+                                dtc.getAbsolutePath(),
+                                "-I",
+                                "dtb",
+                                "-O",
+                                "dts",
+                                "-qqq",
+                                "-f",
+                                "-s",
+                                "-o",
+                                dt_dump_dts_path,
+                                dtb_from_device.getAbsolutePath());
+        assertTrue(
+                "result convert stderr: " + dtb_to_dts.getStderr(),
+                dtb_to_dts.getStderr().trim().isEmpty());
+        assertTrue(
+                "result convert stdout: " + dtb_to_dts.getStdout(),
+                dtb_to_dts.getStdout().trim().isEmpty());
+
+        // Diff device's DT with the golden DT.
+        CommandResult result_compare =
+                RunUtil.getDefault()
+                        .runTimedCmd(
+                                3000,
+                                "diff",
+                                "-u",
+                                "-w",
+                                "-I",
+                                "kaslr-seed",
+                                "-I",
+                                "instance-id",
+                                "-I",
+                                "rng-seed",
+                                "-I",
+                                "linux,initrd-end",
+                                "-I",
+                                "secretkeeper_public_key",
+                                dt_dump_dts_path,
+                                goldenDt);
+
+        assertTrue(
+                "result compare stderr: " + result_compare.getStderr(),
+                result_compare.getStderr().trim().isEmpty());
+        assertTrue(
+                "result compare stdout: " + result_compare.getStdout(),
+                result_compare.getStdout().trim().isEmpty());
+    }
+
     @Before
     public void setUp() throws Exception {
         assumeDeviceIsCapable(getDevice());
@@ -1428,4 +1556,11 @@
                 "Microdroid is not supported for specific VM protection type",
                 getAndroidDevice().supportsMicrodroid(protectedVm));
     }
+
+    private void assumeArm64Supported() throws Exception {
+        CommandRunner android = new CommandRunner(getDevice());
+        String abi = android.run("getprop", "ro.product.cpu.abi");
+        assertThat(abi).isNotEmpty();
+        assumeTrue("Skipping test as the architecture is not supported", abi.startsWith("arm64"));
+    }
 }
diff --git a/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_golden.dts b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_golden.dts
new file mode 100644
index 0000000..795c50f
--- /dev/null
+++ b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_golden.dts
@@ -0,0 +1,145 @@
+/dts-v1/;
+
+/ {
+        #address-cells = <0x02>;
+        #size-cells = <0x02>;
+        compatible = "linux,dummy-virt";
+        interrupt-parent = <0x01>;
+        name = "reference";
+
+        U6_16550A@2e8 {
+                clock-frequency = <0x1c2000>;
+                compatible = "ns16550a";
+                interrupts = <0x00 0x02 0x01>;
+                reg = <0x00 0x2e8 0x00 0x08>;
+        };
+
+        U6_16550A@2f8 {
+                clock-frequency = <0x1c2000>;
+                compatible = "ns16550a";
+                interrupts = <0x00 0x02 0x01>;
+                reg = <0x00 0x2f8 0x00 0x08>;
+        };
+
+        U6_16550A@3e8 {
+                clock-frequency = <0x1c2000>;
+                compatible = "ns16550a";
+                interrupts = <0x00 0x00 0x01>;
+                reg = <0x00 0x3e8 0x00 0x08>;
+        };
+
+        U6_16550A@3f8 {
+                clock-frequency = <0x1c2000>;
+                compatible = "ns16550a";
+                interrupts = <0x00 0x00 0x01>;
+                reg = <0x00 0x3f8 0x00 0x08>;
+        };
+
+        __symbols__ {
+                intc = "/intc";
+        };
+
+        avf {
+                secretkeeper_public_key = [];
+
+                untrusted {
+                        defer-rollback-protection;
+                        instance-id = <0xf145d4f8 0x15f03952 0x5af249aa 0xfead94d8 0xb9f05746 0xd9163f48 0x7251b67b 0xe117409e 0x2b14dfa5 0xcaa8caf7 0x14176d2d 0xf88cc94b 0xeed4a59d 0x9a2d8fe5 0x5ac590f1 0xbb6c96f5>;
+                };
+        };
+
+        chosen {
+                bootargs = "panic=-1 crashkernel=17M";
+                kaslr-seed = <>;
+                linux,initrd-end = <0x81200360>;
+                linux,initrd-start = <0x81000000>;
+                linux,pci-probe-only = <0x01>;
+                rng-seed = <>;
+                stdout-path = "/U6_16550A@3f8";
+        };
+
+        config {
+                kernel-address = <0x80000000>;
+                kernel-size = <0xc91000>;
+        };
+
+        cpufreq {
+                compatible = "virtual,kvm-cpufreq";
+        };
+
+        cpus {
+                #address-cells = <0x01>;
+                #size-cells = <0x00>;
+
+                cpu@0 {
+                        compatible = "arm,armv8";
+                        device_type = "cpu";
+                        phandle = <0x100>;
+                        reg = <0x00>;
+                };
+        };
+
+        intc {
+                #address-cells = <0x02>;
+                #interrupt-cells = <0x03>;
+                #size-cells = <0x02>;
+                compatible = "arm,gic-v3";
+                interrupt-controller;
+                phandle = <0x01>;
+                reg = <0x00 0x3fff0000 0x00 0x10000 0x00 0x3ffd0000 0x00 0x20000>;
+        };
+
+        memory {
+                device_type = "memory";
+                reg = <0x00 0x80000000 0x00 0x10000000>;
+        };
+
+        pci {
+                #address-cells = <0x03>;
+                #interrupt-cells = <0x01>;
+                #size-cells = <0x02>;
+                bus-range = <0x00 0x00>;
+                compatible = "pci-host-cam-generic";
+                device_type = "pci";
+                dma-coherent;
+                interrupt-map = <0x800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x1000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x05 0x04 0x1800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x06 0x04 0x2000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x07 0x04 0x2800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x08 0x04 0x3000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x09 0x04 0x3800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x0a 0x04 0x4000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x0b 0x04 0x4800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x0c 0x04>;
+                interrupt-map-mask = <0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07>;
+                ranges = <0x3000000 0x00 0x2000000 0x00 0x2000000 0x00 0x2000000 0x43000000 0x00 0x90800000 0x00 0x90800000 0xff 0x6f800000>;
+                reg = <0x00 0x10000 0x00 0x1000000>;
+        };
+
+        pclk@3M {
+                #clock-cells = <0x00>;
+                clock-frequency = <0x2fefd8>;
+                compatible = "fixed-clock";
+                phandle = <0x18>;
+        };
+
+        psci {
+                compatible = "arm,psci-1.0\0arm,psci-0.2";
+                method = "hvc";
+        };
+
+        rtc@2000 {
+                arm,primecell-periphid = <0x41030>;
+                clock-names = "apb_pclk";
+                clocks = <0x18>;
+                compatible = "arm,primecell";
+                interrupts = <0x00 0x01 0x04>;
+                reg = <0x00 0x2000 0x00 0x1000>;
+        };
+
+        timer {
+                always-on;
+                compatible = "arm,armv8-timer";
+                interrupts = <0x01 0x0d 0x108 0x01 0x0e 0x108 0x01 0x0b 0x108 0x01 0x0a 0x108>;
+        };
+
+        vmwdt@3000 {
+                clock-frequency = <0x02>;
+                compatible = "qemu,vcpu-stall-detector";
+                interrupts = <0x01 0x0f 0x101>;
+                reg = <0x00 0x3000 0x00 0x1000>;
+                timeout-sec = <0x0a>;
+        };
+};
diff --git a/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_protected_golden.dts b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_protected_golden.dts
new file mode 100644
index 0000000..5761c15
--- /dev/null
+++ b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_protected_golden.dts
@@ -0,0 +1,159 @@
+/dts-v1/;
+
+/ {
+        #address-cells = <0x02>;
+        #size-cells = <0x02>;
+        compatible = "linux,dummy-virt";
+        interrupt-parent = <0x01>;
+        name = "reference";
+
+        U6_16550A@2e8 {
+                clock-frequency = <0x1c2000>;
+                compatible = "ns16550a";
+                interrupts = <0x00 0x02 0x01>;
+                reg = <0x00 0x2e8 0x00 0x08>;
+        };
+
+        U6_16550A@2f8 {
+                clock-frequency = <0x1c2000>;
+                compatible = "ns16550a";
+                interrupts = <0x00 0x02 0x01>;
+                reg = <0x00 0x2f8 0x00 0x08>;
+        };
+
+        U6_16550A@3e8 {
+                clock-frequency = <0x1c2000>;
+                compatible = "ns16550a";
+                interrupts = <0x00 0x00 0x01>;
+                reg = <0x00 0x3e8 0x00 0x08>;
+        };
+
+        U6_16550A@3f8 {
+                clock-frequency = <0x1c2000>;
+                compatible = "ns16550a";
+                interrupts = <0x00 0x00 0x01>;
+                reg = <0x00 0x3f8 0x00 0x08>;
+        };
+
+        __symbols__ {
+                intc = "/intc";
+        };
+
+        avf {
+                secretkeeper_public_key = [];
+
+                untrusted {
+                        defer-rollback-protection;
+                        instance-id = <0x4d482941 0x27228238 0x11d7b28 0xaeed3076 0x88eb3fcb 0x2b9de301 0x57ff8977 0xaf8c24b6 0x55466af4 0x23beed37 0x2f976083 0xe630eb28 0x1edbc491 0xa8300897 0xeb3e9f76 0x21ea9284>;
+                };
+        };
+
+        chosen {
+                bootargs = "panic=-1 crashkernel=31M";
+                kaslr-seed = <>;
+                linux,initrd-end = <0x81202104>;
+                linux,initrd-start = <0x81000000>;
+                linux,pci-probe-only = <0x01>;
+                rng-seed = <>;
+                stdout-path = "/U6_16550A@3f8";
+        };
+
+        config {
+                kernel-address = <0x80000000>;
+                kernel-size = <0xc91000>;
+        };
+
+        cpufreq {
+                compatible = "virtual,kvm-cpufreq";
+        };
+
+        cpus {
+                #address-cells = <0x01>;
+                #size-cells = <0x00>;
+
+                cpu@0 {
+                        compatible = "arm,armv8";
+                        device_type = "cpu";
+                        phandle = <0x100>;
+                        reg = <0x00>;
+                };
+        };
+
+        intc {
+                #address-cells = <0x02>;
+                #interrupt-cells = <0x03>;
+                #size-cells = <0x02>;
+                compatible = "arm,gic-v3";
+                interrupt-controller;
+                phandle = <0x01>;
+                reg = <0x00 0x3fff0000 0x00 0x10000 0x00 0x3ffd0000 0x00 0x20000>;
+        };
+
+        memory {
+                device_type = "memory";
+                reg = <0x00 0x80000000 0x00 0x10e00000>;
+        };
+
+        pci {
+                #address-cells = <0x03>;
+                #interrupt-cells = <0x01>;
+                #size-cells = <0x02>;
+                bus-range = <0x00 0x00>;
+                compatible = "pci-host-cam-generic";
+                device_type = "pci";
+                dma-coherent;
+                interrupt-map = <0x800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x1000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x05 0x04 0x1800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x06 0x04 0x2000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x07 0x04 0x2800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x08 0x04 0x3000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x09 0x04 0x3800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x0a 0x04 0x4000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x0b 0x04>;
+                interrupt-map-mask = <0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07>;
+                memory-region = <0x02>;
+                ranges = <0x3000000 0x00 0x2000000 0x00 0x2000000 0x00 0x2000000 0x43000000 0x00 0x91600000 0x00 0x91600000 0xff 0x6ea00000>;
+                reg = <0x00 0x10000 0x00 0x1000000>;
+        };
+
+        pclk@3M {
+                #clock-cells = <0x00>;
+                clock-frequency = <0x2fefd8>;
+                compatible = "fixed-clock";
+                phandle = <0x18>;
+        };
+
+        psci {
+                compatible = "arm,psci-1.0\0arm,psci-0.2";
+                method = "hvc";
+        };
+
+        reserved-memory {
+                #address-cells = <0x02>;
+                #size-cells = <0x02>;
+                ranges;
+
+                restricted_dma_reserved {
+                        alignment = <0x00 0x1000>;
+                        compatible = "restricted-dma-pool";
+                        phandle = <0x02>;
+                        size = <0x00 0xe00000>;
+                };
+        };
+
+        rtc@2000 {
+                arm,primecell-periphid = <0x41030>;
+                clock-names = "apb_pclk";
+                clocks = <0x18>;
+                compatible = "arm,primecell";
+                interrupts = <0x00 0x01 0x04>;
+                reg = <0x00 0x2000 0x00 0x1000>;
+        };
+
+        timer {
+                always-on;
+                compatible = "arm,armv8-timer";
+                interrupts = <0x01 0x0d 0x108 0x01 0x0e 0x108 0x01 0x0b 0x108 0x01 0x0a 0x108>;
+        };
+
+        vmwdt@3000 {
+                clock-frequency = <0x02>;
+                compatible = "qemu,vcpu-stall-detector";
+                interrupts = <0x01 0x0f 0x101>;
+                reg = <0x00 0x3000 0x00 0x1000>;
+                timeout-sec = <0x0a>;
+        };
+};
diff --git a/tests/vm_accessor/README.md b/tests/vm_accessor/README.md
index c85cf3c..8b0eb2a 100644
--- a/tests/vm_accessor/README.md
+++ b/tests/vm_accessor/README.md
@@ -1,15 +1,16 @@
 # Demo for serving a service in a VM
 
 You can implement a service in a VM, and let client in the Android can use it
-as if it's in the Android. To do so, implement IAccessor.
+as if it's in the Android. To do so, use libbinder's IAccessor.
 
-IAccessor allows AIDL service in a VM can be accessed via servicemanager.
-To do so, VM owners should also provide IAccessor implementation. servicemanager
-will connect to the IAccessor and get the binder of the service in a VM with it.
+IAccessor allows AIDL service in a VM to be accessed via servicemanager.
+To do so, VM owners should also provide IAccessor through libbinder's service
+manager APIs. servicemanager will connect to the IAccessor and get the binder
+of the service in a VM with it.
 
 com.android.virt.accessor_demo apex contains the minimum setup for IAccessor as
 follows:
-  - accessor_demo: Sample implementation of IAccessor, which is expected to
+  - accessor_demo: Sample implementation using IAccessor, which is expected to
       launch VM and returns the Vsock connection of service in the VM.
   - AccessorVmApp: Sample app that conatins VM payload. Provides the actual
       implementation of service in a VM.
diff --git a/tests/vm_accessor/accessor/Android.bp b/tests/vm_accessor/accessor/Android.bp
index 7c0ee6d..8055f91 100644
--- a/tests/vm_accessor/accessor/Android.bp
+++ b/tests/vm_accessor/accessor/Android.bp
@@ -14,7 +14,6 @@
     ],
     rustlibs: [
         "android.system.virtualizationservice-rust",
-        "android.os.accessor-rust",
         "libanyhow",
         "libandroid_logger",
         "libbinder_rs",
diff --git a/tests/vm_accessor/accessor/src/accessor.rs b/tests/vm_accessor/accessor/src/accessor.rs
deleted file mode 100644
index 966bffb..0000000
--- a/tests/vm_accessor/accessor/src/accessor.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2024, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! IAcessor implementation.
-//! TODO: Keep this in proper places, so other pVMs can use this.
-//! TODO: Allows to customize VMs for launching. (e.g. port, ...)
-
-use android_os_accessor::aidl::android::os::IAccessor::IAccessor;
-use binder::{self, Interface, ParcelFileDescriptor};
-use log::info;
-use std::time::Duration;
-use vmclient::VmInstance;
-
-// Note: Do not use LazyServiceGuard here, to make this service and VM are quit
-//       when nobody references it.
-// TODO(b/353492849): Do not use IAccessor directly.
-#[derive(Debug)]
-pub struct Accessor {
-    // Note: we can't simply keep reference by specifying lifetime to Accessor,
-    //       because 'trait Interface' requires 'static.
-    vm: VmInstance,
-    port: i32,
-    instance: String,
-}
-
-impl Accessor {
-    pub fn new(vm: VmInstance, port: i32, instance: &str) -> Self {
-        Self { vm, port, instance: instance.into() }
-    }
-}
-
-impl Interface for Accessor {}
-
-impl IAccessor for Accessor {
-    fn addConnection(&self) -> binder::Result<ParcelFileDescriptor> {
-        self.vm.wait_until_ready(Duration::from_secs(20)).unwrap();
-
-        info!("VM is ready. Connecting to service via port {}", self.port);
-
-        self.vm.vm.connectVsock(self.port)
-    }
-    fn getInstanceName(&self) -> binder::Result<String> {
-        Ok(self.instance.clone())
-    }
-}
diff --git a/tests/vm_accessor/accessor/src/main.rs b/tests/vm_accessor/accessor/src/main.rs
index 49f5794..db53d8e 100644
--- a/tests/vm_accessor/accessor/src/main.rs
+++ b/tests/vm_accessor/accessor/src/main.rs
@@ -14,16 +14,14 @@
 
 //! Android VM control tool.
 
-mod accessor;
 mod run;
 
-use accessor::Accessor;
-use android_os_accessor::aidl::android::os::IAccessor::BnAccessor;
 use anyhow::Error;
 use anyhow::{anyhow, bail};
-use binder::{BinderFeatures, ProcessState};
+use binder::ProcessState;
 use log::info;
 use run::run_vm;
+use std::time::Duration;
 
 // Private contract between IAccessor impl and VM service.
 const PORT: i32 = 5678;
@@ -40,11 +38,13 @@
     );
 
     let vm = run_vm()?;
+    vm.wait_until_ready(Duration::from_secs(20)).unwrap();
+    let accessor = vm.vm.createAccessorBinder(SERVICE_NAME, PORT).unwrap();
+
+    let accessor_delegator = binder::delegate_accessor(SERVICE_NAME, accessor).unwrap();
 
     // If you want to serve multiple services in a VM, then register Accessor impls multiple times.
-    let accessor = Accessor::new(vm, PORT, SERVICE_NAME);
-    let accessor_binder = BnAccessor::new_binder(accessor, BinderFeatures::default());
-    binder::register_lazy_service(SERVICE_NAME, accessor_binder.as_binder()).map_err(|e| {
+    binder::register_lazy_service(SERVICE_NAME, accessor_delegator).map_err(|e| {
         anyhow!("Failed to register lazy service, service={SERVICE_NAME}, err={e:?}",)
     })?;
     info!("service {SERVICE_NAME} is registered as lazy service");
diff --git a/tests/vm_accessor/accessor/src/run.rs b/tests/vm_accessor/accessor/src/run.rs
index 932baab..6dcc507 100644
--- a/tests/vm_accessor/accessor/src/run.rs
+++ b/tests/vm_accessor/accessor/src/run.rs
@@ -128,6 +128,7 @@
         Some(android_log_fd()?), /* console_out */
         None,                    /* console_in */
         Some(android_log_fd()?), /* log */
+        None,                    /* dump_dt */
         Some(Box::new(Callback {})),
     )
     .context("Failed to create VM")?;
diff --git a/tests/vmbase_example/src/main.rs b/tests/vmbase_example/src/main.rs
index e0563b7..34a2b0b 100644
--- a/tests/vmbase_example/src/main.rs
+++ b/tests/vmbase_example/src/main.rs
@@ -119,6 +119,7 @@
         Some(console),
         /* consoleIn */ None,
         Some(log_writer),
+        /* dump_dt */ None,
         None,
     )
     .context("Failed to create VM")?;