Merge "Use new method/field to get StagedApexInfo[]" 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..0e2a7d1 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -15,12 +15,10 @@
  */
 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;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -30,9 +28,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 +47,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);
@@ -88,10 +94,7 @@
     public void onIpAddrAvailable(String ipAddr) {
         mVmIpAddr = ipAddr;
         ((TextView) findViewById(R.id.ip_addr_textview)).setText(mVmIpAddr);
-
-        // TODO(b/359523803): Use AVF API to be notified when shell is ready instead of using dealy
-        new Handler(Looper.getMainLooper())
-                .postDelayed(() -> gotoURL("http://" + mVmIpAddr + ":7681"), 2000);
+        gotoURL("http://" + mVmIpAddr + ":7681");
     }
 
     @Override
@@ -101,7 +104,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 +114,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 4b203d6..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;
@@ -222,7 +224,7 @@
         console_in_fd: Option<&ParcelFileDescriptor>,
         log_fd: Option<&ParcelFileDescriptor>,
         dump_dt_fd: Option<&ParcelFileDescriptor>,
-    ) -> binder::Result<Strong<dyn IVirtualMachine>> {
+    ) -> binder::Result<Strong<dyn IVirtualMachine::IVirtualMachine>> {
         let mut is_protected = false;
         let ret = self.create_vm_internal(
             config,
@@ -488,7 +490,7 @@
         log_fd: Option<&ParcelFileDescriptor>,
         is_protected: &mut bool,
         dump_dt_fd: Option<&ParcelFileDescriptor>,
-    ) -> binder::Result<Strong<dyn IVirtualMachine>> {
+    ) -> binder::Result<Strong<dyn IVirtualMachine::IVirtualMachine>> {
         let requester_uid = get_calling_uid();
         let requester_debug_pid = get_calling_pid();
 
@@ -1331,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.
@@ -1399,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/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/build/apex/Android.bp b/build/apex/Android.bp
index 4916df7..f794239 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -80,6 +80,11 @@
     }),
 }
 
+vintf_fragment {
+    name: "virtualizationservice.xml",
+    src: "virtualizationservice.xml",
+}
+
 apex_defaults {
     name: "com.android.virt_avf_enabled",
 
@@ -166,7 +171,7 @@
         true: "AndroidManifest.xml",
         default: unset,
     }),
-    vintf_fragments: select(soong_config_variable("ANDROID", "avf_remote_attestation_enabled"), {
+    vintf_fragment_modules: select(soong_config_variable("ANDROID", "avf_remote_attestation_enabled"), {
         "true": ["virtualizationservice.xml"],
         default: unset,
     }),
diff --git a/build/debian/build.sh b/build/debian/build.sh
index c50e5e5..97d9373 100755
--- a/build/debian/build.sh
+++ b/build/debian/build.sh
@@ -2,15 +2,15 @@
 
 # This is a script to build a Debian image that can run in a VM created via AVF.
 # TODOs:
-# - Support x86_64 architecture
 # - Add Android-specific packages via a new class
 # - Use a stable release from debian-cloud-images
 
 show_help() {
-	echo Usage: $0 [OPTION]... [FILE]
-	echo Builds a debian image and save it to FILE.
-	echo Options:
-	echo -h         Pring usage and this help message and exit.
+	echo "Usage: sudo $0 [OPTION]... [FILE]"
+	echo "Builds a debian image and save it to FILE. [sudo is required]"
+	echo "Options:"
+	echo "-h         Print usage and this help message and exit."
+	echo "-a ARCH    Architecture of the image [default is aarch64]"
 }
 
 check_sudo() {
@@ -21,104 +21,128 @@
 }
 
 parse_options() {
-	while getopts ":h" option; do
+	while getopts "ha:" 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"
+				if [[ "$arch" == "x86_64" ]]; then
+					debian_arch="amd64"
+				fi
+				;;
+			*)
+				echo "Invalid option: $OPTARG"
+				exit
+				;;
 		esac
 	done
-	if [ -n "$1" ]; then
-		built_image=$1
+	if [[ "${*:$OPTIND:1}" ]]; then
+		built_image="${*:$OPTIND:1}"
 	fi
 }
 
 install_prerequisites() {
 	apt update
+	packages=(
+		binfmt-support
+		build-essential
+		ca-certificates
+		curl
+		debsums
+		dosfstools
+		fai-server
+		fai-setup-storage
+		fdisk
+		make
+		protobuf-compiler
+		python3
+		python3-libcloud
+		python3-marshmallow
+		python3-pytest
+		python3-yaml
+		qemu-user-static
+		qemu-utils
+		sudo
+		udev
+	)
+	if [[ "$arch" == "aarch64" ]]; then
+		packages+=(
+			gcc-aarch64-linux-gnu
+			libc6-dev-arm64-cross
+			qemu-system-arm
+		)
+	else
+		packages+=(
+			qemu-system
+		)
+	fi
 	DEBIAN_FRONTEND=noninteractive \
-	apt install --no-install-recommends --assume-yes \
-		binfmt-support \
-		build-essential \
-		ca-certificates \
-		curl \
-		debsums \
-		dosfstools \
-		fai-server \
-		fai-setup-storage \
-		fdisk \
-		gcc-aarch64-linux-gnu \
-		libc6-dev-arm64-cross \
-		make \
-		python3 \
-		python3-libcloud \
-		python3-marshmallow \
-		python3-pytest \
-		python3-yaml \
-		qemu-system-arm \
-		qemu-user-static \
-		qemu-utils \
-		sudo \
-		udev \
+	apt install --no-install-recommends --assume-yes "${packages[@]}"
 
-
-	if [ ! -f $HOME/.cargo/bin/cargo ]; then
+	if [ ! -f $"HOME"/.cargo/bin/cargo ]; then
 		curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
 	fi
 
-        source $HOME/.cargo/env
-        rustup target add aarch64-unknown-linux-gnu
-
-        sed -i s/losetup\ -f/losetup\ -P\ -f/g /usr/sbin/fai-diskimage
-        sed -i 's/wget \$/wget -t 0 \$/g' /usr/share/debootstrap/functions
-
-        apt install --no-install-recommends --assume-yes curl
-        # just for testing
-        echo libseccomp: $(curl -is https://deb.debian.org/debian/pool/main/libs/libseccomp/libseccomp2_2.5.4-1+deb12u1_arm64.deb | head -n 1)
-        echo libsemanage-common: $(curl -is https://deb.debian.org/debian/pool/main/libs/libsemanage/libsemanage-common_3.4-1_all.deb | head -n 1)
-        echo libpcre2: $(curl -is https://deb.debian.org/debian/pool/main/p/pcre2/libpcre2-8-0_10.42-1_arm64.deb | head -n 1)
+	source "$HOME"/.cargo/env
+	rustup target add "${arch}"-unknown-linux-gnu
 }
 
 download_debian_cloud_image() {
 	local ver=master
 	local prj=debian-cloud-images
-	local url=https://salsa.debian.org/cloud-team/${prj}/-/archive/${ver}/${prj}-${ver}.tar.gz
-	local outdir=${debian_cloud_image}
+	local url="https://salsa.debian.org/cloud-team/${prj}/-/archive/${ver}/${prj}-${ver}.tar.gz"
+	local outdir="${debian_cloud_image}"
 
-	mkdir -p ${outdir}
-	wget -O - ${url} | tar xz -C ${outdir} --strip-components=1
+	mkdir -p "${outdir}"
+	wget -O - "${url}" | tar xz -C "${outdir}" --strip-components=1
 }
 
 copy_android_config() {
-	local src=$(dirname $0)/fai_config
-	local dst=${config_space}
+	local src="$(dirname "$0")/fai_config"
+	local dst="${config_space}"
 
-	cp -R ${src}/* ${dst}
-	cp $(dirname $0)/image.yaml ${resources_dir}
+	cp -R "${src}"/* "${dst}"
+	cp "$(dirname "$0")/image.yaml" "${resources_dir}"
 
 	local ttyd_version=1.7.7
-	local url=https://github.com/tsl0922/ttyd/releases/download/${ttyd_version}/ttyd.aarch64
-	mkdir -p ${dst}/files/usr/local/bin/ttyd
-	wget ${url} -O ${dst}/files/usr/local/bin/ttyd/AVF
-	chmod 777 ${dst}/files/usr/local/bin/ttyd/AVF
+	local url="https://github.com/tsl0922/ttyd/releases/download/${ttyd_version}/ttyd.${arch}"
+	mkdir -p "${dst}/files/usr/local/bin/ttyd"
+	wget "${url}" -O "${dst}/files/usr/local/bin/ttyd/AVF"
+	chmod 777 "${dst}/files/usr/local/bin/ttyd/AVF"
 
-        pushd $(dirname $0)/forwarder_guest > /dev/null
-		RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc" cargo build \
-			--target aarch64-unknown-linux-gnu \
-			--target-dir ${workdir}/forwarder_guest
-		mkdir -p ${dst}/files/usr/local/bin/forwarder_guest
-		cp ${workdir}/forwarder_guest/aarch64-unknown-linux-gnu/debug/forwarder_guest ${dst}/files/usr/local/bin/forwarder_guest/AVF
-		chmod 777 ${dst}/files/usr/local/bin/forwarder_guest/AVF
+	pushd "$(dirname "$0")/../../guest/forwarder_guest" > /dev/null
+	RUSTFLAGS="-C linker=${arch}-linux-gnu-gcc" cargo build \
+		--target "${arch}-unknown-linux-gnu" \
+		--target-dir "${workdir}/forwarder_guest"
+	mkdir -p "${dst}/files/usr/local/bin/forwarder_guest"
+	cp "${workdir}/forwarder_guest/${arch}-unknown-linux-gnu/debug/forwarder_guest" "${dst}/files/usr/local/bin/forwarder_guest/AVF"
+	chmod 777 "${dst}/files/usr/local/bin/forwarder_guest/AVF"
+	popd > /dev/null
+
+	pushd $(dirname $0)/../../guest/ip_addr_reporter > /dev/null
+	RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc" cargo build \
+		--target aarch64-unknown-linux-gnu \
+		--target-dir ${workdir}/ip_addr_reporter
+	mkdir -p ${dst}/files/usr/local/bin/ip_addr_reporter
+	cp ${workdir}/ip_addr_reporter/aarch64-unknown-linux-gnu/debug/ip_addr_reporter ${dst}/files/usr/local/bin/ip_addr_reporter/AVF
+	chmod 777 ${dst}/files/usr/local/bin/ip_addr_reporter/AVF
 	popd > /dev/null
 }
 
 run_fai() {
-	local out=${built_image}
-	make -C ${debian_cloud_image} image_bookworm_nocloud_arm64
-	mv ${debian_cloud_image}/image_bookworm_nocloud_arm64.raw ${out}
+	local out="${built_image}"
+	make -C "${debian_cloud_image}" "image_bookworm_nocloud_${debian_arch}"
+	mv "${debian_cloud_image}/image_bookworm_nocloud_${debian_arch}.raw" "${out}"
 }
 
 clean_up() {
-	rm -rf ${workdir}
+	rm -rf "${workdir}"
 }
 
 set -e
@@ -130,8 +154,10 @@
 debian_version=bookworm
 config_space=${debian_cloud_image}/config_space/${debian_version}
 resources_dir=${debian_cloud_image}/src/debian_cloud_images/resources
+arch=aarch64
+debian_arch=arm64
+parse_options "$@"
 check_sudo
-parse_options $@
 install_prerequisites
 download_debian_cloud_image
 copy_android_config
diff --git a/build/debian/build_in_container.sh b/build/debian/build_in_container.sh
index 6bc366b..fd1a975 100755
--- a/build/debian/build_in_container.sh
+++ b/build/debian/build_in_container.sh
@@ -1,5 +1,24 @@
 #!/bin/bash
 
-if [ -z $ANDROID_BUILD_TOP ]; then echo "forgot to source build/envsetup.sh?" && exit 1; fi
+if [ -z "$ANDROID_BUILD_TOP" ]; then echo "forgot to source build/envsetup.sh?" && exit 1; fi
 
-docker run --privileged -it -v $ANDROID_BUILD_TOP/packages/modules/Virtualization:/root/Virtualization -v /dev:/dev ubuntu:22.04 /root/Virtualization/build/debian/build.sh
+arch=aarch64
+while getopts "a:" option; do
+  case ${option} in
+    a)
+      if [[ "$OPTARG" != "aarch64" && "$OPTARG" != "x86_64" ]]; then
+        echo "Invalid architecture: $OPTARG"
+        exit
+      fi
+      arch="$OPTARG"
+      ;;
+    *)
+      echo "Invalid option: $OPTARG"
+      exit
+      ;;
+  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"
diff --git a/build/debian/fai_config/files/etc/systemd/system/ip_addr_reporter.service/AVF b/build/debian/fai_config/files/etc/systemd/system/ip_addr_reporter.service/AVF
new file mode 100644
index 0000000..7d163fb
--- /dev/null
+++ b/build/debian/fai_config/files/etc/systemd/system/ip_addr_reporter.service/AVF
@@ -0,0 +1,13 @@
+[Unit]
+Description=ip report service
+After=syslog.target
+After=network.target
+Requires=ttyd.service
+[Service]
+ExecStart=/usr/local/bin/ip_addr_reporter
+Type=simple
+Restart=on-failure
+User=root
+Group=root
+[Install]
+WantedBy=multi-user.target
diff --git a/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF b/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
index f71557d..6b932e9 100644
--- a/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
+++ b/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
@@ -3,7 +3,7 @@
 After=syslog.target
 After=network.target
 [Service]
-ExecStart=/usr/local/bin/ttyd -W screen -aAxR -S main login
+ExecStart=/usr/local/bin/ttyd -W screen -aAxR -S main login -f droid
 Type=simple
 Restart=always
 User=root
diff --git a/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF b/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF
deleted file mode 100644
index a29020b..0000000
--- a/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF
+++ /dev/null
@@ -1,12 +0,0 @@
-[Unit]
-Description=vsock ip service
-After=syslog.target
-After=network.target
-[Service]
-ExecStart=/usr/bin/python3 /usr/local/bin/vsock.py
-Type=simple
-Restart=always
-User=root
-Group=root
-[Install]
-WantedBy=multi-user.target
diff --git a/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF b/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF
deleted file mode 100755
index 292d953..0000000
--- a/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env python3
-
-import socket
-
-# Constants for vsock (from linux/vm_sockets.h)
-AF_VSOCK = 40
-SOCK_STREAM = 1
-VMADDR_CID_ANY = -1
-
-def get_local_ip():
-    """Retrieves the first IPv4 address found on the system.
-
-    Returns:
-        str: The local IPv4 address, or '127.0.0.1' if no IPv4 address is found.
-    """
-
-    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-    try:
-        s.connect(('8.8.8.8', 80))
-        ip = s.getsockname()[0]
-    except Exception:
-        ip = '127.0.0.1'
-    finally:
-        s.close()
-    return ip
-
-def main():
-    PORT = 1024
-
-    # Create a vsock socket
-    server_socket = socket.socket(AF_VSOCK, SOCK_STREAM)
-
-    # Bind the socket to the server address
-    server_address = (VMADDR_CID_ANY, PORT)
-    server_socket.bind(server_address)
-
-    # Listen for incoming connections
-    server_socket.listen(1)
-    print(f"VSOCK server listening on port {PORT}...")
-
-    while True:
-        # Accept a connection
-        connection, client_address = server_socket.accept()
-        print(f"Connection from: {client_address}")
-
-        try:
-            # Get the local IP address
-            local_ip = get_local_ip()
-
-            # Send the IP address to the client
-            connection.sendall(local_ip.encode())
-        finally:
-            # Close the connection
-            connection.close()
-
-if __name__ == "__main__":
-    main()
diff --git a/build/debian/fai_config/scripts/AVF/10-systemd b/build/debian/fai_config/scripts/AVF/10-systemd
index d33b92a..09d1bd1 100755
--- a/build/debian/fai_config/scripts/AVF/10-systemd
+++ b/build/debian/fai_config/scripts/AVF/10-systemd
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 chmod +x $target/usr/local/bin/forwarder_guest
+chmod +x $target/usr/local/bin/ip_addr_reporter
 chmod +x $target/usr/local/bin/ttyd
-chmod +x $target/usr/local/bin/vsock.py
 ln -s /etc/systemd/system/ttyd.service $target/etc/systemd/system/multi-user.target.wants/ttyd.service
-ln -s /etc/systemd/system/vsockip.service $target/etc/systemd/system/multi-user.target.wants/vsockip.service
\ No newline at end of file
+ln -s /etc/systemd/system/ip_addr_reporter.service $target/etc/systemd/system/multi-user.target.wants/ip_addr_reporter.service
\ No newline at end of file
diff --git a/build/debian/fai_config/scripts/AVF/20-useradd b/build/debian/fai_config/scripts/AVF/20-useradd
new file mode 100755
index 0000000..9fbcd43
--- /dev/null
+++ b/build/debian/fai_config/scripts/AVF/20-useradd
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+$ROOTCMD useradd -m -u 1000 -N -G sudo droid
+$ROOTCMD echo 'droid ALL=(ALL) NOPASSWD:ALL' >> $target/etc/sudoers
\ No newline at end of file
diff --git a/build/debian/forwarder_guest/Cargo.toml b/guest/forwarder_guest/Cargo.toml
similarity index 79%
rename from build/debian/forwarder_guest/Cargo.toml
rename to guest/forwarder_guest/Cargo.toml
index e70dcd4..65f57cf 100644
--- a/build/debian/forwarder_guest/Cargo.toml
+++ b/guest/forwarder_guest/Cargo.toml
@@ -5,7 +5,7 @@
 
 [dependencies]
 clap = { version = "4.5.19", features = ["derive"] }
-forwarder = { path = "../../../libs/libforwarder" }
+forwarder = { path = "../../libs/libforwarder" }
 poll_token_derive = "0.1.0"
 remain = "0.2.14"
 vmm-sys-util = "0.12.1"
diff --git a/build/debian/forwarder_guest/src/main.rs b/guest/forwarder_guest/src/main.rs
similarity index 100%
rename from build/debian/forwarder_guest/src/main.rs
rename to guest/forwarder_guest/src/main.rs
diff --git a/guest/ip_addr_reporter/.gitignore b/guest/ip_addr_reporter/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/guest/ip_addr_reporter/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/guest/ip_addr_reporter/Cargo.toml b/guest/ip_addr_reporter/Cargo.toml
new file mode 100644
index 0000000..e255eaf
--- /dev/null
+++ b/guest/ip_addr_reporter/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "ip_addr_reporter"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+netdev = "0.31.0"
+prost = "0.13.3"
+tokio = { version = "1.40.0", features = ["rt-multi-thread"] }
+tonic = "0.12.3"
+
+[build-dependencies]
+tonic-build = "0.12.3"
diff --git a/guest/ip_addr_reporter/build.rs b/guest/ip_addr_reporter/build.rs
new file mode 100644
index 0000000..e3939d4
--- /dev/null
+++ b/guest/ip_addr_reporter/build.rs
@@ -0,0 +1,7 @@
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let proto_file = "../../libs/debian_service/proto/DebianService.proto";
+
+    tonic_build::compile_protos(proto_file).unwrap();
+
+    Ok(())
+}
diff --git a/guest/ip_addr_reporter/src/main.rs b/guest/ip_addr_reporter/src/main.rs
new file mode 100644
index 0000000..5784a83
--- /dev/null
+++ b/guest/ip_addr_reporter/src/main.rs
@@ -0,0 +1,26 @@
+use api::debian_service_client::DebianServiceClient;
+use api::IpAddr;
+
+pub mod api {
+    tonic::include_proto!("com.android.virtualization.vmlauncher.proto");
+}
+
+#[tokio::main]
+async fn main() -> Result<(), String> {
+    let gateway_ip_addr = netdev::get_default_gateway()?.ipv4[0];
+    let ip_addr = netdev::get_default_interface()?.ipv4[0].addr();
+    const PORT: i32 = 12000;
+
+    let server_addr = format!("http://{}:{}", gateway_ip_addr.to_string(), PORT);
+
+    println!("local ip addr: {}", ip_addr.to_string());
+    println!("coonect to grpc server {}", server_addr);
+
+    let mut client = DebianServiceClient::connect(server_addr).await.map_err(|e| e.to_string())?;
+
+    let request = tonic::Request::new(IpAddr { addr: ip_addr.to_string() });
+
+    let response = client.report_vm_ip_addr(request).await.map_err(|e| e.to_string())?;
+    println!("response from server: {:?}", response);
+    Ok(())
+}
diff --git a/build/debian/port_listener/build.sh b/guest/port_listener/build.sh
similarity index 100%
rename from build/debian/port_listener/build.sh
rename to guest/port_listener/build.sh
diff --git a/build/debian/port_listener/src/common.h b/guest/port_listener/src/common.h
similarity index 100%
rename from build/debian/port_listener/src/common.h
rename to guest/port_listener/src/common.h
diff --git a/build/debian/port_listener/src/listen_tracker.ebpf.c b/guest/port_listener/src/listen_tracker.ebpf.c
similarity index 99%
rename from build/debian/port_listener/src/listen_tracker.ebpf.c
rename to guest/port_listener/src/listen_tracker.ebpf.c
index 030ded0..9e98aad 100644
--- a/build/debian/port_listener/src/listen_tracker.ebpf.c
+++ b/guest/port_listener/src/listen_tracker.ebpf.c
@@ -16,11 +16,10 @@
 // src/platform2/vm_tools/port_listener/listen_tracker.ebpf.c
 
 // bpf_helpers.h uses types defined here
-#include "vmlinux.h"
-
 #include <bpf/bpf_helpers.h>
 
 #include "common.h"
+#include "vmlinux.h"
 
 // For some reason 6.1 doesn't include these symbols in the debug build
 // so they don't get included in vmlinux.h. These features have existed since
diff --git a/build/debian/port_listener/src/main.cc b/guest/port_listener/src/main.cc
similarity index 99%
rename from build/debian/port_listener/src/main.cc
rename to guest/port_listener/src/main.cc
index b0b0979..1caceaf 100644
--- a/build/debian/port_listener/src/main.cc
+++ b/guest/port_listener/src/main.cc
@@ -18,9 +18,8 @@
 #include <bpf/libbpf.h>
 #include <bpf/libbpf_legacy.h>
 #include <glog/logging.h>
-#include <sys/socket.h>
-
 #include <linux/vm_sockets.h> // Needs to come after sys/socket.h
+#include <sys/socket.h>
 
 #include <memory>
 #include <unordered_map>
diff --git a/libs/debian_service/Android.bp b/libs/debian_service/Android.bp
new file mode 100644
index 0000000..0495825
--- /dev/null
+++ b/libs/debian_service/Android.bp
@@ -0,0 +1,56 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+JAVA_LITE_PROTO_CMD = "mkdir -p $(genDir)/gen && " +
+    "$(location aprotoc) --java_opt=annotate_code=false " +
+    "-Iexternal/protobuf/src " +
+    "-Ipackages/modules/Virtualization/libs/debian_service/proto " +
+    "--plugin=protoc-gen-grpc-java=$(location protoc-gen-grpc-java-plugin) " +
+    "--grpc-java_out=lite:$(genDir)/gen $(in) && " +
+    "$(location soong_zip) -o $(out) -C $(genDir)/gen -D $(genDir)/gen"
+
+java_genrule {
+    name: "debian-service-stub-lite",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-java-plugin",
+        "soong_zip",
+    ],
+    cmd: JAVA_LITE_PROTO_CMD,
+    srcs: [
+        "proto/*.proto",
+        ":libprotobuf-internal-protos",
+    ],
+    out: [
+        "protos.srcjar",
+    ],
+}
+
+java_library {
+    name: "debian-service-grpclib-lite",
+    proto: {
+        type: "lite",
+        include_dirs: [
+            "external/protobuf/src",
+            "external/protobuf/java",
+        ],
+    },
+    srcs: [
+        ":debian-service-stub-lite",
+        "proto/*.proto",
+        ":libprotobuf-internal-protos",
+    ],
+    libs: ["javax_annotation-api_1.3.2"],
+    static_libs: [
+        "libprotobuf-java-lite",
+        "grpc-java-core-android",
+        "grpc-java-okhttp-client-lite",
+        "guava",
+    ],
+    sdk_version: "current",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.virt",
+    ],
+}
diff --git a/libs/debian_service/proto/DebianService.proto b/libs/debian_service/proto/DebianService.proto
new file mode 100644
index 0000000..5adf615
--- /dev/null
+++ b/libs/debian_service/proto/DebianService.proto
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package com.android.virtualization.vmlauncher.proto;
+
+option java_package = "com.android.virtualization.vmlauncher.proto";
+option java_multiple_files = true;
+
+service DebianService {
+  rpc ReportVmIpAddr (IpAddr) returns (ReportVmIpAddrResponse) {}
+}
+
+message IpAddr {
+  string addr = 1;
+}
+
+message ReportVmIpAddrResponse {
+    bool success = 1;
+}
\ No newline at end of file
diff --git a/libs/libservice_vm_requests/src/rkp.rs b/libs/libservice_vm_requests/src/rkp.rs
index e2be11b..7de7cd5 100644
--- a/libs/libservice_vm_requests/src/rkp.rs
+++ b/libs/libservice_vm_requests/src/rkp.rs
@@ -63,8 +63,7 @@
 
 const CSR_PAYLOAD_SCHEMA_V3: u8 = 3;
 const AUTH_REQ_SCHEMA_V1: u8 = 1;
-// TODO(b/300624493): Add a new certificate type for AVF CSR.
-const CERTIFICATE_TYPE: &str = "keymint";
+const CERTIFICATE_TYPE: &str = "rkp-vm";
 
 /// Builds the CSR described in:
 ///
diff --git a/libs/vm_launcher_lib/Android.bp b/libs/vm_launcher_lib/Android.bp
index cb6fc9e..5267508 100644
--- a/libs/vm_launcher_lib/Android.bp
+++ b/libs/vm_launcher_lib/Android.bp
@@ -12,6 +12,7 @@
     platform_apis: true,
     static_libs: [
         "gson",
+        "debian-service-grpclib-lite",
     ],
     libs: [
         "framework-virtualization.impl",
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
new file mode 100644
index 0000000..2f7666a
--- /dev/null
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
@@ -0,0 +1,49 @@
+/*
+ * 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.vmlauncher;
+
+import android.util.Log;
+
+import com.android.virtualization.vmlauncher.proto.DebianServiceGrpc;
+import com.android.virtualization.vmlauncher.proto.IpAddr;
+import com.android.virtualization.vmlauncher.proto.ReportVmIpAddrResponse;
+
+import io.grpc.stub.StreamObserver;
+
+class DebianServiceImpl extends DebianServiceGrpc.DebianServiceImplBase {
+    public static final String TAG = "DebianService";
+    private final DebianServiceCallback mCallback;
+
+    protected DebianServiceImpl(DebianServiceCallback callback) {
+        super();
+        mCallback = callback;
+    }
+
+    @Override
+    public void reportVmIpAddr(
+            IpAddr request, StreamObserver<ReportVmIpAddrResponse> responseObserver) {
+        Log.d(DebianServiceImpl.TAG, "reportVmIpAddr: " + request.toString());
+        mCallback.onIpAddressAvailable(request.getAddr());
+        ReportVmIpAddrResponse reply = ReportVmIpAddrResponse.newBuilder().setSuccess(true).build();
+        responseObserver.onNext(reply);
+        responseObserver.onCompleted();
+    }
+
+    protected interface DebianServiceCallback {
+        void onIpAddressAvailable(String ipAddr);
+    }
+}
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
index 5e78f99..4cca110 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
@@ -22,25 +22,23 @@
 import android.app.Service;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.os.ParcelFileDescriptor;
 import android.os.ResultReceiver;
 import android.system.virtualmachine.VirtualMachine;
 import android.system.virtualmachine.VirtualMachineConfig;
 import android.system.virtualmachine.VirtualMachineException;
 import android.util.Log;
 
-import java.io.BufferedReader;
-import java.io.FileInputStream;
+import io.grpc.InsecureServerCredentials;
+import io.grpc.Server;
+import io.grpc.okhttp.OkHttpServerBuilder;
+
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.nio.file.Path;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-public class VmLauncherService extends Service {
+public class VmLauncherService extends Service implements DebianServiceImpl.DebianServiceCallback {
     private static final String TAG = "VmLauncherService";
     // TODO: this path should be from outside of this service
     private static final String VM_CONFIG_PATH = "/data/local/tmp/vm_config.json";
@@ -54,6 +52,7 @@
     private ExecutorService mExecutorService;
     private VirtualMachine mVirtualMachine;
     private ResultReceiver mResultReceiver;
+    private Server mServer;
 
     @Override
     public IBinder onBind(Intent intent) {
@@ -113,10 +112,9 @@
         startForeground();
 
         mResultReceiver.send(RESULT_START, null);
-        if (config.getCustomImageConfig().useNetwork()) {
-            Handler handler = new Handler(Looper.getMainLooper());
-            gatherIpAddrFromVm(handler);
-        }
+
+        startDebianServer();
+
         return START_NOT_STICKY;
     }
 
@@ -134,6 +132,7 @@
             mExecutorService = null;
             mVirtualMachine = null;
         }
+        stopDebianServer();
     }
 
     private boolean isVmRunning() {
@@ -141,34 +140,37 @@
                 && mVirtualMachine.getStatus() == VirtualMachine.STATUS_RUNNING;
     }
 
-    // TODO(b/359523803): Use AVF API to get ip addr when it exists
-    private void gatherIpAddrFromVm(Handler handler) {
-        handler.postDelayed(
-                () -> {
-                    if (!isVmRunning()) {
-                        Log.d(TAG, "A virtual machine instance isn't running");
-                        return;
-                    }
-                    int INTERNAL_VSOCK_SERVER_PORT = 1024;
-                    try (ParcelFileDescriptor pfd =
-                            mVirtualMachine.connectVsock(INTERNAL_VSOCK_SERVER_PORT)) {
-                        try (BufferedReader input =
-                                new BufferedReader(
-                                        new InputStreamReader(
-                                                new FileInputStream(pfd.getFileDescriptor())))) {
-                            String vmIpAddr = input.readLine().strip();
-                            Bundle b = new Bundle();
-                            b.putString(KEY_VM_IP_ADDR, vmIpAddr);
-                            mResultReceiver.send(RESULT_IPADDR, b);
-                            return;
-                        } catch (IOException e) {
-                            Log.e(TAG, e.toString());
-                        }
-                    } catch (Exception e) {
-                        Log.e(TAG, e.toString());
-                    }
-                    gatherIpAddrFromVm(handler);
-                },
-                1000);
+    private void startDebianServer() {
+        new Thread(
+                        () -> {
+                            // TODO(b/372666638): gRPC for java doesn't support vsock for now.
+                            // In addition, let's consider using a dynamic port and SSL(and client
+                            // certificate)
+                            int port = 12000;
+                            try {
+                                mServer =
+                                        OkHttpServerBuilder.forPort(
+                                                        port, InsecureServerCredentials.create())
+                                                .addService(new DebianServiceImpl(this))
+                                                .build()
+                                                .start();
+                            } catch (IOException e) {
+                                Log.d(TAG, "grpc server error", e);
+                            }
+                        })
+                .start();
+    }
+
+    private void stopDebianServer() {
+        if (mServer != null) {
+            mServer.shutdown();
+        }
+    }
+
+    @Override
+    public void onIpAddressAvailable(String ipAddr) {
+        Bundle b = new Bundle();
+        b.putString(VmLauncherService.KEY_VM_IP_ADDR, ipAddr);
+        mResultReceiver.send(VmLauncherService.RESULT_IPADDR, b);
     }
 }
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 1f86d6b..d0838a6 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -26,7 +26,6 @@
         ":microdroid_general_sepolicy.conf",
         ":test.com.android.virt.pem",
         ":test2.com.android.virt.pem",
-        "java/**/goldens/dt_dump_*",
     ],
     data_native_bins: [
         "sepolicy-analyze",
@@ -39,7 +38,6 @@
         "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.
@@ -50,7 +48,6 @@
         "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 b8b4312..2d55d66 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -1352,134 +1352,6 @@
         }
     }
 
-    @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());
@@ -1556,11 +1428,4 @@
                 "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
deleted file mode 100644
index 795c50f..0000000
--- a/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_golden.dts
+++ /dev/null
@@ -1,145 +0,0 @@
-/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
deleted file mode 100644
index 5761c15..0000000
--- a/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_protected_golden.dts
+++ /dev/null
@@ -1,159 +0,0 @@
-/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");