Merge "Take ownership of inherited FDs using the rustutils crate" into main
diff --git a/OWNERS b/OWNERS
index 40c709f..717a4db 100644
--- a/OWNERS
+++ b/OWNERS
@@ -28,3 +28,10 @@
 tabba@google.com
 vdonnefort@google.com
 victorhsieh@google.com
+
+# Ferrochrome
+per-file android/FerrochromeApp/**=jiyong@google.com,jeongik@google.com
+per-file android/LinuxInstaller/**=jiyong@google.com,jeongik@google.com
+per-file android/TerminalApp/**=jiyong@google.com,jeongik@google.com
+per-file android/VmLauncherApp/**=jiyong@google.com,jeongik@google.com
+per-file libs/vm_launcher_lib/**=jiyong@google.com,jeongik@google.com
diff --git a/android/TerminalApp/Android.bp b/android/TerminalApp/Android.bp
index 3ae014e..1a7c581 100644
--- a/android/TerminalApp/Android.bp
+++ b/android/TerminalApp/Android.bp
@@ -9,8 +9,10 @@
     static_libs: [
         "vm_launcher_lib",
     ],
-    sdk_version: "system_current",
+    platform_apis: true,
+    privileged: true,
     optimize: {
+        proguard_flags_files: ["proguard.flags"],
         shrink_resources: true,
     },
     apex_available: [
diff --git a/android/TerminalApp/AndroidManifest.xml b/android/TerminalApp/AndroidManifest.xml
index c92da67..e338c49 100644
--- a/android/TerminalApp/AndroidManifest.xml
+++ b/android/TerminalApp/AndroidManifest.xml
@@ -2,9 +2,13 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.virtualization.terminal" >
 
+    <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
+    <uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
     <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
 
+    <uses-feature android:name="android.software.virtualization_framework" android:required="true" />
     <application
 	android:label="@string/app_name"
         android:icon="@mipmap/ic_launcher"
@@ -27,6 +31,20 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+
+        <service
+            android:name="com.android.virtualization.vmlauncher.VmLauncherService"
+            android:enabled="true"
+            android:exported="false"
+            android:foregroundServiceType="specialUse">
+            <property
+                android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
+                android:value="Run VM instances" />
+            <intent-filter>
+                <action android:name="android.virtualization.START_VM_LAUNCHER_SERVICE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </service>
     </application>
 
 </manifest>
diff --git a/android/TerminalApp/proguard.flags b/android/TerminalApp/proguard.flags
new file mode 100644
index 0000000..13ec24e
--- /dev/null
+++ b/android/TerminalApp/proguard.flags
@@ -0,0 +1,7 @@
+# Keep the no-args constructor of the deserialized class
+-keepclassmembers class com.android.virtualization.vmlauncher.ConfigJson {
+  <init>();
+}
+-keepclassmembers class com.android.virtualization.vmlauncher.ConfigJson$* {
+  <init>();
+}
diff --git a/android/VmLauncherApp/Android.bp b/android/VmLauncherApp/Android.bp
index 7dd2473..2e8cc93 100644
--- a/android/VmLauncherApp/Android.bp
+++ b/android/VmLauncherApp/Android.bp
@@ -11,7 +11,7 @@
         "android.system.virtualizationservice_internal-java",
         // TODO(b/331708504): will be removed when AVF framework handles surface
         "libcrosvm_android_display_service-java",
-        "gson",
+        "vm_launcher_lib",
     ],
     libs: [
         "framework-virtualization.impl",
diff --git a/android/VmLauncherApp/AndroidManifest.xml b/android/VmLauncherApp/AndroidManifest.xml
index 583fce7..4fb4b5c 100644
--- a/android/VmLauncherApp/AndroidManifest.xml
+++ b/android/VmLauncherApp/AndroidManifest.xml
@@ -6,8 +6,6 @@
     <uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
     <uses-feature android:name="android.software.virtualization_framework" android:required="true" />
 
     <permission android:name="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER"
@@ -28,20 +26,6 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-        <service
-            android:name=".VmLauncherService"
-            android:enabled="true"
-            android:exported="true"
-            android:permission="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER"
-            android:foregroundServiceType="specialUse">
-            <property
-                android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
-                android:value="Run VM instances" />
-            <intent-filter>
-                <action android:name="android.virtualization.START_VM_LAUNCHER_SERVICE" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </service>
 
     </application>
 
diff --git a/android/virtualizationservice/aidl/Android.bp b/android/virtualizationservice/aidl/Android.bp
index c1bff5e..79a9d40 100644
--- a/android/virtualizationservice/aidl/Android.bp
+++ b/android/virtualizationservice/aidl/Android.bp
@@ -29,6 +29,7 @@
         rust: {
             enabled: true,
             apex_available: [
+                "//apex_available:platform",
                 "com.android.virt",
                 "com.android.compos",
                 "com.android.microfuchsia",
@@ -149,6 +150,7 @@
         rust: {
             enabled: true,
             apex_available: [
+                "//apex_available:platform",
                 "com.android.virt",
                 "com.android.compos",
                 "com.android.microfuchsia",
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index be62d18..4916df7 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -136,7 +136,6 @@
         "vm",
     ],
     prebuilts: [
-        "features_com.android.virt.xml",
         "microdroid_initrd_debuggable",
         "microdroid_initrd_normal",
         "microdroid.json",
diff --git a/build/apex/permissions/Android.bp b/build/apex/permissions/Android.bp
index 0c925ce..678a4f2 100644
--- a/build/apex/permissions/Android.bp
+++ b/build/apex/permissions/Android.bp
@@ -21,4 +21,5 @@
     name: "features_com.android.virt.xml",
     sub_dir: "permissions",
     src: "features_com.android.virt.xml",
+    soc_specific: true,
 }
diff --git a/build/apex/product_packages.mk b/build/apex/product_packages.mk
index a024192..b2a4ca2 100644
--- a/build/apex/product_packages.mk
+++ b/build/apex/product_packages.mk
@@ -24,6 +24,7 @@
 
 PRODUCT_PACKAGES += \
     com.android.compos \
+    features_com.android.virt.xml
 
 # TODO(b/207336449): Figure out how to get these off /system
 PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST := \
diff --git a/build/debian/build.sh b/build/debian/build.sh
new file mode 100755
index 0000000..9136a67
--- /dev/null
+++ b/build/debian/build.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+
+# This is a script to build a Debian image that can run in a VM created via AVF.
+# TODOs:
+# - Support x86_64 architecture
+# - Add Android-specific packages via a new class
+# - Use a stable release from debian-cloud-images
+
+show_help() {
+	echo Usage: $0 [OPTION]... [FILE]
+	echo Builds a debian image and save it to FILE.
+	echo Options:
+	echo -h         Pring usage and this help message and exit.
+}
+
+check_sudo() {
+	if [ "$EUID" -ne 0 ]; then
+		echo "Please run as root."
+		exit
+	fi
+}
+
+parse_options() {
+	while getopts ":h" option; do
+		case ${option} in
+			h)
+				show_help
+				exit;;
+		esac
+	done
+	if [ -n "$1" ]; then
+		built_image=$1
+	fi
+}
+
+install_prerequisites() {
+	DEBIAN_FRONTEND=noninteractive \
+	apt install --no-install-recommends --assume-yes \
+		ca-certificates \
+		debsums \
+		dosfstools \
+		fai-server \
+		fai-setup-storage \
+		fdisk \
+		make \
+		python3 \
+		python3-libcloud \
+		python3-marshmallow \
+		python3-pytest \
+		python3-yaml \
+		qemu-utils \
+		udev \
+		qemu-system-arm \
+		qemu-user-static
+}
+
+download_debian_cloud_image() {
+	local ver=master
+	local prj=debian-cloud-images
+	local url=https://salsa.debian.org/cloud-team/${prj}/-/archive/${ver}/${prj}-${ver}.tar.gz
+	local outdir=${debian_cloud_image}
+
+	mkdir -p ${outdir}
+	wget -O - ${url} | tar xz -C ${outdir} --strip-components=1
+}
+
+copy_android_config() {
+	local src=$(dirname $0)/fai_config
+	local dst=${config_space}
+
+	cp -R ${src}/* ${dst}
+	cp $(dirname $0)/image.yaml ${resources_dir}
+}
+
+run_fai() {
+	local out=${built_image}
+	make -C ${debian_cloud_image} image_bookworm_nocloud_arm64
+	mv ${debian_cloud_image}/image_bookworm_nocloud_arm64.raw ${out}
+}
+
+clean_up() {
+	rm -rf ${workdir}
+}
+
+set -e
+trap clean_up EXIT
+
+built_image=image.raw
+workdir=$(mktemp -d)
+debian_cloud_image=${workdir}/debian_cloud_image
+debian_version=bookworm
+config_space=${debian_cloud_image}/config_space/${debian_version}
+resources_dir=${debian_cloud_image}/src/debian_cloud_images/resources
+check_sudo
+parse_options $@
+install_prerequisites
+download_debian_cloud_image
+copy_android_config
+run_fai
diff --git a/build/debian/fai_config/class/AVF.var b/build/debian/fai_config/class/AVF.var
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build/debian/fai_config/class/AVF.var
diff --git a/build/debian/fai_config/package_config/AVF b/build/debian/fai_config/package_config/AVF
new file mode 100644
index 0000000..7d86d41
--- /dev/null
+++ b/build/debian/fai_config/package_config/AVF
@@ -0,0 +1,4 @@
+PACKAGES install
+
+# Just for testing
+tree
diff --git a/build/debian/image.yaml b/build/debian/image.yaml
new file mode 100644
index 0000000..eb42a07
--- /dev/null
+++ b/build/debian/image.yaml
@@ -0,0 +1,60 @@
+# After modifications, please call:
+# "python3 -m debian_cloud_images.cli.generate_ci .gitlab/ci/generated.yml"
+---
+apiVersion: cloud.debian.org/v1alpha1
+kind: ImageConfig
+
+archs:
+- name: amd64
+  azureName: X64
+  ociArch: amd64
+  faiClasses: [AMD64]
+- name: arm64
+  azureName: Arm64
+  ociArch: arm64
+  faiClasses: [ARM64]
+- name: ppc64el
+  faiClasses: [PPC64EL]
+  ociArch: ppc64le
+- name: riscv64
+  faiClasses: [RISCV64]
+  ociArch: riscv64
+
+releases:
+- name: bookworm
+  basename: bookworm
+  id: '12'
+  baseid: '12'
+  faiClasses: [BOOKWORM, LINUX_VERSION_BASE, EXTRAS]
+  matches:
+  - matchArches: [amd64, arm64, ppc64el]
+- name: bookworm-backports
+  basename: bookworm-backports
+  id: 12-backports
+  baseid: '12'
+  faiClasses: [BOOKWORM, LINUX_VERSION_BACKPORTS, EXTRAS]
+  matches:
+  - matchArches: [amd64, arm64, ppc64el]
+- name: trixie
+  basename: trixie
+  id: '13'
+  baseid: '13'
+  faiClasses: [TRIXIE, LINUX_VERSION_BASE, EXTRAS]
+  matches:
+  - matchArches: [amd64, arm64, ppc64el]
+
+vendors:
+- name: nocloud
+  faiClasses: [SYSTEM_BOOT, NOCLOUD, LINUX_VARIANT_BASE, TIME_SYSTEMD, AVF]
+  size: 2
+
+types:
+- name: dev
+  faiClasses: [TYPE_DEV]
+  outputName: 'debian-{release}-{vendor}-{arch}-{build_type}-{build_id}-{version}'
+  outputVersion: '{version}'
+  outputVersionAzure: '0.0.{version!s}'
+- name: official
+  outputName: 'debian-{release}-{vendor}-{arch}-{build_type}-{version}'
+  outputVersion: '{date}-{version}'
+  outputVersionAzure: '0.{date!s}.{version!s}'
diff --git a/libs/libvmclient/Android.bp b/libs/libvmclient/Android.bp
index 5bd59da..d318d0e 100644
--- a/libs/libvmclient/Android.bp
+++ b/libs/libvmclient/Android.bp
@@ -23,6 +23,7 @@
         "com.android.compos",
         "com.android.microfuchsia",
         "com.android.virt",
+        "//apex_available:platform",
     ],
 }
 
diff --git a/libs/libvmclient/src/lib.rs b/libs/libvmclient/src/lib.rs
index bc9d683..ce7d5a5 100644
--- a/libs/libvmclient/src/lib.rs
+++ b/libs/libvmclient/src/lib.rs
@@ -55,6 +55,7 @@
     time::Duration,
 };
 
+const EARLY_VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/early_virtmgr";
 const VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/virtmgr";
 const VIRTMGR_THREADS: usize = 2;
 
@@ -122,10 +123,20 @@
     /// Spawns a new instance of virtmgr, a child process that will host
     /// the VirtualizationService AIDL service.
     pub fn new() -> Result<VirtualizationService, io::Error> {
+        Self::new_with_path(VIRTMGR_PATH)
+    }
+
+    /// Spawns a new instance of early_virtmgr, a child process that will host
+    /// the VirtualizationService AIDL service for early VMs.
+    pub fn new_early() -> Result<VirtualizationService, io::Error> {
+        Self::new_with_path(EARLY_VIRTMGR_PATH)
+    }
+
+    fn new_with_path(virtmgr_path: &str) -> Result<VirtualizationService, io::Error> {
         let (wait_fd, ready_fd) = posix_pipe()?;
         let (client_fd, server_fd) = posix_socketpair()?;
 
-        let mut command = Command::new(VIRTMGR_PATH);
+        let mut command = Command::new(virtmgr_path);
         // Can't use BorrowedFd as it doesn't implement Display
         command.arg("--rpc-server-fd").arg(format!("{}", server_fd.as_raw_fd()));
         command.arg("--ready-fd").arg(format!("{}", ready_fd.as_raw_fd()));
diff --git a/libs/vm_launcher_lib/Android.bp b/libs/vm_launcher_lib/Android.bp
index 8591c8d..cb6fc9e 100644
--- a/libs/vm_launcher_lib/Android.bp
+++ b/libs/vm_launcher_lib/Android.bp
@@ -9,5 +9,12 @@
         "//apex_available:platform",
         "com.android.virt",
     ],
-    sdk_version: "system_current",
+    platform_apis: true,
+    static_libs: [
+        "gson",
+    ],
+    libs: [
+        "framework-virtualization.impl",
+        "framework-annotations-lib",
+    ],
 }
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/ConfigJson.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
similarity index 100%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/ConfigJson.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Logger.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Logger.java
similarity index 100%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Logger.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Logger.java
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Runner.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Runner.java
similarity index 98%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Runner.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Runner.java
index a5f58fe..9b97fee 100644
--- a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Runner.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Runner.java
@@ -30,7 +30,7 @@
 
 /** Utility class for creating a VM and waiting for it to finish. */
 class Runner {
-    private static final String TAG = MainActivity.TAG;
+    private static final String TAG = Runner.class.getSimpleName();
     private final VirtualMachine mVirtualMachine;
     private final Callback mCallback;
 
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmLauncherService.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
similarity index 100%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmLauncherService.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java