Merge "Add localdebs dir to install deb files" into main
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index cfb1cbe..397a546 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -21,7 +21,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -72,6 +71,7 @@
public class MainActivity extends BaseActivity
implements VmLauncherService.VmLauncherServiceCallback, AccessibilityStateChangeListener {
static final String TAG = "VmTerminalApp";
+ static final String KEY_DISK_SIZE = "disk_size";
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;
@@ -509,17 +509,11 @@
}
private void resizeDiskIfNecessary(InstalledImage image) {
- String prefKey = getString(R.string.preference_file_key);
- String key = getString(R.string.preference_disk_size_key);
- SharedPreferences sharedPref = this.getSharedPreferences(prefKey, Context.MODE_PRIVATE);
try {
- // Use current size as default value to ensure if its size is multiple of 4096
- long newSize = sharedPref.getLong(key, image.getSize());
- Log.d(TAG, "Resizing disk to " + newSize + " bytes");
- newSize = image.resize(newSize);
- sharedPref.edit().putLong(key, newSize).apply();
+ // TODO(b/382190982): Show snackbar message instead when it's recoverable.
+ image.resize(getIntent().getLongExtra(KEY_DISK_SIZE, image.getSize()));
} catch (IOException e) {
- Log.e(TAG, "Failed to resize disk", e);
+ ErrorActivity.start(this, new Exception("Failed to resize disk", e));
return;
}
}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
index 30475f5..b893d9e 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
@@ -15,9 +15,7 @@
*/
package com.android.virtualization.terminal
-import android.content.Context
import android.content.Intent
-import android.content.SharedPreferences
import android.icu.text.MeasureFormat
import android.icu.text.NumberFormat
import android.icu.util.Measure
@@ -37,12 +35,12 @@
import java.util.regex.Pattern
class SettingsDiskResizeActivity : AppCompatActivity() {
+ // TODO(b/382191950): Calculate the maxDiskSizeMb based on the device storage usage
private val maxDiskSizeMb: Long = 16 shl 10
private val numberPattern: Pattern = Pattern.compile("[\\d]*[\\٫.,]?[\\d]+")
private var diskSizeStepMb: Long = 0
private var diskSizeMb: Long = 0
- private lateinit var sharedPref: SharedPreferences
private lateinit var buttons: View
private lateinit var cancelButton: View
private lateinit var resizeButton: View
@@ -71,13 +69,8 @@
diskSizeStepMb = 1L shl resources.getInteger(R.integer.disk_size_round_up_step_size_in_mb)
- sharedPref =
- this.getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE)
- diskSizeMb =
- bytesToMb(
- sharedPref.getLong(getString(R.string.preference_disk_size_key), /* defValue= */ 0)
- )
val image = InstalledImage.getDefault(this)
+ diskSizeMb = bytesToMb(image.getSize())
val minDiskSizeMb = bytesToMb(image.getSmallestSizePossible()).coerceAtMost(diskSizeMb)
diskSizeText = findViewById<TextView>(R.id.settings_disk_resize_resize_gb_assigned)!!
@@ -139,16 +132,14 @@
.show()
}
- fun resize() {
+ private fun resize() {
diskSizeMb = progressToMb(diskSizeSlider.progress)
buttons.isVisible = false
- val editor = sharedPref.edit()
- editor.putLong(getString(R.string.preference_disk_size_key), mbToBytes(diskSizeMb))
- editor.apply()
// Restart terminal
val intent = baseContext.packageManager.getLaunchIntentForPackage(baseContext.packageName)
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ intent?.putExtra(MainActivity.KEY_DISK_SIZE, mbToBytes(diskSizeMb))
finish()
startActivity(intent)
}
diff --git a/android/TerminalApp/res/values/config.xml b/android/TerminalApp/res/values/config.xml
index 713e1a5..7f0b5e6 100644
--- a/android/TerminalApp/res/values/config.xml
+++ b/android/TerminalApp/res/values/config.xml
@@ -15,8 +15,5 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="preference_file_key" translatable="false">com.android.virtualization.terminal.PREFERENCE_FILE_KEY</string>
- <string name="preference_disk_size_key" translatable="false">PREFERENCE_DISK_SIZE_KEY</string>
-
<bool name="terminal_portrait_only">true</bool>
</resources>
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 6541764..946bc8c 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -108,6 +108,7 @@
"rialto_bin",
"android_bootloader_crosvm_aarch64",
],
+ native_shared_libs: ["libavf"],
},
x86_64: {
binaries: [
@@ -128,6 +129,7 @@
prebuilts: [
"android_bootloader_crosvm_x86_64",
],
+ native_shared_libs: ["libavf"],
},
},
binaries: [
diff --git a/build/microdroid/Android.bp b/build/microdroid/Android.bp
index 7f23ae6..f750f62 100644
--- a/build/microdroid/Android.bp
+++ b/build/microdroid/Android.bp
@@ -50,6 +50,7 @@
avb_private_key: ":microdroid_sign_key",
avb_algorithm: "SHA256_RSA4096",
avb_hash_algorithm: "sha256",
+ use_fec: false,
partition_name: "system",
deps: [
"init_second_stage.microdroid",
@@ -245,6 +246,7 @@
avb_private_key: ":microdroid_sign_key",
avb_algorithm: "SHA256_RSA4096",
avb_hash_algorithm: "sha256",
+ use_fec: false,
file_contexts: ":microdroid_vendor_file_contexts.gen",
// For deterministic output, use fake_timestamp, hard-coded uuid
fake_timestamp: "1611569676",
diff --git a/libs/libavf/Android.bp b/libs/libavf/Android.bp
new file mode 100644
index 0000000..e143709
--- /dev/null
+++ b/libs/libavf/Android.bp
@@ -0,0 +1,58 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_bindgen {
+ name: "libavf_bindgen",
+ wrapper_src: "include/android/virtualization.h",
+ crate_name: "avf_bindgen",
+ defaults: ["avf_build_flags_rust"],
+ source_stem: "bindings",
+ bindgen_flags: ["--default-enum-style rust"],
+ apex_available: ["com.android.virt"],
+}
+
+rust_defaults {
+ name: "libavf.default",
+ crate_name: "avf",
+ defaults: ["avf_build_flags_rust"],
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ rustlibs: [
+ "libvmclient",
+ "android.system.virtualizationcommon-rust",
+ "android.system.virtualizationservice-rust",
+ "libavf_bindgen",
+ "libbinder_rs",
+ "liblibc",
+ "liblog_rust",
+ "librpcbinder_rs",
+ ],
+ apex_available: ["com.android.virt"],
+}
+
+rust_ffi_static {
+ name: "libavf_impl",
+ defaults: ["libavf.default"],
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libavf",
+ llndk: {
+ symbol_file: "libavf.map.txt",
+ moved_to_apex: true,
+ },
+ whole_static_libs: ["libavf_impl"],
+ shared_libs: [
+ "libbinder_ndk",
+ "libbinder_rpc_unstable",
+ "liblog",
+ ],
+ export_static_lib_headers: ["libavf_impl"],
+ apex_available: ["com.android.virt"],
+ version_script: "libavf.map.txt",
+ stubs: {
+ symbol_file: "libavf.map.txt",
+ },
+}
diff --git a/libs/libavf/include/android/virtualization.h b/libs/libavf/include/android/virtualization.h
new file mode 100644
index 0000000..f33ee75
--- /dev/null
+++ b/libs/libavf/include/android/virtualization.h
@@ -0,0 +1,326 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+__BEGIN_DECLS
+
+/**
+ * Represents a handle on a virtual machine raw config.
+ */
+typedef struct AVirtualMachineRawConfig AVirtualMachineRawConfig;
+
+/**
+ * Create a new virtual machine raw config object with no properties.
+ *
+ * This only creates the raw config object. `name` and `kernel` must be set with
+ * calls to {@link AVirtualMachineRawConfig_setName} and {@link AVirtualMachineRawConfig_setKernel}.
+ * Other properties, set by {@link AVirtualMachineRawConfig_setMemoryMib},
+ * {@link AVirtualMachineRawConfig_setInitRd}, {@link AVirtualMachineRawConfig_addDisk},
+ * {@link AVirtualMachineRawConfig_setProtectedVm}, and {@link AVirtualMachineRawConfig_setBalloon}
+ * are optional.
+ *
+ * The caller takes ownership of the returned raw config object, and is responsible for creating a
+ * VM by calling {@link AVirtualMachine_createRaw} or releasing it by calling
+ * {@link AVirtualMachineRawConfig_destroy}.
+ *
+ * \return A new virtual machine raw config object.
+ */
+AVirtualMachineRawConfig* AVirtualMachineRawConfig_create();
+
+/**
+ * Destroy a virtual machine config object.
+ *
+ * \param config a virtual machine config object.
+ *
+ * `AVirtualMachineRawConfig_destroy` does nothing if `config` is null. A destroyed config object
+ * must not be reused.
+ */
+void AVirtualMachineRawConfig_destroy(AVirtualMachineRawConfig* config);
+
+/**
+ * Set a name of a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param name a pointer to a null-terminated string for the name.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setName(AVirtualMachineRawConfig* config, const char* name);
+
+/**
+ * Set an instance ID of a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param instanceId a pointer to a 64-byte buffer for the instance ID.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setInstanceId(AVirtualMachineRawConfig* config,
+ const int8_t* instanceId);
+
+/**
+ * Set a kernel image of a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param fd a readable file descriptor containing the kernel image, or -1 to unset.
+ * `AVirtualMachineRawConfig_setKernel` takes ownership of `fd`.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setKernel(AVirtualMachineRawConfig* config, int fd);
+
+/**
+ * Set an init rd of a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param fd a readable file descriptor containing the kernel image, or -1 to unset.
+ * `AVirtualMachineRawConfig_setInitRd` takes ownership of `fd`.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setInitRd(AVirtualMachineRawConfig* config, int fd);
+
+/**
+ * Add a disk for a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param fd a readable file descriptor containing the disk image.
+ * `AVirtualMachineRawConfig_addDisk` takes ownership of `fd`.
+ *
+ * \return If successful, it returns 0. If `fd` is invalid, it returns -EINVAL.
+ */
+int AVirtualMachineRawConfig_addDisk(AVirtualMachineRawConfig* config, int fd);
+
+/**
+ * Set how much memory will be given to a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param memoryMib the amount of RAM to give the virtual machine, in MiB. 0 or negative to use the
+ * default.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setMemoryMib(AVirtualMachineRawConfig* config, int32_t memoryMib);
+
+/**
+ * Set whether a virtual machine is protected or not.
+ *
+ * \param config a virtual machine config object.
+ * \param protectedVm whether the virtual machine should be protected.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setProtectedVm(AVirtualMachineRawConfig* config, bool protectedVm);
+
+/**
+ * Set whether a virtual machine uses memory ballooning or not.
+ *
+ * \param config a virtual machine config object.
+ * \param balloon whether the virtual machine should use memory ballooning.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setBalloon(AVirtualMachineRawConfig* config, bool balloon);
+
+/**
+ * Set whether to use an alternate, hypervisor-specific authentication method
+ * for protected VMs. You don't want to use this.
+ *
+ * \return If successful, it returns 0. It returns `-ENOTSUP` if the hypervisor doesn't have an
+ * alternate auth mode.
+ */
+int AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod(AVirtualMachineRawConfig* config,
+ bool enable);
+
+/**
+ * Use the specified fd as the backing memfd for a range of the guest
+ * physical memory.
+ *
+ * \param config a virtual machine config object.
+ * \param fd a memfd
+ * \param rangeStart range start IPA
+ * \param rangeEnd range end IPA
+ *
+ * \return If successful, it returns 0. It returns `-ENOTSUP` if the hypervisor doesn't support
+ * backing memfd.
+ */
+int AVirtualMachineRawConfig_addCustomMemoryBackingFile(AVirtualMachineRawConfig* config, int fd,
+ size_t rangeStart, size_t rangeEnd);
+
+/**
+ * Represents a handle on a virtualization service, responsible for managing virtual machines.
+ */
+typedef struct AVirtualizationService AVirtualizationService;
+
+/**
+ * Spawn a new instance of `virtmgr`, a child process that will host the `VirtualizationService`
+ * service, and connect to the child process.
+ *
+ * The caller takes ownership of the returned service object, and is responsible for releasing it
+ * by calling {@link AVirtualizationService_destroy}.
+ *
+ * \param early set to true when running a service for early virtual machines. See
+ * [`early_vm.md`](../../../../docs/early_vm.md) for more details on early virtual machines.
+ * \param service an out parameter that will be set to the service handle.
+ *
+ * \return
+ * - If successful, it sets `service` and returns 0.
+ * - If it fails to spawn `virtmgr`, it leaves `service` untouched and returns a negative value
+ * representing the OS error code.
+ * - If it fails to connect to the spawned `virtmgr`, it leaves `service` untouched and returns
+ * `-ECONNREFUSED`.
+ */
+int AVirtualizationService_create(AVirtualizationService** service, bool early);
+
+/**
+ * Destroy a VirtualizationService object.
+ *
+ * `AVirtualizationService_destroy` does nothing if `service` is null. A destroyed service object
+ * must not be reused.
+ *
+ * \param service a handle on a virtualization service.
+ */
+void AVirtualizationService_destroy(AVirtualizationService* service);
+
+/**
+ * Represents a handle on a virtual machine.
+ */
+typedef struct AVirtualMachine AVirtualMachine;
+
+/**
+ * The reason why a virtual machine stopped.
+ * @see AVirtualMachine_waitForStop
+ */
+enum StopReason : int32_t {
+ /**
+ * VirtualizationService died.
+ */
+ VIRTUALIZATION_SERVICE_DIED = 1,
+ /**
+ * There was an error waiting for the virtual machine.
+ */
+ INFRASTRUCTURE_ERROR = 2,
+ /**
+ * The virtual machine was killed.
+ */
+ KILLED = 3,
+ /**
+ * The virtual machine stopped for an unknown reason.
+ */
+ UNKNOWN = 4,
+ /**
+ * The virtual machine requested to shut down.
+ */
+ SHUTDOWN = 5,
+ /**
+ * crosvm had an error starting the virtual machine.
+ */
+ START_FAILED = 6,
+ /**
+ * The virtual machine requested to reboot, possibly as the result of a kernel panic.
+ */
+ REBOOT = 7,
+ /**
+ * The virtual machine or crosvm crashed.
+ */
+ CRASH = 8,
+ /**
+ * The pVM firmware failed to verify the VM because the public key doesn't match.
+ */
+ PVM_FIRMWARE_PUBLIC_KEY_MISMATCH = 9,
+ /**
+ * The pVM firmware failed to verify the VM because the instance image changed.
+ */
+ PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED = 10,
+ /**
+ * The virtual machine was killed due to hangup.
+ */
+ HANGUP = 11,
+ /**
+ * VirtualizationService sent a stop reason which was not recognised by the client library.
+ */
+ UNRECOGNISED = 0,
+};
+
+/**
+ * Create a virtual machine with given raw `config`.
+ *
+ * The created virtual machine is in stopped state. To run the created virtual machine, call
+ * {@link AVirtualMachine_start}.
+ *
+ * The caller takes ownership of the returned virtual machine object, and is responsible for
+ * releasing it by calling {@link AVirtualMachine_destroy}.
+ *
+ * \param service a handle on a virtualization service.
+ * \param config a virtual machine config object. Ownership will always be transferred from the
+ * caller, even if unsuccessful. `config` must not be reused.
+ * \param consoleOutFd a writable file descriptor for the console output, or -1. Ownership will
+ * always be transferred from the caller, even if unsuccessful.
+ * \param consoleInFd a readable file descriptor for the console input, or -1. Ownership will always
+ * be transferred from the caller, even if unsuccessful.
+ * \param logFd a writable file descriptor for the log output, or -1. Ownership will always be
+ * transferred from the caller, even if unsuccessful.
+ * \param vm an out parameter that will be set to the virtual machine handle.
+ *
+ * \return If successful, it sets `vm` and returns 0. Otherwise, it leaves `vm` untouched and
+ * returns `-EIO`.
+ */
+int AVirtualMachine_createRaw(const AVirtualizationService* service,
+ AVirtualMachineRawConfig* config, int consoleOutFd, int consoleInFd,
+ int logFd, AVirtualMachine** vm);
+
+/**
+ * Start a virtual machine.
+ *
+ * \param vm a handle on a virtual machine.
+ *
+ * \return If successful, it returns 0. Otherwise, it returns `-EIO`.
+ */
+int AVirtualMachine_start(AVirtualMachine* vm);
+
+/**
+ * Stop a virtual machine.
+ *
+ * \param vm a handle on a virtual machine.
+ *
+ * \return If successful, it returns 0. Otherwise, it returns `-EIO`.
+ */
+int AVirtualMachine_stop(AVirtualMachine* vm);
+
+/**
+ * Wait until a virtual machine stops.
+ *
+ * \param vm a handle on a virtual machine.
+ *
+ * \return The reason why the virtual machine stopped.
+ */
+enum StopReason AVirtualMachine_waitForStop(AVirtualMachine* vm);
+
+/**
+ * Destroy a virtual machine.
+ *
+ * `AVirtualMachine_destroy` does nothing if `vm` is null. A destroyed virtual machine must not be
+ * reused.
+ *
+ * \param vm a handle on a virtual machine.
+ */
+void AVirtualMachine_destroy(AVirtualMachine* vm);
+
+__END_DECLS
diff --git a/libs/libavf/libavf.map.txt b/libs/libavf/libavf.map.txt
new file mode 100644
index 0000000..ecb4cc9
--- /dev/null
+++ b/libs/libavf/libavf.map.txt
@@ -0,0 +1,24 @@
+LIBAVF {
+ global:
+ AVirtualMachineRawConfig_create; # apex llndk
+ AVirtualMachineRawConfig_destroy; # apex llndk
+ AVirtualMachineRawConfig_setName; # apex llndk
+ AVirtualMachineRawConfig_setInstanceId; # apex llndk
+ AVirtualMachineRawConfig_setKernel; # apex llndk
+ AVirtualMachineRawConfig_setInitRd; # apex llndk
+ AVirtualMachineRawConfig_addDisk; # apex llndk
+ AVirtualMachineRawConfig_setMemoryMib; # apex llndk
+ AVirtualMachineRawConfig_setProtectedVm; # apex llndk
+ AVirtualMachineRawConfig_setBalloon; # apex llndk
+ AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod; # apex llndk
+ AVirtualMachineRawConfig_addCustomMemoryBackingFile; # apex llndk
+ AVirtualizationService_create; # apex llndk
+ AVirtualizationService_destroy; # apex llndk
+ AVirtualMachine_createRaw; # apex llndk
+ AVirtualMachine_start; # apex llndk
+ AVirtualMachine_stop; # apex llndk
+ AVirtualMachine_waitForStop; # apex llndk
+ AVirtualMachine_destroy; # apex llndk
+ local:
+ *;
+};
diff --git a/libs/libavf/src/lib.rs b/libs/libavf/src/lib.rs
new file mode 100644
index 0000000..0a8f891
--- /dev/null
+++ b/libs/libavf/src/lib.rs
@@ -0,0 +1,413 @@
+// 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.
+
+//! Stable C library for AVF.
+
+use std::ffi::CStr;
+use std::fs::File;
+use std::os::fd::FromRawFd;
+use std::os::raw::{c_char, c_int};
+use std::ptr;
+
+use android_system_virtualizationservice::{
+ aidl::android::system::virtualizationservice::{
+ DiskImage::DiskImage, IVirtualizationService::IVirtualizationService,
+ VirtualMachineConfig::VirtualMachineConfig,
+ VirtualMachineRawConfig::VirtualMachineRawConfig,
+ },
+ binder::{ParcelFileDescriptor, Strong},
+};
+use avf_bindgen::StopReason;
+use vmclient::{DeathReason, VirtualizationService, VmInstance};
+
+/// Create a new virtual machine config object with no properties.
+#[no_mangle]
+pub extern "C" fn AVirtualMachineRawConfig_create() -> *mut VirtualMachineRawConfig {
+ let config = Box::new(VirtualMachineRawConfig {
+ platformVersion: "~1.0".to_owned(),
+ ..Default::default()
+ });
+ Box::into_raw(config)
+}
+
+/// Destroy a virtual machine config object.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `config` must not be
+/// used after deletion.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_destroy(config: *mut VirtualMachineRawConfig) {
+ if !config.is_null() {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ unsafe {
+ let _ = Box::from_raw(config);
+ }
+ }
+}
+
+/// Set a name of a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setName(
+ config: *mut VirtualMachineRawConfig,
+ name: *const c_char,
+) -> c_int {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ // SAFETY: `name` is assumed to be a pointer to a valid C string.
+ config.name = unsafe { CStr::from_ptr(name) }.to_string_lossy().into_owned();
+ 0
+}
+
+/// Set an instance ID of a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `instanceId` must be a
+/// valid, non-null pointer to 64-byte data.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setInstanceId(
+ config: *mut VirtualMachineRawConfig,
+ instance_id: *const u8,
+) -> c_int {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ // SAFETY: `instanceId` is assumed to be a valid pointer to 64 bytes of memory. `config`
+ // is assumed to be a valid object returned by AVirtuaMachineConfig_create.
+ // Both never overlap.
+ unsafe {
+ ptr::copy_nonoverlapping(instance_id, config.instanceId.as_mut_ptr(), 64);
+ }
+ 0
+}
+
+/// Set a kernel image of a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
+/// file descriptor or -1. `AVirtualMachineRawConfig_setKernel` takes ownership of `fd` and `fd`
+/// will be closed upon `AVirtualMachineRawConfig_delete`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setKernel(
+ config: *mut VirtualMachineRawConfig,
+ fd: c_int,
+) -> c_int {
+ let file = get_file_from_fd(fd);
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ config.kernel = file.map(ParcelFileDescriptor::new);
+ 0
+}
+
+/// Set an init rd of a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
+/// file descriptor or -1. `AVirtualMachineRawConfig_setInitRd` takes ownership of `fd` and `fd`
+/// will be closed upon `AVirtualMachineRawConfig_delete`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setInitRd(
+ config: *mut VirtualMachineRawConfig,
+ fd: c_int,
+) -> c_int {
+ let file = get_file_from_fd(fd);
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ config.initrd = file.map(ParcelFileDescriptor::new);
+ 0
+}
+
+/// Add a disk for a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
+/// file descriptor. `AVirtualMachineRawConfig_addDisk` takes ownership of `fd` and `fd` will be
+/// closed upon `AVirtualMachineRawConfig_delete`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_addDisk(
+ config: *mut VirtualMachineRawConfig,
+ fd: c_int,
+ writable: bool,
+) -> c_int {
+ let file = get_file_from_fd(fd);
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ match file {
+ // partition not supported yet
+ None => -libc::EINVAL,
+ Some(file) => {
+ config.disks.push(DiskImage {
+ image: Some(ParcelFileDescriptor::new(file)),
+ writable,
+ ..Default::default()
+ });
+ 0
+ }
+ }
+}
+
+/// Set how much memory will be given to a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setMemoryMib(
+ config: *mut VirtualMachineRawConfig,
+ memory_mib: i32,
+) -> c_int {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ config.memoryMib = memory_mib;
+ 0
+}
+
+/// Set whether a virtual machine is protected or not.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setProtectedVm(
+ config: *mut VirtualMachineRawConfig,
+ protected_vm: bool,
+) -> c_int {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ config.protectedVm = protected_vm;
+ 0
+}
+
+/// Set whether a virtual machine uses memory ballooning or not.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setBalloon(
+ config: *mut VirtualMachineRawConfig,
+ balloon: bool,
+) -> c_int {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ config.noBalloon = !balloon;
+ 0
+}
+
+/// NOT IMPLEMENTED.
+///
+/// # Returns
+/// It always returns `-ENOTSUP`.
+#[no_mangle]
+pub extern "C" fn AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod(
+ _config: *mut VirtualMachineRawConfig,
+ _enable: bool,
+) -> c_int {
+ -libc::ENOTSUP
+}
+
+/// NOT IMPLEMENTED.
+///
+/// # Returns
+/// It always returns `-ENOTSUP`.
+#[no_mangle]
+pub extern "C" fn AVirtualMachineRawConfig_addCustomMemoryBackingFile(
+ _config: *mut VirtualMachineRawConfig,
+ _fd: c_int,
+ _range_start: usize,
+ _range_end: usize,
+) -> c_int {
+ -libc::ENOTSUP
+}
+
+/// Spawn a new instance of `virtmgr`, a child process that will host the `VirtualizationService`
+/// AIDL service, and connect to the child process.
+///
+/// # Safety
+/// `service_ptr` must be a valid, non-null pointer to a mutable raw pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualizationService_create(
+ service_ptr: *mut *mut Strong<dyn IVirtualizationService>,
+ early: bool,
+) -> c_int {
+ let virtmgr =
+ if early { VirtualizationService::new_early() } else { VirtualizationService::new() };
+ let virtmgr = match virtmgr {
+ Ok(virtmgr) => virtmgr,
+ Err(e) => return -e.raw_os_error().unwrap_or(libc::EIO),
+ };
+ match virtmgr.connect() {
+ Ok(service) => {
+ // SAFETY: `service` is assumed to be a valid, non-null pointer to a mutable raw
+ // pointer. `service` is the only reference here and `config` takes
+ // ownership.
+ unsafe {
+ *service_ptr = Box::into_raw(Box::new(service));
+ }
+ 0
+ }
+ Err(_) => -libc::ECONNREFUSED,
+ }
+}
+
+/// Destroy a VirtualizationService object.
+///
+/// # Safety
+/// `service` must be a pointer returned by `AVirtualizationService_create` or
+/// `AVirtualizationService_create_early`. `service` must not be reused after deletion.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualizationService_destroy(
+ service: *mut Strong<dyn IVirtualizationService>,
+) {
+ if !service.is_null() {
+ // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
+ // `AVirtualizationService_create`. It's the only reference to the object.
+ unsafe {
+ let _ = Box::from_raw(service);
+ }
+ }
+}
+
+/// Create a virtual machine with given `config`.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `service` must be a
+/// pointer returned by `AVirtualMachineRawConfig_create`. `vm_ptr` must be a valid, non-null
+/// pointer to a mutable raw pointer. `console_out_fd`, `console_in_fd`, and `log_fd` must be a
+/// valid file descriptor or -1. `AVirtualMachine_create` takes ownership of `console_out_fd`,
+/// `console_in_fd`, and `log_fd`, and taken file descriptors must not be reused.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachine_createRaw(
+ service: *const Strong<dyn IVirtualizationService>,
+ config: *mut VirtualMachineRawConfig,
+ console_out_fd: c_int,
+ console_in_fd: c_int,
+ log_fd: c_int,
+ vm_ptr: *mut *mut VmInstance,
+) -> c_int {
+ // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
+ // `AVirtualizationService_create` or `AVirtualizationService_create_early`. It's the only
+ // reference to the object.
+ let service = unsafe { &*service };
+
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // `AVirtualMachineRawConfig_create`. It's the only reference to the object.
+ let config = unsafe { *Box::from_raw(config) };
+ let config = VirtualMachineConfig::RawConfig(config);
+
+ let console_out = get_file_from_fd(console_out_fd);
+ let console_in = get_file_from_fd(console_in_fd);
+ let log = get_file_from_fd(log_fd);
+
+ match VmInstance::create(service.as_ref(), &config, console_out, console_in, log, None, None) {
+ Ok(vm) => {
+ // SAFETY: `vm_ptr` is assumed to be a valid, non-null pointer to a mutable raw pointer.
+ // `vm` is the only reference here and `vm_ptr` takes ownership.
+ unsafe {
+ *vm_ptr = Box::into_raw(Box::new(vm));
+ }
+ 0
+ }
+ Err(_) => -libc::EIO,
+ }
+}
+
+/// Start a virtual machine.
+///
+/// # Safety
+/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachine_start(vm: *const VmInstance) -> c_int {
+ // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
+ // `AVirtualMachine_createRaw`. It's the only reference to the object.
+ let vm = unsafe { &*vm };
+ match vm.start() {
+ Ok(_) => 0,
+ Err(_) => -libc::EIO,
+ }
+}
+
+/// Stop a virtual machine.
+///
+/// # Safety
+/// `vm` must be a pointer returned by `AVirtualMachine_create`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachine_stop(vm: *const VmInstance) -> c_int {
+ // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
+ // `AVirtualMachine_createRaw`. It's the only reference to the object.
+ let vm = unsafe { &*vm };
+ match vm.stop() {
+ Ok(_) => 0,
+ Err(_) => -libc::EIO,
+ }
+}
+
+/// Wait until a virtual machine stops.
+///
+/// # Safety
+/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachine_waitForStop(vm: *const VmInstance) -> StopReason {
+ // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachine_create. It's the only reference to the object.
+ let vm = unsafe { &*vm };
+ match vm.wait_for_death() {
+ DeathReason::VirtualizationServiceDied => StopReason::VIRTUALIZATION_SERVICE_DIED,
+ DeathReason::InfrastructureError => StopReason::INFRASTRUCTURE_ERROR,
+ DeathReason::Killed => StopReason::KILLED,
+ DeathReason::Unknown => StopReason::UNKNOWN,
+ DeathReason::Shutdown => StopReason::SHUTDOWN,
+ DeathReason::StartFailed => StopReason::START_FAILED,
+ DeathReason::Reboot => StopReason::REBOOT,
+ DeathReason::Crash => StopReason::CRASH,
+ DeathReason::PvmFirmwarePublicKeyMismatch => StopReason::PVM_FIRMWARE_PUBLIC_KEY_MISMATCH,
+ DeathReason::PvmFirmwareInstanceImageChanged => {
+ StopReason::PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED
+ }
+ DeathReason::Hangup => StopReason::HANGUP,
+ _ => StopReason::UNRECOGNISED,
+ }
+}
+
+/// Destroy a virtual machine.
+///
+/// # Safety
+/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `vm` must not be reused after
+/// deletion.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachine_destroy(vm: *mut VmInstance) {
+ if !vm.is_null() {
+ // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachine_create. It's the only reference to the object.
+ unsafe {
+ let _ = Box::from_raw(vm);
+ }
+ }
+}
+
+fn get_file_from_fd(fd: i32) -> Option<File> {
+ if fd == -1 {
+ None
+ } else {
+ // SAFETY: transferring ownership of `fd` from the caller
+ Some(unsafe { File::from_raw_fd(fd) })
+ }
+}
diff --git a/libs/libvmclient/src/lib.rs b/libs/libvmclient/src/lib.rs
index 13630c0..c0baea5 100644
--- a/libs/libvmclient/src/lib.rs
+++ b/libs/libvmclient/src/lib.rs
@@ -243,6 +243,11 @@
self.vm.start()
}
+ /// Stops the VM.
+ pub fn stop(&self) -> BinderResult<()> {
+ self.vm.stop()
+ }
+
/// Returns the CID used for vsock connections to the VM.
pub fn cid(&self) -> i32 {
self.cid