Merge "Revert "Adding parameter for trusty_security_vm_launcher vCPU configuration"" into main
diff --git a/android/TerminalApp/AndroidManifest.xml b/android/TerminalApp/AndroidManifest.xml
index 61737fe..6a1ecaf 100644
--- a/android/TerminalApp/AndroidManifest.xml
+++ b/android/TerminalApp/AndroidManifest.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     package="com.android.virtualization.terminal">
@@ -65,7 +80,8 @@
         <service
             android:name="com.android.virtualization.vmlauncher.VmLauncherService"
             android:exported="false"
-            android:foregroundServiceType="specialUse">
+            android:foregroundServiceType="specialUse"
+            android:stopWithTask="true" >
             <property
                 android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
                 android:value="Run VM instances" />
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
index a9102b0..1abba85 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
@@ -29,12 +29,15 @@
 import android.os.RemoteException;
 import android.text.format.Formatter;
 import android.util.Log;
+import android.view.View;
 import android.widget.CheckBox;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import com.google.android.material.progressindicator.LinearProgressIndicator;
+import com.google.android.material.snackbar.Snackbar;
+
 import java.lang.ref.WeakReference;
 import java.util.concurrent.ExecutorService;
 
@@ -107,10 +110,10 @@
 
     public void handleCriticalError(Exception e) {
         if (Build.isDebuggable()) {
-            Toast.makeText(
-                            this,
+            Snackbar.make(
+                            findViewById(android.R.id.content),
                             e.getMessage() + ". File a bugreport to go/ferrochrome-bug",
-                            Toast.LENGTH_LONG)
+                            Snackbar.LENGTH_INDEFINITE)
                     .show();
         }
         Log.e(TAG, "Internal error", e);
@@ -128,6 +131,12 @@
     private void setInstallEnabled(boolean enable) {
         mInstallButton.setEnabled(enable);
         mWaitForWifiCheckbox.setEnabled(enable);
+        LinearProgressIndicator progressBar = findViewById(R.id.installer_progress);
+        if (enable) {
+            progressBar.setVisibility(View.INVISIBLE);
+        } else {
+            progressBar.setVisibility(View.VISIBLE);
+        }
 
         int resId =
                 enable
@@ -180,9 +189,9 @@
 
     @MainThread
     private void handleError(String displayText) {
-        // TODO(b/375542145): Display error with snackbar.
         if (Build.isDebuggable()) {
-            Toast.makeText(this, displayText, Toast.LENGTH_LONG).show();
+            Snackbar.make(findViewById(android.R.id.content), displayText, Snackbar.LENGTH_LONG)
+                    .show();
         }
         setInstallEnabled(true);
     }
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index 4e32de7..fdf1f3b 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -80,7 +80,7 @@
     private static final String VM_ADDR = "192.168.0.2";
     private static final int TTYD_PORT = 7681;
     private static final int REQUEST_CODE_INSTALLER = 0x33;
-    private static final int FONT_SIZE_DEFAULT = 12;
+    private static final int FONT_SIZE_DEFAULT = 13;
 
     private X509Certificate[] mCertificates;
     private PrivateKey mPrivateKey;
@@ -88,7 +88,7 @@
     private AccessibilityManager mAccessibilityManager;
     private ConditionVariable mBootCompleted = new ConditionVariable();
     private static final int POST_NOTIFICATIONS_PERMISSION_REQUEST_CODE = 101;
-    private ActivityResultLauncher<Intent> manageExternalStorageActivityResultLauncher;
+    private ActivityResultLauncher<Intent> mManageExternalStorageActivityResultLauncher;
     private static int diskSizeStep;
 
     @Override
@@ -122,27 +122,17 @@
         readClientCertificate();
         connectToTerminalService();
 
-        manageExternalStorageActivityResultLauncher =
+        mManageExternalStorageActivityResultLauncher =
                 registerForActivityResult(
                         new ActivityResultContracts.StartActivityForResult(),
                         (ActivityResult result) -> {
-                            if (Environment.isExternalStorageManager()) {
-                                Toast.makeText(this, "Storage permission set!", Toast.LENGTH_SHORT)
-                                        .show();
-                            } else {
-                                Toast.makeText(
-                                                this,
-                                                "Storage permission not set",
-                                                Toast.LENGTH_SHORT)
-                                        .show();
-                            }
                             startVm();
                         });
 
         // if installer is launched, it will be handled in onActivityResult
         if (!launchInstaller) {
             if (!Environment.isExternalStorageManager()) {
-                requestStoragePermissions(this, manageExternalStorageActivityResultLauncher);
+                requestStoragePermissions(this, mManageExternalStorageActivityResultLauncher);
             } else {
                 startVm();
             }
@@ -435,7 +425,7 @@
                 finish();
             }
             if (!Environment.isExternalStorageManager()) {
-                requestStoragePermissions(this, manageExternalStorageActivityResultLauncher);
+                requestStoragePermissions(this, mManageExternalStorageActivityResultLauncher);
             } else {
                 startVm();
             }
diff --git a/android/TerminalApp/res/drawable/baseline_call_missed_outgoing_24.xml b/android/TerminalApp/res/drawable/baseline_call_missed_outgoing_24.xml
index ec1f990..052d14d 100644
--- a/android/TerminalApp/res/drawable/baseline_call_missed_outgoing_24.xml
+++ b/android/TerminalApp/res/drawable/baseline_call_missed_outgoing_24.xml
@@ -1,3 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="?attr/colorControlNormal" 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"/>
diff --git a/android/TerminalApp/res/drawable/baseline_settings_backup_restore_24.xml b/android/TerminalApp/res/drawable/baseline_settings_backup_restore_24.xml
index 513c42b..895707d 100644
--- a/android/TerminalApp/res/drawable/baseline_settings_backup_restore_24.xml
+++ b/android/TerminalApp/res/drawable/baseline_settings_backup_restore_24.xml
@@ -1,3 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?attr/colorControlNormal" 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"/>
diff --git a/android/TerminalApp/res/drawable/baseline_storage_24.xml b/android/TerminalApp/res/drawable/baseline_storage_24.xml
index 503d1bc..2978ffb 100644
--- a/android/TerminalApp/res/drawable/baseline_storage_24.xml
+++ b/android/TerminalApp/res/drawable/baseline_storage_24.xml
@@ -1,3 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?attr/colorControlNormal" 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"/>
diff --git a/android/TerminalApp/res/drawable/ic_launcher_background.xml b/android/TerminalApp/res/drawable/ic_launcher_background.xml
index 07d5da9..34e738d 100644
--- a/android/TerminalApp/res/drawable/ic_launcher_background.xml
+++ b/android/TerminalApp/res/drawable/ic_launcher_background.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="108dp"
     android:height="108dp"
diff --git a/android/TerminalApp/res/drawable/ic_launcher_foreground.xml b/android/TerminalApp/res/drawable/ic_launcher_foreground.xml
index 8b28c8e..65a3b27 100644
--- a/android/TerminalApp/res/drawable/ic_launcher_foreground.xml
+++ b/android/TerminalApp/res/drawable/ic_launcher_foreground.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="108dp"
     android:height="108dp"
diff --git a/android/TerminalApp/res/drawable/ic_lock_open.xml b/android/TerminalApp/res/drawable/ic_lock_open.xml
deleted file mode 100644
index c623592..0000000
--- a/android/TerminalApp/res/drawable/ic_lock_open.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="48dp"
-    android:height="48dp"
-    android:viewportWidth="960"
-    android:viewportHeight="960"
-    android:tint="?attr/colorControlNormal">
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M220,326L610,326L610,230Q610,175.83 572.12,137.92Q534.24,100 480.12,100Q426,100 388,137.92Q350,175.83 350,230L290,230Q290,151 345.61,95.5Q401.21,40 480.11,40Q559,40 614.5,95.58Q670,151.15 670,230L670,326L740,326Q764.75,326 782.38,343.62Q800,361.25 800,386L800,820Q800,844.75 782.38,862.37Q764.75,880 740,880L220,880Q195.25,880 177.63,862.37Q160,844.75 160,820L160,386Q160,361.25 177.63,343.62Q195.25,326 220,326ZM220,820L740,820Q740,820 740,820Q740,820 740,820L740,386Q740,386 740,386Q740,386 740,386L220,386Q220,386 220,386Q220,386 220,386L220,820Q220,820 220,820Q220,820 220,820ZM480.17,680Q512,680 534.5,657.97Q557,635.94 557,605Q557,575 534.33,550.5Q511.66,526 479.83,526Q448,526 425.5,550.5Q403,575 403,605.5Q403,636 425.67,658Q448.34,680 480.17,680ZM220,820Q220,820 220,820Q220,820 220,820L220,386Q220,386 220,386Q220,386 220,386L220,386Q220,386 220,386Q220,386 220,386L220,820Q220,820 220,820Q220,820 220,820Z"/>
-</vector>
-
diff --git a/android/TerminalApp/res/drawable/ic_settings.xml b/android/TerminalApp/res/drawable/ic_settings.xml
index 4bcd4aa..d117e96 100644
--- a/android/TerminalApp/res/drawable/ic_settings.xml
+++ b/android/TerminalApp/res/drawable/ic_settings.xml
@@ -1,3 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
diff --git a/android/TerminalApp/res/drawable/ic_terminal.xml b/android/TerminalApp/res/drawable/ic_terminal.xml
new file mode 100644
index 0000000..03d7054
--- /dev/null
+++ b/android/TerminalApp/res/drawable/ic_terminal.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="142"
+    android:viewportHeight="168.75">
+  <group android:translateY="133.59375">
+    <path android:pathData="M9.078125,-77.484375L69.75,-51.40625L69.75,-37.765625L9.078125,-11.609375L9.078125,-28.40625L52.53125,-44.71875L9.078125,-60.75L9.078125,-77.484375Z"
+        android:fillColor="#3BBA46"/>
+    <path android:pathData="M139.76562,0L139.76562,13.5L75.21875,13.5L75.21875,0L139.76562,0Z"
+        android:fillColor="#3BBA46"/>
+  </group>
+</vector>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/activity_headless.xml b/android/TerminalApp/res/layout/activity_headless.xml
index 7da87af..8a15dd8 100644
--- a/android/TerminalApp/res/layout/activity_headless.xml
+++ b/android/TerminalApp/res/layout/activity_headless.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
diff --git a/android/TerminalApp/res/layout/activity_installer.xml b/android/TerminalApp/res/layout/activity_installer.xml
index c375cb8..ce37129 100644
--- a/android/TerminalApp/res/layout/activity_installer.xml
+++ b/android/TerminalApp/res/layout/activity_installer.xml
@@ -1,56 +1,92 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:fitsSystemWindows="true"
-    android:paddingLeft="16dp"
-    android:paddingRight="16dp">
-    <TextView
-        android:id="@+id/installer_title"
-        android:layout_width="match_parent"
+    android:fitsSystemWindows="true">
+
+    <com.google.android.material.progressindicator.LinearProgressIndicator
+        android:id="@+id/installer_progress"
+        android:indeterminate="true"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
         android:layout_alignParentStart="true"
-        android:textSize="32sp"
-        android:text="@string/installer_title_text" />
+        android:visibility="invisible"
+        />
 
     <ImageView
-        android:id="@+id/installer_icon"
-        android:layout_width="match_parent"
-        android:layout_height="300dp"
-        android:padding="10dp"
-        android:layout_below="@id/installer_title"
+        android:id="@+id/installer_terminal_icon"
+        android:layout_width="30dp"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_terminal"
+        android:adjustViewBounds="true"
         android:layout_alignParentStart="true"
-        android:src="@drawable/ic_lock_open"
-        android:adjustViewBounds="true" />
+        android:layout_marginTop="48dp"
+        android:layout_marginLeft="32dp"
+        android:layout_marginRight="32dp"
+        app:tint="?attr/colorPrimary" />
+
+    <TextView
+        android:id="@+id/installer_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:singleLine="false"
+        android:text="@string/installer_title_text"
+        android:layout_below="@id/installer_terminal_icon"
+        android:layout_marginTop="24dp"
+        android:layout_marginBottom="24dp"
+        android:layout_marginLeft="32dp"
+        android:layout_marginRight="32dp"
+        android:textSize="36sp" />
 
     <TextView
         android:id="@+id/installer_desc"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:padding="10dp"
-        android:layout_below="@id/installer_icon"
-        android:layout_alignParentStart="true"
         android:singleLine="false"
-        android:textSize="24sp" />
+        android:layout_below="@id/installer_title"
+        android:lineSpacingExtra="5dp"
+        android:layout_marginTop="24dp"
+        android:layout_marginLeft="32dp"
+        android:layout_marginRight="32dp"
+        android:textSize="16sp" />
 
     <CheckBox
         android:id="@+id/installer_wait_for_wifi_checkbox"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:padding="10dp"
+        android:textSize="16sp"
+        android:layout_marginLeft="42dp"
+        android:layout_marginRight="42dp"
+        android:layout_above="@id/installer_install_button"
         android:layout_alignParentEnd="true"
-        android:layout_below="@id/installer_desc"
         android:text="@string/installer_wait_for_wifi_checkbox_text" />
 
     <Button
         android:id="@+id/installer_install_button"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:padding="10dp"
         android:layout_alignParentBottom="true"
         android:layout_alignParentEnd="true"
+        android:layout_marginBottom="32dp"
+        android:layout_marginLeft="40dp"
+        android:layout_marginRight="40dp"
+        android:backgroundTint="?attr/colorPrimaryDark"
         android:text="@string/installer_install_button_enabled_text" />
-
 </RelativeLayout>
diff --git a/android/TerminalApp/res/layout/settings_activity.xml b/android/TerminalApp/res/layout/settings_activity.xml
index 3ec2617..9edfd96 100644
--- a/android/TerminalApp/res/layout/settings_activity.xml
+++ b/android/TerminalApp/res/layout/settings_activity.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
diff --git a/android/TerminalApp/res/layout/settings_disk_resize.xml b/android/TerminalApp/res/layout/settings_disk_resize.xml
index a41b580..6c8c2c1 100644
--- a/android/TerminalApp/res/layout/settings_disk_resize.xml
+++ b/android/TerminalApp/res/layout/settings_disk_resize.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
diff --git a/android/TerminalApp/res/layout/settings_list_item.xml b/android/TerminalApp/res/layout/settings_list_item.xml
index 89f2d82..7b27421 100644
--- a/android/TerminalApp/res/layout/settings_list_item.xml
+++ b/android/TerminalApp/res/layout/settings_list_item.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
diff --git a/android/TerminalApp/res/layout/settings_port_forwarding.xml b/android/TerminalApp/res/layout/settings_port_forwarding.xml
index 1d68907..199b8cb 100644
--- a/android/TerminalApp/res/layout/settings_port_forwarding.xml
+++ b/android/TerminalApp/res/layout/settings_port_forwarding.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
diff --git a/android/TerminalApp/res/layout/settings_port_forwarding_item.xml b/android/TerminalApp/res/layout/settings_port_forwarding_item.xml
index 9e5981e..5418bf8 100644
--- a/android/TerminalApp/res/layout/settings_port_forwarding_item.xml
+++ b/android/TerminalApp/res/layout/settings_port_forwarding_item.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
diff --git a/android/TerminalApp/res/layout/settings_recovery.xml b/android/TerminalApp/res/layout/settings_recovery.xml
index e18c8a6..12344c6 100644
--- a/android/TerminalApp/res/layout/settings_recovery.xml
+++ b/android/TerminalApp/res/layout/settings_recovery.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
diff --git a/android/TerminalApp/res/menu/main_menu.xml b/android/TerminalApp/res/menu/main_menu.xml
index 0fee90e..42ba85d 100644
--- a/android/TerminalApp/res/menu/main_menu.xml
+++ b/android/TerminalApp/res/menu/main_menu.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <menu xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
     <item android:id="@+id/menu_item_settings"
diff --git a/android/TerminalApp/res/mipmap-anydpi-v26/ic_launcher.xml b/android/TerminalApp/res/mipmap-anydpi-v26/ic_launcher.xml
index 7353dbd..f96d8eb 100644
--- a/android/TerminalApp/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/android/TerminalApp/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     <background android:drawable="@color/ic_launcher_background"/>
     <foreground android:drawable="@drawable/ic_launcher_foreground"/>
diff --git a/android/TerminalApp/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/TerminalApp/res/mipmap-anydpi-v26/ic_launcher_round.xml
index 7353dbd..f96d8eb 100644
--- a/android/TerminalApp/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/android/TerminalApp/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     <background android:drawable="@color/ic_launcher_background"/>
     <foreground android:drawable="@drawable/ic_launcher_foreground"/>
diff --git a/android/TerminalApp/res/values/dimens.xml b/android/TerminalApp/res/values/dimens.xml
index e6ed461..e00ef7c 100644
--- a/android/TerminalApp/res/values/dimens.xml
+++ b/android/TerminalApp/res/values/dimens.xml
@@ -1,3 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <resources>
     <dimen name="activity_split_ratio">0.3</dimen>
 </resources>
\ No newline at end of file
diff --git a/android/TerminalApp/res/values/ic_launcher_background.xml b/android/TerminalApp/res/values/ic_launcher_background.xml
index 337764a..8740b87 100644
--- a/android/TerminalApp/res/values/ic_launcher_background.xml
+++ b/android/TerminalApp/res/values/ic_launcher_background.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <resources>
     <color name="ic_launcher_background">#070E1E</color>
 </resources>
\ No newline at end of file
diff --git a/android/TerminalApp/res/values/integers.xml b/android/TerminalApp/res/values/integers.xml
index e20987c..dc64c81 100644
--- a/android/TerminalApp/res/values/integers.xml
+++ b/android/TerminalApp/res/values/integers.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <resources>
     <integer name="split_min_width">720</integer>
     <integer name="disk_size_round_up_step_size_in_mb">4</integer>
diff --git a/android/TerminalApp/res/values/strings.xml b/android/TerminalApp/res/values/strings.xml
index ca803ec..3448388 100644
--- a/android/TerminalApp/res/values/strings.xml
+++ b/android/TerminalApp/res/values/strings.xml
@@ -83,17 +83,17 @@
     <string name="settings_recovery_title">Recovery</string>
     <!-- Settings menu subtitle for recoverying image [CHAR LIMIT=none] -->
     <string name="settings_recovery_sub_title">Partition Recovery options</string>
-    <!-- Settings menu title for resetting the virtual machine image [CHAR LIMIT=none] -->
+    <!-- Settings menu title for resetting the terminal [CHAR LIMIT=none] -->
     <string name="settings_recovery_reset_title">Change to Initial version</string>
-    <!-- Settings menu subtitle for resetting the virtual machine image [CHAR LIMIT=none] -->
+    <!-- Settings menu subtitle for resetting the terminal [CHAR LIMIT=none] -->
     <string name="settings_recovery_reset_sub_title">Remove all</string>
-    <!-- Dialog title for restarting the terminal [CHAR LIMIT=none] -->
-    <string name="settings_recovery_reset_dialog_title">Reset the virtual machine</string>
-    <!-- Dialog message for restarting the terminal [CHAR LIMIT=none] -->
-    <string name="settings_recovery_reset_dialog_message">Data will be deleted.</string>
-    <!-- Dialog button confirm for restarting the terminal [CHAR LIMIT=16] -->
+    <!-- Dialog title for resetting the terminal [CHAR LIMIT=none] -->
+    <string name="settings_recovery_reset_dialog_title">Reset terminal</string>
+    <!-- Dialog message for resetting the terminal [CHAR LIMIT=none] -->
+    <string name="settings_recovery_reset_dialog_message">Data will be deleted</string>
+    <!-- Dialog button confirm for resetting the terminal [CHAR LIMIT=16] -->
     <string name="settings_recovery_reset_dialog_confirm">Confirm</string>
-    <!-- Dialog button cancel for restarting the terminal [CHAR LIMIT=16] -->
+    <!-- Dialog button cancel for resetting the terminal [CHAR LIMIT=16] -->
     <string name="settings_recovery_reset_dialog_cancel">Cancel</string>
 
     <!-- Notification action button for settings [CHAR LIMIT=none] -->
@@ -101,7 +101,7 @@
     <!-- Notification title for foreground service notification [CHAR LIMIT=none] -->
     <string name="service_notification_title">Terminal is running</string>
     <!-- Notification content for foreground service notification [CHAR LIMIT=none] -->
-    <string name="service_notification_content">Click to open the terminal.</string>
+    <string name="service_notification_content">Click to open the terminal</string>
     <!-- Notification action button for closing the virtual machine [CHAR LIMIT=none] -->
     <string name="service_notification_quit_action">Close</string>
 </resources>
diff --git a/android/TerminalApp/res/xml/main_split_config.xml b/android/TerminalApp/res/xml/main_split_config.xml
index f51e7ea..c2da907 100644
--- a/android/TerminalApp/res/xml/main_split_config.xml
+++ b/android/TerminalApp/res/xml/main_split_config.xml
@@ -1,3 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:window="http://schemas.android.com/apk/res-auto">
 
     <!-- Define a split for the named activities. -->
diff --git a/android/forwarder_host/src/forwarder_host.rs b/android/forwarder_host/src/forwarder_host.rs
index 26bae89..7496a02 100644
--- a/android/forwarder_host/src/forwarder_host.rs
+++ b/android/forwarder_host/src/forwarder_host.rs
@@ -28,7 +28,7 @@
 use std::time::Duration;
 
 use forwarder::forwarder::ForwarderSession;
-use jni::objects::{JObject, JValue};
+use jni::objects::{JIntArray, JObject, JValue};
 use jni::sys::jint;
 use jni::JNIEnv;
 use log::{debug, error, info, warn};
@@ -45,11 +45,16 @@
 static SHUTDOWN_EVT: LazyLock<EventFd> =
     LazyLock::new(|| EventFd::new().expect("Could not create shutdown eventfd"));
 
+static UPDATE_EVT: LazyLock<EventFd> =
+    LazyLock::new(|| EventFd::new().expect("Could not create update eventfd"));
+
+static UPDATE_QUEUE: LazyLock<Arc<Mutex<VecDeque<u16>>>> =
+    LazyLock::new(|| Arc::new(Mutex::new(VecDeque::new())));
+
 #[remain::sorted]
 #[derive(Debug)]
 enum Error {
     BindVsock(io::Error),
-    EventFdNew(nix::Error),
     IncorrectCid(u32),
     LaunchForwarderGuest(jni::errors::Error),
     NoListenerForPort(u16),
@@ -62,7 +67,6 @@
     TcpAccept(io::Error),
     TcpListenerPort(io::Error),
     UpdateEventRead(nix::Error),
-    UpdateEventWrite(nix::Error),
     VsockAccept(io::Error),
     VsockAcceptTimeout,
     VsockListenerPort(io::Error),
@@ -78,7 +82,6 @@
         #[remain::sorted]
         match self {
             BindVsock(e) => write!(f, "failed to bind vsock: {}", e),
-            EventFdNew(e) => write!(f, "failed to create eventfd: {}", e),
             IncorrectCid(cid) => write!(f, "chunnel connection from unexpected cid {}", cid),
             LaunchForwarderGuest(e) => write!(f, "failed to launch forwarder_guest {}", e),
             NoListenerForPort(port) => write!(f, "could not find listener for port: {}", port),
@@ -93,7 +96,6 @@
                 write!(f, "failed to read local sockaddr for tcp listener: {}", e)
             }
             UpdateEventRead(e) => write!(f, "failed to read update eventfd: {}", e),
-            UpdateEventWrite(e) => write!(f, "failed to write update eventfd: {}", e),
             VsockAccept(e) => write!(f, "failed to accept vsock: {}", e),
             VsockAcceptTimeout => write!(f, "timed out waiting for vsock connection"),
             VsockListenerPort(e) => write!(f, "failed to get vsock listener port: {}", e),
@@ -101,12 +103,6 @@
     }
 }
 
-/// A TCP forwarding target. Uniquely identifies a listening port in a given container.
-struct TcpForwardTarget {
-    pub port: u16,
-    pub vsock_cid: u32,
-}
-
 /// A tag that uniquely identifies a particular forwarding session. This has arbitrarily been
 /// chosen as the fd of the local (TCP) socket.
 type SessionTag = u32;
@@ -127,7 +123,6 @@
 struct PortListeners {
     tcp4_listener: TcpListener,
     tcp6_listener: TcpListener,
-    forward_target: TcpForwardTarget,
 }
 
 /// SocketFamily specifies whether a socket uses IPv4 or IPv6.
@@ -140,25 +135,18 @@
 struct ForwarderSessions<'a> {
     listening_ports: BTreeMap<u16, PortListeners>,
     tcp4_forwarders: HashMap<SessionTag, ForwarderSession>,
-    update_evt: EventFd,
-    update_queue: Arc<Mutex<VecDeque<TcpForwardTarget>>>,
+    cid: u32,
     jni_env: JNIEnv<'a>,
     jni_cb: JObject<'a>,
 }
 
 impl<'a> ForwarderSessions<'a> {
     /// Creates a new instance of ForwarderSessions.
-    fn new(
-        update_evt: EventFd,
-        update_queue: Arc<Mutex<VecDeque<TcpForwardTarget>>>,
-        jni_env: JNIEnv<'a>,
-        jni_cb: JObject<'a>,
-    ) -> Result<Self> {
+    fn new(cid: i32, jni_env: JNIEnv<'a>, jni_cb: JObject<'a>) -> Result<Self> {
         Ok(ForwarderSessions {
             listening_ports: BTreeMap::new(),
             tcp4_forwarders: HashMap::new(),
-            update_evt,
-            update_queue,
+            cid: cid as u32,
             jni_env,
             jni_cb,
         })
@@ -167,12 +155,11 @@
     /// Adds or removes listeners based on the latest listening ports from the D-Bus thread.
     fn process_update_queue(&mut self, poll_ctx: &PollContext<Token>) -> Result<()> {
         // Unwrap of LockResult is customary.
-        let mut update_queue = self.update_queue.lock().unwrap();
+        let mut update_queue = UPDATE_QUEUE.lock().unwrap();
         let mut active_ports: BTreeSet<u16> = BTreeSet::new();
 
         // Add any new listeners first.
-        while let Some(target) = update_queue.pop_front() {
-            let port = target.port;
+        while let Some(port) = update_queue.pop_front() {
             // Ignore privileged ports.
             if port < 1024 {
                 continue;
@@ -201,7 +188,7 @@
                 poll_ctx
                     .add(&tcp6_listener, Token::Ipv6Listener(port))
                     .map_err(Error::PollContextAdd)?;
-                o.insert(PortListeners { tcp4_listener, tcp6_listener, forward_target: target });
+                o.insert(PortListeners { tcp4_listener, tcp6_listener });
             }
             active_ports.insert(port);
         }
@@ -218,7 +205,7 @@
         }
 
         // Consume the eventfd.
-        self.update_evt.read().map_err(Error::UpdateEventRead)?;
+        UPDATE_EVT.read().map_err(Error::UpdateEventRead)?;
 
         Ok(())
     }
@@ -240,12 +227,8 @@
         // This session should be dropped if any of the PollContext setup fails. Since the only
         // extant fds for the underlying sockets will be closed, they will be unregistered from
         // epoll set automatically.
-        let session = create_forwarder_session(
-            listener,
-            &port_listeners.forward_target,
-            &mut self.jni_env,
-            &self.jni_cb,
-        )?;
+        let session =
+            create_forwarder_session(listener, self.cid, &mut self.jni_env, &self.jni_cb)?;
 
         let tag = session.local_stream().as_raw_fd() as u32;
 
@@ -293,7 +276,7 @@
 
     fn run(&mut self) -> Result<()> {
         let poll_ctx: PollContext<Token> = PollContext::new().map_err(Error::PollContextNew)?;
-        poll_ctx.add(&self.update_evt, Token::UpdatePorts).map_err(Error::PollContextAdd)?;
+        poll_ctx.add(&*UPDATE_EVT, Token::UpdatePorts).map_err(Error::PollContextAdd)?;
         poll_ctx.add(&*SHUTDOWN_EVT, Token::Shutdown).map_err(Error::PollContextAdd)?;
 
         loop {
@@ -340,7 +323,7 @@
 /// Creates a forwarder session from a `listener` that has a pending connection to accept.
 fn create_forwarder_session(
     listener: &TcpListener,
-    target: &TcpForwardTarget,
+    cid: u32,
     jni_env: &mut JNIEnv,
     jni_cb: &JObject,
 ) -> Result<ForwarderSession> {
@@ -376,7 +359,7 @@
         Some(_) => {
             let (vsock_stream, sockaddr) = vsock_listener.accept().map_err(Error::VsockAccept)?;
 
-            if sockaddr.cid() != target.vsock_cid {
+            if sockaddr.cid() != cid {
                 Err(Error::IncorrectCid(sockaddr.cid()))
             } else {
                 Ok(ForwarderSession::new(tcp_stream.into(), vsock_stream.into()))
@@ -386,33 +369,10 @@
     }
 }
 
-fn update_listening_ports(
-    update_queue: &Arc<Mutex<VecDeque<TcpForwardTarget>>>,
-    update_evt: &EventFd,
-    cid: i32,
-) -> Result<()> {
-    let mut update_queue = update_queue.lock().unwrap();
-
-    // TODO(b/340126051): Bring listening ports from the guest.
-    update_queue.push_back(TcpForwardTarget {
-        port: 12345, /* Example value for testing */
-        vsock_cid: cid as u32,
-    });
-
-    update_evt.write(1).map_err(Error::UpdateEventWrite)?;
-    Ok(())
-}
-
 // TODO(b/340126051): Host can receive opened ports from the guest.
 fn run_forwarder_host(cid: i32, jni_env: JNIEnv, jni_cb: JObject) -> Result<()> {
     debug!("Starting forwarder_host");
-    let update_evt = EventFd::new().map_err(Error::EventFdNew)?;
-    let update_queue = Arc::new(Mutex::new(VecDeque::new()));
-
-    // TODO(b/340126051): Instead of one-time execution, bring port info with separated thread.
-    update_listening_ports(&update_queue, &update_evt, cid)?;
-
-    let mut sessions = ForwarderSessions::new(update_evt, update_queue, jni_env, jni_cb)?;
+    let mut sessions = ForwarderSessions::new(cid, jni_env, jni_cb)?;
     sessions.run()
 }
 
@@ -442,3 +402,22 @@
 ) {
     SHUTDOWN_EVT.write(1).expect("Failed to write shutdown event FD");
 }
+
+/// JNI function for updating listening ports.
+#[no_mangle]
+pub extern "C" fn Java_com_android_virtualization_vmlauncher_DebianServiceImpl_updateListeningPorts(
+    env: JNIEnv,
+    _class: JObject,
+    ports: JIntArray,
+) {
+    let length = env.get_array_length(&ports).expect("Failed to get length of port array");
+    let mut buf = vec![0; length as usize];
+    env.get_int_array_region(ports, 0, &mut buf).expect("Failed to get port array");
+
+    let mut update_queue = UPDATE_QUEUE.lock().unwrap();
+    update_queue.clear();
+    for port in buf {
+        update_queue.push_back(port.try_into().expect("Failed to add port into update queue"));
+    }
+    UPDATE_EVT.write(1).expect("failed to write update eventfd");
+}
diff --git a/build/debian/build.sh b/build/debian/build.sh
index 7fc9035..899e376 100755
--- a/build/debian/build.sh
+++ b/build/debian/build.sh
@@ -11,6 +11,7 @@
 	echo "Options:"
 	echo "-h         Print usage and this help message and exit."
 	echo "-a ARCH    Architecture of the image [default is aarch64]"
+	echo "-r         Release mode build"
 }
 
 check_sudo() {
@@ -21,7 +22,7 @@
 }
 
 parse_options() {
-	while getopts "ha:" option; do
+	while getopts "hra:" option; do
 		case ${option} in
 			h)
 				show_help
@@ -36,6 +37,9 @@
 					debian_arch="amd64"
 				fi
 				;;
+			r)
+				mode=release
+				;;
 			*)
 				echo "Invalid option: $OPTARG"
 				exit
@@ -119,11 +123,17 @@
 
 build_rust_binary_and_copy() {
 	pushd "$(dirname "$0")/../../guest/$1" > /dev/null
+	local release_flag=
+	local artifact_mode=debug
+	if [[ "$mode" == "release" ]]; then
+		release_flag="--release"
+		artifact_mode=release
+	fi
 	RUSTFLAGS="-C linker=${arch}-linux-gnu-gcc" cargo build \
 		--target "${arch}-unknown-linux-gnu" \
-		--target-dir "${workdir}/$1"
+		--target-dir "${workdir}/$1" ${release_flag}
 	mkdir -p "${dst}/files/usr/local/bin/$1"
-	cp "${workdir}/$1/${arch}-unknown-linux-gnu/debug/$1" "${dst}/files/usr/local/bin/$1/AVF"
+	cp "${workdir}/$1/${arch}-unknown-linux-gnu/${artifact_mode}/$1" "${dst}/files/usr/local/bin/$1/AVF"
 	chmod 777 "${dst}/files/usr/local/bin/$1/AVF"
 
 	mkdir -p "${dst}/files/usr/share/doc/$1"
@@ -197,6 +207,7 @@
 resources_dir=${debian_cloud_image}/src/debian_cloud_images/resources
 arch=aarch64
 debian_arch=arm64
+mode=debug
 parse_options "$@"
 check_sudo
 install_prerequisites
diff --git a/build/debian/build_in_container.sh b/build/debian/build_in_container.sh
index fd1a975..d5680e0 100755
--- a/build/debian/build_in_container.sh
+++ b/build/debian/build_in_container.sh
@@ -3,7 +3,8 @@
 if [ -z "$ANDROID_BUILD_TOP" ]; then echo "forgot to source build/envsetup.sh?" && exit 1; fi
 
 arch=aarch64
-while getopts "a:" option; do
+release_flag=
+while getopts "ra:" option; do
   case ${option} in
     a)
       if [[ "$OPTARG" != "aarch64" && "$OPTARG" != "x86_64" ]]; then
@@ -12,6 +13,9 @@
       fi
       arch="$OPTARG"
       ;;
+    r)
+      release_flag="-r"
+      ;;
     *)
       echo "Invalid option: $OPTARG"
       exit
@@ -21,4 +25,4 @@
 
 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"
+  /dev:/dev ubuntu:22.04 /root/Virtualization/build/debian/build.sh -a "$arch" $release_flag
diff --git a/build/debian/fai_config/files/etc/systemd/system/forwarder_guest_launcher.service/AVF b/build/debian/fai_config/files/etc/systemd/system/forwarder_guest_launcher.service/AVF
index f4c2a24..6dbabea 100644
--- a/build/debian/fai_config/files/etc/systemd/system/forwarder_guest_launcher.service/AVF
+++ b/build/debian/fai_config/files/etc/systemd/system/forwarder_guest_launcher.service/AVF
@@ -4,7 +4,7 @@
 After=network.target
 After=virtiofs_internal.service
 [Service]
-ExecStart=/usr/bin/bash -c '/usr/local/bin/forwarder_guest_launcher --host 192.168.0.1 --grpc_port $(cat /mnt/internal/debian_service_port)'
+ExecStart=/usr/bin/bash -c 'RUST_LOG=debug /usr/local/bin/forwarder_guest_launcher --host 192.168.0.1 --grpc_port $(cat /mnt/internal/debian_service_port)'
 Type=simple
 Restart=on-failure
 RestartSec=1
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 a2516ff..4a32f2b 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
@@ -4,7 +4,7 @@
 After=network.target
 After=virtiofs_internal.service
 [Service]
-ExecStart=/usr/local/bin/ttyd --ssl --ssl-cert /etc/ttyd/server.crt --ssl-key /etc/ttyd/server.key --ssl-ca /mnt/internal/ca.crt -W login -f droid
+ExecStart=/usr/local/bin/ttyd --ssl --ssl-cert /etc/ttyd/server.crt --ssl-key /etc/ttyd/server.key --ssl-ca /mnt/internal/ca.crt -t disableLeaveAlert=true -W login -f droid
 Type=simple
 Restart=always
 User=root
diff --git a/build/debian/fai_config/package_config/AVF b/build/debian/fai_config/package_config/AVF
index 7d86d41..1be57fe 100644
--- a/build/debian/fai_config/package_config/AVF
+++ b/build/debian/fai_config/package_config/AVF
@@ -1,4 +1,3 @@
 PACKAGES install
 
-# Just for testing
-tree
+procps
diff --git a/build/debian/image.yaml b/build/debian/image.yaml
index eb42a07..93ec273 100644
--- a/build/debian/image.yaml
+++ b/build/debian/image.yaml
@@ -46,7 +46,7 @@
 vendors:
 - name: nocloud
   faiClasses: [SYSTEM_BOOT, NOCLOUD, LINUX_VARIANT_BASE, TIME_SYSTEMD, AVF]
-  size: 2
+  size: 6
 
 types:
 - name: dev
diff --git a/build/debian/vm_config.json.aarch64 b/build/debian/vm_config.json.aarch64
index bbe590f..d41a29c 100644
--- a/build/debian/vm_config.json.aarch64
+++ b/build/debian/vm_config.json.aarch64
@@ -33,7 +33,6 @@
     "memory_mib": 4096,
     "debuggable": true,
     "console_out": true,
-    "connect_console": true,
     "console_input_device": "ttyS0",
     "network": true
 }
diff --git a/build/debian/vm_config.json.x86_64 b/build/debian/vm_config.json.x86_64
index 1719815..d338080 100644
--- a/build/debian/vm_config.json.x86_64
+++ b/build/debian/vm_config.json.x86_64
@@ -24,7 +24,6 @@
     "memory_mib": 4096,
     "debuggable": true,
     "console_out": true,
-    "connect_console": true,
     "console_input_device": "ttyS0",
     "network": true
 }
diff --git a/build/microdroid/Android.bp b/build/microdroid/Android.bp
index d5d8108..7f23ae6 100644
--- a/build/microdroid/Android.bp
+++ b/build/microdroid/Android.bp
@@ -139,7 +139,10 @@
             ],
         },
     },
-    linker_config_src: "linker.config.json",
+    linker_config: {
+        gen_linker_config: true,
+        linker_config_srcs: ["linker.config.json"],
+    },
     base_dir: "system",
     dirs: microdroid_rootdirs + select(release_flag("RELEASE_AVF_ENABLE_DICE_CHANGES"), {
         true: ["microdroid_resources"],
diff --git a/guest/forwarder_guest_launcher/Cargo.toml b/guest/forwarder_guest_launcher/Cargo.toml
index b7f9eaf..03fda56 100644
--- a/guest/forwarder_guest_launcher/Cargo.toml
+++ b/guest/forwarder_guest_launcher/Cargo.toml
@@ -7,6 +7,8 @@
 [dependencies]
 anyhow = "1.0.91"
 clap = { version = "4.5.20", features = ["derive"] }
+env_logger = "0.11.5"
+log = "0.4.22"
 prost = "0.13.3"
 tokio = { version = "1.40.0", features = ["process", "rt-multi-thread"] }
 tonic = "0.12.3"
diff --git a/guest/forwarder_guest_launcher/src/main.rs b/guest/forwarder_guest_launcher/src/main.rs
index d753d19..abb39f6 100644
--- a/guest/forwarder_guest_launcher/src/main.rs
+++ b/guest/forwarder_guest_launcher/src/main.rs
@@ -18,6 +18,7 @@
 use clap::Parser;
 use debian_service::debian_service_client::DebianServiceClient;
 use debian_service::QueueOpeningRequest;
+use log::debug;
 use tokio::process::Command;
 use tonic::transport::Endpoint;
 use tonic::Request;
@@ -41,7 +42,8 @@
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
-    println!("Starting forwarder_guest_launcher");
+    env_logger::init();
+    debug!("Starting forwarder_guest_launcher");
     let args = Args::parse();
     let addr = format!("https://{}:{}", args.host_addr, args.grpc_port);
 
@@ -58,7 +60,7 @@
             .context("Failed to convert guest_tcp_port as i16")?;
         let vsock_port = response.vsock_port as u32;
 
-        println!(
+        debug!(
             "executing forwarder_guest with guest_tcp_port: {:?}, vsock_port: {:?}",
             &tcp_port, &vsock_port
         );
diff --git a/guest/port_listener/build.sh b/guest/port_listener/build.sh
deleted file mode 100755
index a1d0205..0000000
--- a/guest/port_listener/build.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/bash
-
-set -e
-
-check_sudo() {
-	if [ "$EUID" -ne 0 ]; then
-		echo "Please run as root."
-		exit
-	fi
-}
-
-install_prerequisites() {
-    apt update
-    apt install --no-install-recommends --assume-yes \
-        bpftool \
-        clang \
-        libbpf-dev \
-        libgoogle-glog-dev \
-        libstdc++-14-dev
-}
-
-build_port_listener() {
-    cp $(dirname $0)/src/* ${workdir}
-    out_dir=${PWD}
-    pushd ${workdir}
-        bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
-        clang \
-            -O2 \
-            -Wall \
-            -target bpf \
-            -g \
-            -c listen_tracker.ebpf.c \
-            -o listen_tracker.ebpf.o
-        bpftool gen skeleton listen_tracker.ebpf.o > listen_tracker.skel.h
-        clang++ \
-            -O2 \
-            -Wall \
-            -lbpf \
-            -lglog \
-            -o port_listener \
-            main.cc
-        cp port_listener ${out_dir}
-    popd
-}
-
-clean_up() {
-	rm -rf ${workdir}
-}
-trap clean_up EXIT
-workdir=$(mktemp -d)
-
-check_sudo
-install_prerequisites
-build_port_listener
diff --git a/guest/port_listener/src/common.h b/guest/port_listener/src/common.h
deleted file mode 100644
index d6e507c..0000000
--- a/guest/port_listener/src/common.h
+++ /dev/null
@@ -1,31 +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.
-
-// Copied from ChromiumOS with relicensing:
-// src/platform2/vm_tools/port_listener/common.h
-
-#ifndef VM_TOOLS_PORT_LISTENER_COMMON_H_
-#define VM_TOOLS_PORT_LISTENER_COMMON_H_
-
-enum State {
-    kPortListenerUp,
-    kPortListenerDown,
-};
-
-struct event {
-    enum State state;
-    uint16_t port;
-};
-
-#endif // VM_TOOLS_PORT_LISTENER_COMMON_H_
diff --git a/guest/port_listener/src/listen_tracker.ebpf.c b/guest/port_listener/src/listen_tracker.ebpf.c
deleted file mode 100644
index 9e98aad..0000000
--- a/guest/port_listener/src/listen_tracker.ebpf.c
+++ /dev/null
@@ -1,80 +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.
-
-// Copied from ChromiumOS with relicensing:
-// src/platform2/vm_tools/port_listener/listen_tracker.ebpf.c
-
-// bpf_helpers.h uses types defined here
-#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
-// well before 6.1.
-#define BPF_F_NO_PREALLOC (1U << 0)
-#define BPF_ANY 0
-
-struct {
-    __uint(type, BPF_MAP_TYPE_RINGBUF);
-    __uint(max_entries, 1 << 24);
-} events SEC(".maps");
-
-struct {
-    __uint(type, BPF_MAP_TYPE_HASH);
-    __type(key, struct sock*);
-    __type(value, __u8);
-    __uint(max_entries, 65535);
-    __uint(map_flags, BPF_F_NO_PREALLOC);
-} sockmap SEC(".maps");
-
-const __u8 set_value = 0;
-
-SEC("tp/sock/inet_sock_set_state")
-int tracepoint_inet_sock_set_state(struct trace_event_raw_inet_sock_set_state* ctx) {
-    // We don't support anything other than TCP.
-    if (ctx->protocol != IPPROTO_TCP) {
-        return 0;
-    }
-    struct sock* sk = (struct sock*)ctx->skaddr;
-    // If we're transitioning away from LISTEN but we don't know about this
-    // socket yet then don't report anything.
-    if (ctx->oldstate == BPF_TCP_LISTEN && bpf_map_lookup_elem(&sockmap, &sk) == NULL) {
-        return 0;
-    }
-    // If we aren't transitioning to or from TCP_LISTEN then we don't care.
-    if (ctx->newstate != BPF_TCP_LISTEN && ctx->oldstate != BPF_TCP_LISTEN) {
-        return 0;
-    }
-
-    struct event* ev;
-    ev = bpf_ringbuf_reserve(&events, sizeof(*ev), 0);
-    if (!ev) {
-        return 0;
-    }
-    ev->port = ctx->sport;
-
-    if (ctx->newstate == BPF_TCP_LISTEN) {
-        bpf_map_update_elem(&sockmap, &sk, &set_value, BPF_ANY);
-        ev->state = kPortListenerUp;
-    }
-    if (ctx->oldstate == BPF_TCP_LISTEN) {
-        bpf_map_delete_elem(&sockmap, &sk);
-        ev->state = kPortListenerDown;
-    }
-    bpf_ringbuf_submit(ev, 0);
-
-    return 0;
-}
diff --git a/guest/port_listener/src/main.cc b/guest/port_listener/src/main.cc
deleted file mode 100644
index 1caceaf..0000000
--- a/guest/port_listener/src/main.cc
+++ /dev/null
@@ -1,167 +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.
-
-// Copied from ChromiumOS with relicensing:
-// src/platform2/vm_tools/port_listener/main.cc
-
-#include <bpf/libbpf.h>
-#include <bpf/libbpf_legacy.h>
-#include <glog/logging.h>
-#include <linux/vm_sockets.h> // Needs to come after sys/socket.h
-#include <sys/socket.h>
-
-#include <memory>
-#include <unordered_map>
-
-#include "common.h"
-#include "listen_tracker.skel.h"
-
-typedef std::unordered_map<int, int> port_usage_map;
-
-namespace port_listener {
-namespace {
-
-int HandleEvent(void* ctx, void* const data, size_t size) {
-    port_usage_map* map = reinterpret_cast<port_usage_map*>(ctx);
-    const struct event* ev = (struct event*)data;
-
-    switch (ev->state) {
-        case kPortListenerUp:
-            (*map)[ev->port]++;
-            break;
-
-        case kPortListenerDown:
-            if ((*map)[ev->port] > 0) {
-                (*map)[ev->port]--;
-            } else {
-                LOG(INFO) << "Received down event while port count was 0; ignoring";
-            }
-
-            break;
-
-        default:
-            LOG(ERROR) << "Unknown event state " << ev->state;
-    }
-
-    LOG(INFO) << "Listen event: port=" << ev->port << " state=" << ev->state;
-
-    return 0;
-}
-
-typedef std::unique_ptr<struct ring_buffer, decltype(&ring_buffer__free)> ring_buffer_ptr;
-typedef std::unique_ptr<listen_tracker_ebpf, decltype(&listen_tracker_ebpf__destroy)>
-        listen_tracker_ptr;
-
-// BPFProgram tracks the state and resources of the listen_tracker BPF program.
-class BPFProgram {
-public:
-    // Default movable but not copyable.
-    BPFProgram(BPFProgram&& other) = default;
-    BPFProgram(const BPFProgram& other) = delete;
-    BPFProgram& operator=(BPFProgram&& other) = default;
-    BPFProgram& operator=(const BPFProgram& other) = delete;
-
-    // Load loads the listen_tracker BPF program and prepares it for polling. On
-    // error nullptr is returned.
-    static std::unique_ptr<BPFProgram> Load() {
-        auto* skel = listen_tracker_ebpf__open();
-        if (!skel) {
-            PLOG(ERROR) << "Failed to open listen_tracker BPF skeleton";
-            return nullptr;
-        }
-        listen_tracker_ptr skeleton(skel, listen_tracker_ebpf__destroy);
-
-        int err = listen_tracker_ebpf__load(skeleton.get());
-        if (err) {
-            PLOG(ERROR) << "Failed to load listen_tracker BPF program";
-            return nullptr;
-        }
-
-        auto map = std::make_unique<port_usage_map>();
-        auto* rb = ring_buffer__new(bpf_map__fd(skel->maps.events), HandleEvent, map.get(), NULL);
-        if (!rb) {
-            PLOG(ERROR) << "Failed to open ring buffer for listen_tracker";
-            return nullptr;
-        }
-        ring_buffer_ptr ringbuf(rb, ring_buffer__free);
-
-        err = listen_tracker_ebpf__attach(skeleton.get());
-        if (err) {
-            PLOG(ERROR) << "Failed to attach listen_tracker";
-            return nullptr;
-        }
-
-        return std::unique_ptr<BPFProgram>(
-                new BPFProgram(std::move(skeleton), std::move(ringbuf), std::move(map)));
-    }
-
-    // Poll waits for the listen_tracker BPF program to post a new event to the
-    // ring buffer. BPFProgram handles integrating this new event into the
-    // port_usage map and callers should consult port_usage() after Poll returns
-    // for the latest data.
-    const bool Poll() {
-        int err = ring_buffer__poll(rb_.get(), -1);
-        if (err < 0) {
-            LOG(ERROR) << "Error polling ring buffer ret=" << err;
-            return false;
-        }
-
-        return true;
-    }
-
-    const port_usage_map& port_usage() { return *port_usage_; }
-
-private:
-    BPFProgram(listen_tracker_ptr&& skeleton, ring_buffer_ptr&& rb,
-               std::unique_ptr<port_usage_map>&& port_usage)
-          : skeleton_(std::move(skeleton)),
-            rb_(std::move(rb)),
-            port_usage_(std::move(port_usage)) {}
-
-    listen_tracker_ptr skeleton_;
-    ring_buffer_ptr rb_;
-    std::unique_ptr<port_usage_map> port_usage_;
-};
-
-} // namespace
-} // namespace port_listener
-
-int main(int argc, char** argv) {
-    google::InitGoogleLogging(argv[0]);
-    libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
-
-    // Load our BPF program.
-    auto program = port_listener::BPFProgram::Load();
-    if (program == nullptr) {
-        LOG(ERROR) << "Failed to load BPF program";
-        return EXIT_FAILURE;
-    }
-
-    // main loop: poll for listen updates
-    for (;;) {
-        if (!program->Poll()) {
-            LOG(ERROR) << "Failure while polling BPF program";
-            return EXIT_FAILURE;
-        }
-        // port_usage will be updated with the latest usage data
-
-        for (auto it : program->port_usage()) {
-            if (it.second <= 0) {
-                continue;
-            }
-            // TODO(b/340126051): Add listening TCP4 ports.
-        }
-        // TODO(b/340126051): Notify port information to the guest agent.
-    }
-}
diff --git a/guest/pvmfw/Android.bp b/guest/pvmfw/Android.bp
index a5b7494..477d0a8 100644
--- a/guest/pvmfw/Android.bp
+++ b/guest/pvmfw/Android.bp
@@ -19,6 +19,7 @@
         "libcoset_nostd",
         "libcstr",
         "libdiced_open_dice_nostd",
+        "libhypervisor_backends",
         "liblibfdt_nostd",
         "liblog_rust_nostd",
         "libpvmfw_avb_nostd",
diff --git a/guest/pvmfw/src/device_assignment.rs b/guest/pvmfw/src/device_assignment.rs
index 9b55cff..f37f443 100644
--- a/guest/pvmfw/src/device_assignment.rs
+++ b/guest/pvmfw/src/device_assignment.rs
@@ -28,12 +28,12 @@
 use core::iter::Iterator;
 use core::mem;
 use core::ops::Range;
+// TODO(b/308694211): Use hypervisor_backends::{DeviceAssigningHypervisor, Error} proper for tests.
+#[cfg(not(test))]
+use hypervisor_backends::DeviceAssigningHypervisor;
 use libfdt::{Fdt, FdtError, FdtNode, FdtNodeMut, Phandle, Reg};
 use log::error;
 use log::warn;
-// TODO(b/308694211): Use vmbase::hyp::{DeviceAssigningHypervisor, Error} proper for tests.
-#[cfg(not(test))]
-use vmbase::hyp::DeviceAssigningHypervisor;
 use zerocopy::byteorder::big_endian::U32;
 use zerocopy::FromBytes as _;
 
diff --git a/guest/pvmfw/src/entry.rs b/guest/pvmfw/src/entry.rs
index 64a03cc..ce911b8 100644
--- a/guest/pvmfw/src/entry.rs
+++ b/guest/pvmfw/src/entry.rs
@@ -20,6 +20,7 @@
 use core::mem::{drop, size_of};
 use core::ops::Range;
 use core::slice;
+use hypervisor_backends::get_mmio_guard;
 use log::error;
 use log::info;
 use log::warn;
@@ -28,7 +29,6 @@
 use vmbase::{
     arch::aarch64::min_dcache_line_size,
     configure_heap, console_writeln,
-    hyp::get_mmio_guard,
     layout::{self, crosvm, UART_PAGE_ADDR},
     main,
     memory::{MemoryTracker, MEMORY, SIZE_128KB, SIZE_4KB},
diff --git a/guest/pvmfw/src/fdt.rs b/guest/pvmfw/src/fdt.rs
index 0381f3e..6bbb05e 100644
--- a/guest/pvmfw/src/fdt.rs
+++ b/guest/pvmfw/src/fdt.rs
@@ -30,6 +30,8 @@
 use core::mem::size_of;
 use core::ops::Range;
 use cstr::cstr;
+use hypervisor_backends::get_device_assigner;
+use hypervisor_backends::get_mem_sharer;
 use libfdt::AddressRange;
 use libfdt::CellIterator;
 use libfdt::Fdt;
@@ -46,7 +48,6 @@
 use vmbase::fdt::pci::PciMemoryFlags;
 use vmbase::fdt::pci::PciRangeType;
 use vmbase::fdt::SwiotlbInfo;
-use vmbase::hyp;
 use vmbase::layout::{crosvm::MEM_START, MAX_VIRT_ADDR};
 use vmbase::memory::SIZE_4KB;
 use vmbase::util::RangeExt as _;
@@ -1147,9 +1148,9 @@
 
     let device_assignment = match vm_dtbo {
         Some(vm_dtbo) => {
-            if let Some(hypervisor) = hyp::get_device_assigner() {
+            if let Some(hypervisor) = get_device_assigner() {
                 // TODO(ptosi): Cache the (single?) granule once, in vmbase.
-                let granule = hyp::get_mem_sharer()
+                let granule = get_mem_sharer()
                     .ok_or_else(|| {
                         error!("No MEM_SHARE found during device assignment validation");
                         RebootReason::InternalError
diff --git a/guest/pvmfw/src/memory.rs b/guest/pvmfw/src/memory.rs
index f49d79b..8e8b338 100644
--- a/guest/pvmfw/src/memory.rs
+++ b/guest/pvmfw/src/memory.rs
@@ -23,12 +23,12 @@
 use core::ops::Range;
 use core::result;
 use core::slice;
+use hypervisor_backends::get_mem_sharer;
 use log::debug;
 use log::error;
 use log::info;
 use log::warn;
 use vmbase::{
-    hyp::get_mem_sharer,
     layout::{self, crosvm},
     memory::{PageTable, MEMORY, SIZE_2MB, SIZE_4KB},
     util::align_up,
diff --git a/guest/rialto/Android.bp b/guest/rialto/Android.bp
index 7bcfd54..8afb8ba 100644
--- a/guest/rialto/Android.bp
+++ b/guest/rialto/Android.bp
@@ -14,6 +14,7 @@
         "libciborium_nostd",
         "libcstr",
         "libdiced_open_dice_nostd",
+        "libhypervisor_backends",
         "liblibfdt_nostd",
         "liblog_rust_nostd",
         "libservice_vm_comm_nostd",
diff --git a/guest/rialto/src/error.rs b/guest/rialto/src/error.rs
index ba5f4b0..f021c36 100644
--- a/guest/rialto/src/error.rs
+++ b/guest/rialto/src/error.rs
@@ -17,11 +17,10 @@
 use aarch64_paging::MapError;
 use core::{fmt, result};
 use diced_open_dice::DiceError;
+use hypervisor_backends::Error as HypervisorError;
 use libfdt::FdtError;
 use service_vm_comm::RequestProcessingError;
-use vmbase::{
-    fdt::pci::PciError, hyp::Error as HypervisorError, memory::MemoryTrackerError, virtio::pci,
-};
+use vmbase::{fdt::pci::PciError, memory::MemoryTrackerError, virtio::pci};
 
 pub type Result<T> = result::Result<T, Error>;
 
diff --git a/guest/rialto/src/main.rs b/guest/rialto/src/main.rs
index ec9a76e..244010d 100644
--- a/guest/rialto/src/main.rs
+++ b/guest/rialto/src/main.rs
@@ -32,6 +32,7 @@
 use core::num::NonZeroUsize;
 use core::slice;
 use diced_open_dice::{bcc_handover_parse, DiceArtifacts};
+use hypervisor_backends::get_mem_sharer;
 use libfdt::FdtError;
 use log::{debug, error, info};
 use service_vm_comm::{ServiceVmRequest, VmType};
@@ -47,7 +48,6 @@
     fdt::pci::PciInfo,
     fdt::SwiotlbInfo,
     generate_image_header,
-    hyp::get_mem_sharer,
     layout::{self, crosvm},
     main,
     memory::{MemoryTracker, PageTable, MEMORY, PAGE_SIZE, SIZE_128KB},
diff --git a/guest/trusty/security_vm/launcher/Android.bp b/guest/trusty/security_vm/launcher/Android.bp
index ff628fd..e482e02 100644
--- a/guest/trusty/security_vm/launcher/Android.bp
+++ b/guest/trusty/security_vm/launcher/Android.bp
@@ -18,3 +18,40 @@
         false: false,
     }),
 }
+
+prebuilt_etc {
+    name: "lk_trusty.elf",
+    system_ext_specific: true,
+    relative_install_path: "vm/trusty_vm",
+    filename: "lk_trusty.elf",
+    arch: {
+        x86_64: {
+            src: ":trusty_security_vm_signed",
+        },
+    },
+    src: ":empty_file",
+}
+
+filegroup {
+    name: "trusty_vm_sign_key",
+    srcs: [":avb_testkey_rsa4096"],
+}
+
+// python -c "import hashlib; print(hashlib.sha256(b'trusty_security_vm_salt').hexdigest())"
+trusty_security_vm_salt = "75a71e967c1a1e0f805cca20465e7acf83e6a04e567a67c426d8b5a94f8d61c5"
+
+avb_add_hash_footer {
+    name: "trusty_security_vm_signed",
+    filename: "trusty_security_vm_signed",
+    partition_name: "boot",
+    private_key: ":trusty_vm_sign_key",
+    salt: trusty_security_vm_salt,
+    src: ":empty_file",
+    enabled: false,
+    arch: {
+        x86_64: {
+            src: ":trusty-test-lk.elf",
+            enabled: true,
+        },
+    },
+}
diff --git a/libs/debian_service/proto/DebianService.proto b/libs/debian_service/proto/DebianService.proto
index a887bf2..bf05ebe 100644
--- a/libs/debian_service/proto/DebianService.proto
+++ b/libs/debian_service/proto/DebianService.proto
@@ -22,6 +22,7 @@
 option java_multiple_files = true;
 
 service DebianService {
+  rpc ReportVmActivePorts (ReportVmActivePortsRequest) returns (ReportVmActivePortsResponse) {}
   rpc ReportVmIpAddr (IpAddr) returns (ReportVmIpAddrResponse) {}
   rpc OpenForwardingRequestQueue (QueueOpeningRequest) returns (stream ForwardingRequestItem) {}
 }
@@ -38,6 +39,14 @@
   bool success = 1;
 }
 
+message ReportVmActivePortsRequest {
+  repeated int32 ports = 1;
+}
+
+message ReportVmActivePortsResponse {
+  bool success = 1;
+}
+
 message ForwardingRequestItem {
   int32 guest_tcp_port = 1;
   int32 vsock_port = 2;
diff --git a/libs/dice/OWNERS b/libs/dice/OWNERS
deleted file mode 100644
index fbc501d..0000000
--- a/libs/dice/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-ascull@google.com
diff --git a/libs/libhypervisor_backends/Android.bp b/libs/libhypervisor_backends/Android.bp
new file mode 100644
index 0000000..b001b8f
--- /dev/null
+++ b/libs/libhypervisor_backends/Android.bp
@@ -0,0 +1,35 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library_rlib {
+    name: "libhypervisor_backends",
+    crate_name: "hypervisor_backends",
+    defaults: ["avf_build_flags_rust"],
+    edition: "2021",
+    prefer_rlib: true,
+    host_supported: false,
+    no_stdlibs: true,
+    srcs: ["src/lib.rs"],
+    rustlibs: [
+        "libonce_cell_nostd",
+        "libsmccc",
+        "libuuid_nostd",
+    ],
+    enabled: false,
+    target: {
+        android_arm64: {
+            enabled: true,
+            stdlibs: [
+                "libcompiler_builtins.rust_sysroot",
+                "libcore.rust_sysroot",
+            ],
+        },
+    },
+}
+
+dirgroup {
+    name: "trusty_dirgroup_packages_modules_virtualization_libs_libhypervisor_backends",
+    visibility: ["//trusty/vendor/google/aosp/scripts"],
+    dirs: ["."],
+}
diff --git a/libs/libhypervisor_backends/rules.mk b/libs/libhypervisor_backends/rules.mk
new file mode 100644
index 0000000..6fc9dea
--- /dev/null
+++ b/libs/libhypervisor_backends/rules.mk
@@ -0,0 +1,13 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+MODULE := $(LOCAL_DIR)
+MODULE_CRATE_NAME := hypervisor_backends
+MODULE_SRCS := \
+	$(LOCAL_DIR)/src/lib.rs \
+
+MODULE_LIBRARY_DEPS := \
+	trusty/user/base/lib/liballoc-rust \
+	$(call FIND_CRATE,once_cell) \
+	$(call FIND_CRATE,smccc) \
+	$(call FIND_CRATE,uuid) \
+
+include make/library.mk
\ No newline at end of file
diff --git a/libs/libvmbase/src/hyp/error.rs b/libs/libhypervisor_backends/src/error.rs
similarity index 100%
rename from libs/libvmbase/src/hyp/error.rs
rename to libs/libhypervisor_backends/src/error.rs
diff --git a/libs/libvmbase/src/hyp/hypervisor.rs b/libs/libhypervisor_backends/src/hypervisor.rs
similarity index 100%
rename from libs/libvmbase/src/hyp/hypervisor.rs
rename to libs/libhypervisor_backends/src/hypervisor.rs
diff --git a/libs/libvmbase/src/hyp/hypervisor/common.rs b/libs/libhypervisor_backends/src/hypervisor/common.rs
similarity index 98%
rename from libs/libvmbase/src/hyp/hypervisor/common.rs
rename to libs/libhypervisor_backends/src/hypervisor/common.rs
index 8f0e4dc..bfe638f 100644
--- a/libs/libvmbase/src/hyp/hypervisor/common.rs
+++ b/libs/libhypervisor_backends/src/hypervisor/common.rs
@@ -14,7 +14,7 @@
 
 //! This module regroups some common traits shared by all the hypervisors.
 
-use crate::hyp::Result;
+use crate::Result;
 
 /// Trait for the hypervisor.
 pub trait Hypervisor {
diff --git a/libs/libvmbase/src/hyp/hypervisor/geniezone.rs b/libs/libhypervisor_backends/src/hypervisor/geniezone.rs
similarity index 98%
rename from libs/libvmbase/src/hyp/hypervisor/geniezone.rs
rename to libs/libhypervisor_backends/src/hypervisor/geniezone.rs
index fcb9b42..fe56528 100644
--- a/libs/libvmbase/src/hyp/hypervisor/geniezone.rs
+++ b/libs/libhypervisor_backends/src/hypervisor/geniezone.rs
@@ -17,10 +17,7 @@
 use core::fmt::{self, Display, Formatter};
 
 use super::{Hypervisor, MemSharingHypervisor, MmioGuardedHypervisor};
-use crate::{
-    hyp::{Error, Result},
-    memory::page_4kb_of,
-};
+use crate::{mem::page_4kb_of, Error, Result};
 
 use smccc::{
     error::{positive_or_error_64, success_or_error_64},
diff --git a/libs/libvmbase/src/hyp/hypervisor/gunyah.rs b/libs/libhypervisor_backends/src/hypervisor/gunyah.rs
similarity index 100%
rename from libs/libvmbase/src/hyp/hypervisor/gunyah.rs
rename to libs/libhypervisor_backends/src/hypervisor/gunyah.rs
diff --git a/libs/libvmbase/src/hyp/hypervisor/kvm.rs b/libs/libhypervisor_backends/src/hypervisor/kvm.rs
similarity index 98%
rename from libs/libvmbase/src/hyp/hypervisor/kvm.rs
rename to libs/libhypervisor_backends/src/hypervisor/kvm.rs
index 7ed829e..e18c1f4 100644
--- a/libs/libvmbase/src/hyp/hypervisor/kvm.rs
+++ b/libs/libhypervisor_backends/src/hypervisor/kvm.rs
@@ -17,10 +17,7 @@
 use core::fmt::{self, Display, Formatter};
 
 use super::{DeviceAssigningHypervisor, Hypervisor, MemSharingHypervisor, MmioGuardedHypervisor};
-use crate::{
-    hyp::{Error, Result},
-    memory::page_4kb_of,
-};
+use crate::{mem::page_4kb_of, Error, Result};
 
 use smccc::{
     error::{positive_or_error_64, success_or_error_32, success_or_error_64},
diff --git a/libs/libvmbase/src/hyp.rs b/libs/libhypervisor_backends/src/lib.rs
similarity index 94%
rename from libs/libvmbase/src/hyp.rs
rename to libs/libhypervisor_backends/src/lib.rs
index 1cc2ca7..33dc5ad 100644
--- a/libs/libvmbase/src/hyp.rs
+++ b/libs/libhypervisor_backends/src/lib.rs
@@ -14,8 +14,13 @@
 
 //! This library provides wrappers around various hypervisor backends.
 
+#![no_std]
+
+extern crate alloc;
+
 mod error;
 mod hypervisor;
+mod mem;
 
 pub use error::{Error, Result};
 pub use hypervisor::{
diff --git a/libs/libhypervisor_backends/src/mem.rs b/libs/libhypervisor_backends/src/mem.rs
new file mode 100644
index 0000000..ff65c49
--- /dev/null
+++ b/libs/libhypervisor_backends/src/mem.rs
@@ -0,0 +1,28 @@
+// 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.
+
+/// The size of a 4KB memory in bytes.
+pub const SIZE_4KB: usize = 4 << 10;
+
+/// Computes the largest multiple of the provided alignment smaller or equal to the address.
+///
+/// Note: the result is undefined if alignment isn't a power of two.
+pub const fn unchecked_align_down(addr: usize, alignment: usize) -> usize {
+    addr & !(alignment - 1)
+}
+
+/// Computes the address of the 4KiB page containing a given address.
+pub const fn page_4kb_of(addr: usize) -> usize {
+    unchecked_align_down(addr, SIZE_4KB)
+}
diff --git a/libs/libvmbase/Android.bp b/libs/libvmbase/Android.bp
index c4e8385..3088633 100644
--- a/libs/libvmbase/Android.bp
+++ b/libs/libvmbase/Android.bp
@@ -81,6 +81,7 @@
         "libbuddy_system_allocator",
         "libcfg_if",
         "libcstr",
+        "libhypervisor_backends",
         "liblibfdt_nostd",
         "liblog_rust_nostd",
         "libonce_cell_nostd",
diff --git a/libs/libvmbase/src/entry.rs b/libs/libvmbase/src/entry.rs
index f442a32..2433722 100644
--- a/libs/libvmbase/src/entry.rs
+++ b/libs/libvmbase/src/entry.rs
@@ -15,7 +15,7 @@
 //! Rust entry point.
 
 use crate::{
-    bionic, console, heap, hyp,
+    bionic, console, heap,
     layout::{UART_ADDRESSES, UART_PAGE_ADDR},
     logger,
     memory::{PAGE_SIZE, SIZE_16KB, SIZE_4KB},
@@ -23,10 +23,11 @@
     rand,
 };
 use core::mem::size_of;
+use hypervisor_backends::{get_mmio_guard, Error};
 use static_assertions::const_assert_eq;
 
-fn try_console_init() -> Result<(), hyp::Error> {
-    if let Some(mmio_guard) = hyp::get_mmio_guard() {
+fn try_console_init() -> Result<(), Error> {
+    if let Some(mmio_guard) = get_mmio_guard() {
         mmio_guard.enroll()?;
 
         // TODO(ptosi): Use MmioSharer::share() to properly track this MMIO_GUARD_MAP.
diff --git a/libs/libvmbase/src/lib.rs b/libs/libvmbase/src/lib.rs
index 630834b..431e899 100644
--- a/libs/libvmbase/src/lib.rs
+++ b/libs/libvmbase/src/lib.rs
@@ -26,7 +26,6 @@
 pub mod fdt;
 pub mod heap;
 mod hvc;
-pub mod hyp;
 pub mod layout;
 pub mod linker;
 pub mod logger;
diff --git a/libs/libvmbase/src/memory/error.rs b/libs/libvmbase/src/memory/error.rs
index 4d08f1e..870e4c9 100644
--- a/libs/libvmbase/src/memory/error.rs
+++ b/libs/libvmbase/src/memory/error.rs
@@ -16,7 +16,7 @@
 
 use core::fmt;
 
-use crate::hyp;
+use hypervisor_backends::Error as HypervisorError;
 
 /// Errors for MemoryTracker operations.
 #[derive(Debug, Clone)]
@@ -38,7 +38,7 @@
     /// Region couldn't be unmapped.
     FailedToUnmap,
     /// Error from the interaction with the hypervisor.
-    Hypervisor(hyp::Error),
+    Hypervisor(HypervisorError),
     /// Failure to set `SHARED_MEMORY`.
     SharedMemorySetFailure,
     /// Failure to set `SHARED_POOL`.
@@ -82,8 +82,8 @@
     }
 }
 
-impl From<hyp::Error> for MemoryTrackerError {
-    fn from(e: hyp::Error) -> Self {
+impl From<HypervisorError> for MemoryTrackerError {
+    fn from(e: HypervisorError) -> Self {
         Self::Hypervisor(e)
     }
 }
diff --git a/libs/libvmbase/src/memory/shared.rs b/libs/libvmbase/src/memory/shared.rs
index 92dd09e..7e5e7e9 100644
--- a/libs/libvmbase/src/memory/shared.rs
+++ b/libs/libvmbase/src/memory/shared.rs
@@ -16,7 +16,6 @@
 
 use super::error::MemoryTrackerError;
 use super::util::virt_to_phys;
-use crate::hyp::{self, get_mem_sharer, get_mmio_guard};
 use crate::layout;
 use crate::util::unchecked_align_down;
 use aarch64_paging::paging::{MemoryRegion as VaRange, VirtualAddress, PAGE_SIZE};
@@ -29,6 +28,7 @@
 use core::ops::Range;
 use core::ptr::NonNull;
 use core::result;
+use hypervisor_backends::{self, get_mem_sharer, get_mmio_guard};
 use log::trace;
 use once_cell::race::OnceBox;
 use spin::mutex::SpinMutex;
@@ -108,7 +108,7 @@
 
 /// Allocates a memory range of at least the given size and alignment that is shared with the host.
 /// Returns a pointer to the buffer.
-pub(crate) fn alloc_shared(layout: Layout) -> hyp::Result<NonNull<u8>> {
+pub(crate) fn alloc_shared(layout: Layout) -> hypervisor_backends::Result<NonNull<u8>> {
     assert_ne!(layout.size(), 0);
     let Some(buffer) = try_shared_alloc(layout) else {
         handle_alloc_error(layout);
@@ -143,7 +143,10 @@
 ///
 /// The memory must have been allocated by `alloc_shared` with the same layout, and not yet
 /// deallocated.
-pub(crate) unsafe fn dealloc_shared(vaddr: NonNull<u8>, layout: Layout) -> hyp::Result<()> {
+pub(crate) unsafe fn dealloc_shared(
+    vaddr: NonNull<u8>,
+    layout: Layout,
+) -> hypervisor_backends::Result<()> {
     SHARED_POOL.get().unwrap().lock().dealloc_aligned(vaddr.as_ptr() as usize, layout);
 
     trace!("Deallocated shared buffer at {vaddr:?} with {layout:?}");
diff --git a/libs/libvmbase/src/memory/tracker.rs b/libs/libvmbase/src/memory/tracker.rs
index acf182f..c1f5d54 100644
--- a/libs/libvmbase/src/memory/tracker.rs
+++ b/libs/libvmbase/src/memory/tracker.rs
@@ -19,7 +19,6 @@
 use super::page_table::{PageTable, MMIO_LAZY_MAP_FLAG};
 use super::shared::{SHARED_MEMORY, SHARED_POOL};
 use crate::dsb;
-use crate::hyp::get_mmio_guard;
 use crate::memory::shared::{MemoryRange, MemorySharer, MmioSharer};
 use crate::util::RangeExt as _;
 use aarch64_paging::paging::{Attributes, Descriptor, MemoryRegion as VaRange, VirtualAddress};
@@ -29,6 +28,7 @@
 use core::num::NonZeroUsize;
 use core::ops::Range;
 use core::result;
+use hypervisor_backends::get_mmio_guard;
 use log::{debug, error};
 use spin::mutex::SpinMutex;
 use tinyvec::ArrayVec;
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
index 61679f2..68ff2ec 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.virtualization.vmlauncher;
 
+import android.content.Context;
+import android.content.SharedPreferences;
 import android.util.Log;
 
 import androidx.annotation.Keep;
@@ -24,21 +26,65 @@
 import com.android.virtualization.vmlauncher.proto.ForwardingRequestItem;
 import com.android.virtualization.vmlauncher.proto.IpAddr;
 import com.android.virtualization.vmlauncher.proto.QueueOpeningRequest;
+import com.android.virtualization.vmlauncher.proto.ReportVmActivePortsRequest;
+import com.android.virtualization.vmlauncher.proto.ReportVmActivePortsResponse;
 import com.android.virtualization.vmlauncher.proto.ReportVmIpAddrResponse;
 
 import io.grpc.stub.StreamObserver;
 
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
 final class DebianServiceImpl extends DebianServiceGrpc.DebianServiceImplBase {
     public static final String TAG = "DebianService";
+    private static final String PREFERENCE_FILE_KEY =
+            "com.android.virtualization.terminal.PREFERENCE_FILE_KEY";
+    private static final String PREFERENCE_FORWARDING_PORTS = "PREFERENCE_FORWARDING_PORTS";
+    private static final String PREFERENCE_FORWARDING_PORT_IS_ENABLED_PREFIX =
+            "PREFERENCE_FORWARDING_PORT_IS_ENABLED_";
+
+    private final Context mContext;
+    private final SharedPreferences mSharedPref;
+    private SharedPreferences.OnSharedPreferenceChangeListener mPortForwardingListener;
     private final DebianServiceCallback mCallback;
 
+
     static {
         System.loadLibrary("forwarder_host_jni");
     }
 
-    protected DebianServiceImpl(DebianServiceCallback callback) {
+    DebianServiceImpl(Context context, DebianServiceCallback callback) {
         super();
         mCallback = callback;
+        mContext = context;
+        mSharedPref = mContext.getSharedPreferences(PREFERENCE_FILE_KEY, Context.MODE_PRIVATE);
+    }
+
+    @Override
+    public void reportVmActivePorts(
+            ReportVmActivePortsRequest request,
+            StreamObserver<ReportVmActivePortsResponse> responseObserver) {
+        Log.d(DebianServiceImpl.TAG, "reportVmActivePorts: " + request.toString());
+
+        SharedPreferences.Editor editor = mSharedPref.edit();
+        Set<String> ports = new HashSet<>();
+        for (int port : request.getPortsList()) {
+            ports.add(Integer.toString(port));
+            if (!mSharedPref.contains(
+                    PREFERENCE_FORWARDING_PORT_IS_ENABLED_PREFIX + Integer.toString(port))) {
+                editor.putBoolean(
+                        PREFERENCE_FORWARDING_PORT_IS_ENABLED_PREFIX + Integer.toString(port),
+                        false);
+            }
+        }
+        editor.putStringSet(PREFERENCE_FORWARDING_PORTS, ports);
+        editor.apply();
+
+        ReportVmActivePortsResponse reply =
+                ReportVmActivePortsResponse.newBuilder().setSuccess(true).build();
+        responseObserver.onNext(reply);
+        responseObserver.onCompleted();
     }
 
     @Override
@@ -55,6 +101,19 @@
     public void openForwardingRequestQueue(
             QueueOpeningRequest request, StreamObserver<ForwardingRequestItem> responseObserver) {
         Log.d(DebianServiceImpl.TAG, "OpenForwardingRequestQueue");
+        mPortForwardingListener =
+                new SharedPreferences.OnSharedPreferenceChangeListener() {
+                    @Override
+                    public void onSharedPreferenceChanged(
+                            SharedPreferences sharedPreferences, String key) {
+                        if (key.startsWith(PREFERENCE_FORWARDING_PORT_IS_ENABLED_PREFIX)
+                                || key.equals(PREFERENCE_FORWARDING_PORTS)) {
+                            updateListeningPorts();
+                        }
+                    }
+                };
+        mSharedPref.registerOnSharedPreferenceChangeListener(mPortForwardingListener);
+        updateListeningPorts();
         runForwarderHost(request.getCid(), new ForwarderHostCallback(responseObserver));
         responseObserver.onCompleted();
     }
@@ -79,7 +138,32 @@
 
     private static native void runForwarderHost(int cid, ForwarderHostCallback callback);
 
-    public static native void terminateForwarderHost();
+    private static native void terminateForwarderHost();
+
+    void killForwarderHost() {
+        Log.d(DebianServiceImpl.TAG, "Stopping port forwarding");
+        if (mPortForwardingListener != null) {
+            mSharedPref.unregisterOnSharedPreferenceChangeListener(mPortForwardingListener);
+            terminateForwarderHost();
+        }
+    }
+
+    private static native void updateListeningPorts(int[] ports);
+
+    private void updateListeningPorts() {
+        updateListeningPorts(
+                mSharedPref
+                        .getStringSet(PREFERENCE_FORWARDING_PORTS, Collections.emptySet())
+                        .stream()
+                        .filter(
+                                port ->
+                                        mSharedPref.getBoolean(
+                                                PREFERENCE_FORWARDING_PORT_IS_ENABLED_PREFIX + port,
+                                                false))
+                        .map(Integer::valueOf)
+                        .mapToInt(Integer::intValue)
+                        .toArray());
+    }
 
     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 2bd85e1..846fd26 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
@@ -60,6 +60,7 @@
     private VirtualMachine mVirtualMachine;
     private ResultReceiver mResultReceiver;
     private Server mServer;
+    private DebianServiceImpl mDebianService;
 
     @Override
     public IBinder onBind(Intent intent) {
@@ -107,9 +108,7 @@
                             if (mResultReceiver != null) {
                                 mResultReceiver.send(success ? RESULT_STOP : RESULT_ERROR, null);
                             }
-                            if (!success) {
-                                stopSelf();
-                            }
+                            stopSelf();
                         });
         Path logPath = getFileStreamPath(mVirtualMachine.getName() + ".log").toPath();
         Logger.setup(mVirtualMachine, logPath, mExecutorService);
@@ -129,6 +128,7 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
+        stopDebianServer();
         if (mVirtualMachine != null) {
             if (mVirtualMachine.getStatus() == VirtualMachine.STATUS_RUNNING) {
                 try {
@@ -142,7 +142,6 @@
             mExecutorService = null;
             mVirtualMachine = null;
         }
-        stopDebianServer();
     }
 
     private void startDebianServer() {
@@ -174,10 +173,11 @@
         try {
             // TODO(b/372666638): gRPC for java doesn't support vsock for now.
             int port = 0;
+            mDebianService = new DebianServiceImpl(this, this);
             mServer =
                     OkHttpServerBuilder.forPort(port, InsecureServerCredentials.create())
                             .intercept(interceptor)
-                            .addService(new DebianServiceImpl(this))
+                            .addService(mDebianService)
                             .build()
                             .start();
         } catch (IOException e) {
@@ -199,8 +199,10 @@
     }
 
     private void stopDebianServer() {
+        if (mDebianService != null) {
+            mDebianService.killForwarderHost();
+        }
         if (mServer != null) {
-            DebianServiceImpl.terminateForwarderHost();
             mServer.shutdown();
         }
     }
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index 917a027..a6c79cb 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -221,6 +221,7 @@
 
     @Test
     @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
+    @VsrTest(requirements = {"VSR-7.1-001.006"})
     public void vmAttestationWhenRemoteAttestationIsNotSupported() throws Exception {
         // pVM remote attestation is only supported on protected VMs.
         assumeProtectedVM();
@@ -249,6 +250,7 @@
 
     @Test
     @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
+    @VsrTest(requirements = {"VSR-7.1-001.006"})
     public void vmAttestationWithVendorPartitionWhenSupported() throws Exception {
         // pVM remote attestation is only supported on protected VMs.
         assumeProtectedVM();
@@ -267,6 +269,7 @@
 
     @Test
     @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
+    @VsrTest(requirements = {"VSR-7.1-001.006"})
     public void vmAttestationWhenRemoteAttestationIsSupported() throws Exception {
         // pVM remote attestation is only supported on protected VMs.
         assumeProtectedVM();