Merge "Add -r(release) flag to build.sh to build images for all architectures." into main
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
index 0774bb1..83c6b4c 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
@@ -29,14 +29,16 @@
import android.os.RemoteException;
import android.text.format.Formatter;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
-import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.virtualization.vmlauncher.InstallUtils;
import com.google.android.material.progressindicator.LinearProgressIndicator;
+import com.google.android.material.snackbar.Snackbar;
import java.lang.ref.WeakReference;
import java.util.concurrent.ExecutorService;
@@ -45,7 +47,6 @@
private static final String TAG = "LinuxInstaller";
private static final long ESTIMATED_IMG_SIZE_BYTES = FileUtils.parseSize("350MB");
- static final String EXTRA_AUTO_DOWNLOAD = "auto_download";
private ExecutorService mExecutorService;
private CheckBox mWaitForWifiCheckbox;
@@ -80,17 +81,25 @@
requestInstall();
});
- if (getIntent().getBooleanExtra(EXTRA_AUTO_DOWNLOAD, false)) {
- Log.i(TAG, "Auto downloading");
- requestInstall();
- }
-
Intent intent = new Intent(this, InstallerService.class);
mInstallerServiceConnection = new InstallerServiceConnection(this);
if (!bindService(intent, mInstallerServiceConnection, Context.BIND_AUTO_CREATE)) {
handleCriticalError(new Exception("Failed to connect to installer service"));
}
+ }
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (Build.isDebuggable() && InstallUtils.payloadFromExternalStorageExists()) {
+ Snackbar.make(
+ findViewById(android.R.id.content),
+ "Auto installing",
+ Snackbar.LENGTH_LONG)
+ .show();
+ requestInstall();
+ }
}
@Override
@@ -103,6 +112,15 @@
super.onDestroy();
}
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BUTTON_START) {
+ requestInstall();
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
@VisibleForTesting
public boolean waitForInstallCompleted(long timeoutMillis) {
return mInstallCompleted.block(timeoutMillis);
@@ -110,10 +128,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);
@@ -189,9 +207,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/InstallerService.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
index 2ce0cdc..f97f16f 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
@@ -17,8 +17,6 @@
package com.android.virtualization.terminal;
import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
@@ -54,7 +52,6 @@
public class InstallerService extends Service {
private static final String TAG = "InstallerService";
- private static final String NOTIFICATION_CHANNEL_ID = "installer";
private static final int NOTIFICATION_ID = 1313; // any unique number among notifications
private static final String IMAGE_URL =
@@ -81,23 +78,12 @@
public void onCreate() {
super.onCreate();
- // Create mandatory notification
- NotificationManager manager = getSystemService(NotificationManager.class);
- if (manager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null) {
- NotificationChannel channel =
- new NotificationChannel(
- NOTIFICATION_CHANNEL_ID,
- getString(R.string.installer_notif_title_text),
- NotificationManager.IMPORTANCE_DEFAULT);
- manager.createNotificationChannel(channel);
- }
-
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(
this, /* requestCode= */ 0, intent, PendingIntent.FLAG_IMMUTABLE);
mNotification =
- new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+ new Notification.Builder(this, this.getPackageName())
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(getString(R.string.installer_notif_title_text))
.setContentText(getString(R.string.installer_notif_desc_text))
@@ -134,16 +120,21 @@
}
private void requestInstall() {
- Log.i(TAG, "Installing..");
+ synchronized (mLock) {
+ if (mIsInstalling) {
+ Log.i(TAG, "already installing..");
+ return;
+ } else {
+ Log.i(TAG, "installing..");
+ mIsInstalling = true;
+ }
+ }
// Make service to be long running, even after unbind() when InstallerActivity is destroyed
// The service will still be destroyed if task is remove.
startService(new Intent(this, InstallerService.class));
startForeground(
NOTIFICATION_ID, mNotification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
- synchronized (mLock) {
- mIsInstalling = true;
- }
mExecutorService.execute(
() -> {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index ebf6154..10451ec 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -88,22 +88,25 @@
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
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- boolean launchInstaller = installIfNecessary();
-
NotificationManager notificationManager = getSystemService(NotificationManager.class);
- if (notificationManager.getNotificationChannel(TAG) == null) {
- NotificationChannel notificationChannel =
- new NotificationChannel(TAG, TAG, NotificationManager.IMPORTANCE_LOW);
- notificationManager.createNotificationChannel(notificationChannel);
+ if (notificationManager.getNotificationChannel(this.getPackageName()) == null) {
+ NotificationChannel channel =
+ new NotificationChannel(
+ this.getPackageName(),
+ getString(R.string.app_name),
+ NotificationManager.IMPORTANCE_DEFAULT);
+ notificationManager.createNotificationChannel(channel);
}
+ boolean launchInstaller = installIfNecessary();
+
setContentView(R.layout.activity_headless);
diskSizeStep = getResources().getInteger(
R.integer.disk_size_round_up_step_size_in_mb) << 20;
@@ -122,27 +125,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 +428,7 @@
finish();
}
if (!Environment.isExternalStorageManager()) {
- requestStoragePermissions(this, manageExternalStorageActivityResultLauncher);
+ requestStoragePermissions(this, mManageExternalStorageActivityResultLauncher);
} else {
startVm();
}
@@ -481,8 +474,7 @@
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
Icon icon = Icon.createWithResource(getResources(), R.drawable.ic_launcher_foreground);
Notification notification =
- new Notification.Builder(this, TAG)
- .setChannelId(TAG)
+ new Notification.Builder(this, this.getPackageName())
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(
getResources().getString(R.string.service_notification_title))
@@ -550,9 +542,8 @@
SharedPreferences.Editor editor = sharedPref.edit();
long currentDiskSize = getFilesystemSize(file);
- // The default partition size is 6G
long newSizeInBytes = sharedPref.getLong(getString(R.string.preference_disk_size_key),
- 6L << 30);
+ roundUpDiskSize(currentDiskSize));
editor.putLong(getString(R.string.preference_disk_size_key), newSizeInBytes);
editor.apply();
diff --git a/android/TerminalApp/res/layout/settings_disk_resize.xml b/android/TerminalApp/res/layout/settings_disk_resize.xml
index 6c8c2c1..d80f4f9 100644
--- a/android/TerminalApp/res/layout/settings_disk_resize.xml
+++ b/android/TerminalApp/res/layout/settings_disk_resize.xml
@@ -64,9 +64,11 @@
android:id="@+id/settings_disk_resize_cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:maxWidth="150sp"
+ android:hyphenationFrequency="normal"
android:text="@string/settings_disk_resize_resize_cancel"
android:visibility="invisible"
- android:layout_marginVertical="48dp"
+ android:layout_marginTop="48dp"
android:layout_marginHorizontal="8dp"
app:layout_constraintTop_toTopOf="@+id/settings_disk_resize_disk_size_slider"
app:layout_constraintBottom_toBottomOf="parent"
@@ -76,10 +78,13 @@
android:id="@+id/settings_disk_resize_resize_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:maxWidth="150sp"
+ android:hyphenationFrequency="normal"
android:text="@string/settings_disk_resize_resize_restart_vm_to_apply"
android:visibility="invisible"
+ android:layout_marginTop="48dp"
app:layout_constraintTop_toTopOf="@+id/settings_disk_resize_disk_size_slider"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/android/TerminalApp/res/values/strings.xml b/android/TerminalApp/res/values/strings.xml
index ca803ec..c89fcfa 100644
--- a/android/TerminalApp/res/values/strings.xml
+++ b/android/TerminalApp/res/values/strings.xml
@@ -61,10 +61,10 @@
<string name="settings_disk_resize_resize_gb_assigned_format"><xliff:g id="assigned_size" example="10GB">%1$s</xliff:g> assigned</string>
<!-- Settings menu option description format of the maximum resizable disk size. [CHAR LIMIT=none] -->
<string name="settings_disk_resize_resize_gb_max_format"><xliff:g id="max_size" example="256GB">%1$s</xliff:g> max</string>
- <!-- Settings menu button to cancel disk resize. [CHAR LIMIT=32] -->
+ <!-- Settings menu button to cancel disk resize. [CHAR LIMIT=16] -->
<string name="settings_disk_resize_resize_cancel">Cancel</string>
- <!-- Settings menu button to apply change that requires to restart VM (abbrev of virtual machine). [CHAR LIMIT=64] -->
- <string name="settings_disk_resize_resize_restart_vm_to_apply">Restart VM to apply</string>
+ <!-- Settings menu button to apply change that requires to restart Terminal app. [CHAR LIMIT=20] -->
+ <string name="settings_disk_resize_resize_restart_vm_to_apply">Restart to apply</string>
<!-- Settings menu title for 'port forwarding' [CHAR LIMIT=none] -->
<string name="settings_port_forwarding_title">Port Forwarding</string>
@@ -83,25 +83,25 @@
<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] -->
+ <!-- Notification action button for settings [CHAR LIMIT=20] -->
<string name="service_notification_settings">Settings</string>
<!-- 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>
- <!-- Notification action button for closing the virtual machine [CHAR LIMIT=none] -->
+ <string name="service_notification_content">Click to open the terminal</string>
+ <!-- Notification action button for closing the virtual machine [CHAR LIMIT=20] -->
<string name="service_notification_quit_action">Close</string>
</resources>
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index e2b2804..1cae344 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -21,7 +21,7 @@
use crate::debug_config::DebugConfig;
use crate::dt_overlay::{create_device_tree_overlay, VM_DT_OVERLAY_MAX_SIZE, VM_DT_OVERLAY_PATH};
use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images, add_microdroid_vendor_image};
-use crate::selinux::{getfilecon, SeContext};
+use crate::selinux::{check_tee_service_permission, getfilecon, getprevcon, SeContext};
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::{
Certificate::Certificate,
@@ -502,6 +502,9 @@
check_config_allowed_for_early_vms(config)?;
}
+ let caller_secontext = getprevcon().or_service_specific_exception(-1)?;
+ info!("callers secontext: {}", caller_secontext);
+
// Allocating VM context checks the MANAGE_VIRTUAL_MACHINE permission.
let (vm_context, cid, temporary_directory) = if cfg!(early) {
self.create_early_vm_context(config)?
@@ -534,7 +537,13 @@
clone_or_prepare_logger_fd(console_out_fd, format!("Console({})", cid))?;
let console_in_fd = console_in_fd.map(clone_file).transpose()?;
let log_fd = clone_or_prepare_logger_fd(log_fd, format!("Log({})", cid))?;
- let dump_dt_fd = dump_dt_fd.map(clone_file).transpose()?;
+ let dump_dt_fd = if let Some(fd) = dump_dt_fd {
+ Some(clone_file(fd)?)
+ } else if debug_config.dump_device_tree {
+ Some(prepare_dump_dt_file(&temporary_directory)?)
+ } else {
+ None
+ };
// Counter to generate unique IDs for temporary image files.
let mut next_temporary_image_id = 0;
@@ -558,6 +567,12 @@
let config = config.as_ref();
*is_protected = config.protectedVm;
+ if !config.teeServices.is_empty() {
+ check_tee_service_permission(&caller_secontext, &config.teeServices)
+ .with_log()
+ .or_binder_exception(ExceptionCode::SECURITY)?;
+ }
+
// Check if partition images are labeled incorrectly. This is to prevent random images
// which are not protected by the Android Verified Boot (e.g. bits downloaded by apps) from
// being loaded in a pVM. This applies to everything but the instance image in the raw
@@ -1152,6 +1167,8 @@
for param in custom_config.extraKernelCmdlineParams.iter() {
append_kernel_param(param, &mut vm_config);
}
+
+ vm_config.teeServices.clone_from(&custom_config.teeServices);
}
// Unfortunately specifying page_shift = 14 in bootconfig doesn't enable 16k pages emulation,
@@ -1669,6 +1686,16 @@
Ok(ramdump)
}
+/// Create the empty device tree dump file
+fn prepare_dump_dt_file(temporary_directory: &Path) -> binder::Result<File> {
+ let path = temporary_directory.join("device_tree.dtb");
+ let file = File::create(path)
+ .context("Failed to prepare device tree dump file")
+ .with_log()
+ .or_service_specific_exception(-1)?;
+ Ok(file)
+}
+
fn is_protected(config: &VirtualMachineConfig) -> bool {
match config {
VirtualMachineConfig::RawConfig(config) => config.protectedVm,
@@ -1759,6 +1786,26 @@
Ok(())
}
+fn check_no_tee_services(config: &VirtualMachineConfig) -> binder::Result<()> {
+ match config {
+ VirtualMachineConfig::RawConfig(config) => {
+ if !config.teeServices.is_empty() {
+ return Err(anyhow!("tee_services_allowlist feature is disabled"))
+ .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
+ }
+ }
+ VirtualMachineConfig::AppConfig(config) => {
+ if let Some(custom_config) = &config.customConfig {
+ if !custom_config.teeServices.is_empty() {
+ return Err(anyhow!("tee_services_allowlist feature is disabled"))
+ .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
+ }
+ }
+ }
+ };
+ Ok(())
+}
+
fn check_protected_vm_is_supported() -> binder::Result<()> {
let is_pvm_supported =
hypervisor_props::is_protected_vm_supported().or_service_specific_exception(-1)?;
@@ -1783,6 +1830,9 @@
if !cfg!(debuggable_vms_improvements) {
check_no_extra_kernel_cmdline_params(config)?;
}
+ if !cfg!(tee_services_allowlist) {
+ check_no_tee_services(config)?;
+ }
Ok(())
}
diff --git a/android/virtmgr/src/debug_config.rs b/android/virtmgr/src/debug_config.rs
index 74559de..6e2bfef 100644
--- a/android/virtmgr/src/debug_config.rs
+++ b/android/virtmgr/src/debug_config.rs
@@ -30,6 +30,7 @@
const CUSTOM_DEBUG_POLICY_OVERLAY_SYSPROP: &str =
"hypervisor.virtualizationmanager.debug_policy.path";
+const DUMP_DT_SYSPROP: &str = "hypervisor.virtualizationmanager.dump_device_tree";
const DEVICE_TREE_EMPTY_TREE_SIZE_BYTES: usize = 100; // rough estimation.
struct DPPath {
@@ -183,6 +184,7 @@
#[derive(Debug, Default)]
pub struct DebugConfig {
pub debug_level: DebugLevel,
+ pub dump_device_tree: bool,
debug_policy: DebugPolicy,
}
@@ -193,8 +195,13 @@
info!("Debug policy is disabled");
Default::default()
});
+ let dump_dt_sysprop = system_properties::read_bool(DUMP_DT_SYSPROP, false);
+ let dump_device_tree = dump_dt_sysprop.unwrap_or_else(|e| {
+ warn!("Failed to read sysprop {DUMP_DT_SYSPROP}: {e}");
+ false
+ });
- Self { debug_level, debug_policy }
+ Self { debug_level, debug_policy, dump_device_tree }
}
fn get_debug_policy() -> Option<DebugPolicy> {
diff --git a/android/virtmgr/src/selinux.rs b/android/virtmgr/src/selinux.rs
index ba62b7f..719c9a9 100644
--- a/android/virtmgr/src/selinux.rs
+++ b/android/virtmgr/src/selinux.rs
@@ -15,13 +15,30 @@
//! Wrapper to libselinux
use anyhow::{anyhow, bail, Context, Result};
-use std::ffi::{CStr, CString};
+use std::ffi::{c_int, CStr, CString};
use std::fmt;
use std::io;
use std::ops::Deref;
use std::os::fd::AsRawFd;
use std::os::raw::c_char;
use std::ptr;
+use std::sync;
+
+static SELINUX_LOG_INIT: sync::Once = sync::Once::new();
+
+fn redirect_selinux_logs_to_logcat() {
+ let cb =
+ selinux_bindgen::selinux_callback { func_log: Some(selinux_bindgen::selinux_log_callback) };
+ // SAFETY: `selinux_set_callback` assigns the static lifetime function pointer
+ // `selinux_log_callback` to a static lifetime variable.
+ unsafe {
+ selinux_bindgen::selinux_set_callback(selinux_bindgen::SELINUX_CB_LOG as c_int, cb);
+ }
+}
+
+fn init_logger_once() {
+ SELINUX_LOG_INIT.call_once(redirect_selinux_logs_to_logcat)
+}
// Partially copied from system/security/keystore2/selinux/src/lib.rs
/// SeContext represents an SELinux context string. It can take ownership of a raw
@@ -101,6 +118,56 @@
}
}
+/// Takes ownership of context handle returned by `selinux_android_tee_service_context_handle`
+/// and closes it via `selabel_close` when dropped.
+struct TeeServiceSelinuxBackend {
+ handle: *mut selinux_bindgen::selabel_handle,
+}
+
+impl TeeServiceSelinuxBackend {
+ const BACKEND_ID: i32 = selinux_bindgen::SELABEL_CTX_ANDROID_SERVICE as i32;
+
+ /// Creates a new instance representing selinux context handle returned from
+ /// `selinux_android_tee_service_context_handle`.
+ fn new() -> Result<Self> {
+ // SAFETY: selinux_android_tee_service_context_handle is always safe to call. The returned
+ // handle is valid until `selabel_close` is called on it (see the safety comment on the drop
+ // trait).
+ let handle = unsafe { selinux_bindgen::selinux_android_tee_service_context_handle() };
+ if handle.is_null() {
+ Err(anyhow!("selinux_android_tee_service_context_handle returned a NULL context"))
+ } else {
+ Ok(TeeServiceSelinuxBackend { handle })
+ }
+ }
+
+ fn lookup(&self, tee_service: &str) -> Result<SeContext> {
+ let mut con: *mut c_char = ptr::null_mut();
+ let c_key = CString::new(tee_service).context("failed to convert to CString")?;
+ // SAFETY: the returned pointer `con` is valid until `freecon` is called on it.
+ match unsafe {
+ selinux_bindgen::selabel_lookup(self.handle, &mut con, c_key.as_ptr(), Self::BACKEND_ID)
+ } {
+ 0 => {
+ if !con.is_null() {
+ Ok(SeContext::Raw(con))
+ } else {
+ Err(anyhow!("selabel_lookup returned a NULL context"))
+ }
+ }
+ _ => Err(anyhow!(io::Error::last_os_error())).context("selabel_lookup failed"),
+ }
+ }
+}
+
+impl Drop for TeeServiceSelinuxBackend {
+ fn drop(&mut self) {
+ // SAFETY: the TeeServiceSelinuxBackend is created only with a pointer is set by
+ // libselinux and has to be freed with `selabel_close`.
+ unsafe { selinux_bindgen::selabel_close(self.handle) };
+ }
+}
+
pub fn getfilecon<F: AsRawFd>(file: &F) -> Result<SeContext> {
let fd = file.as_raw_fd();
let mut con: *mut c_char = ptr::null_mut();
@@ -117,3 +184,92 @@
_ => Err(anyhow!(io::Error::last_os_error())).context("fgetfilecon failed"),
}
}
+
+pub fn getprevcon() -> Result<SeContext> {
+ let mut con: *mut c_char = ptr::null_mut();
+ // SAFETY: the returned pointer `con` is wrapped in SeContext::Raw which is freed with
+ // `freecon` when it is dropped.
+ match unsafe { selinux_bindgen::getprevcon(&mut con) } {
+ 0.. => {
+ if !con.is_null() {
+ Ok(SeContext::Raw(con))
+ } else {
+ Err(anyhow!("getprevcon returned a NULL context"))
+ }
+ }
+ _ => Err(anyhow!(io::Error::last_os_error())).context("getprevcon failed"),
+ }
+}
+
+// Wrapper around selinux_check_access
+fn check_access(source: &CStr, target: &CStr, tclass: &str, perm: &str) -> Result<()> {
+ let c_tclass = CString::new(tclass).context("failed to convert tclass to CString")?;
+ let c_perm = CString::new(perm).context("failed to convert perm to CString")?;
+
+ // SAFETY: lifecycle of pointers passed to the selinux_check_access outlive the duration of the
+ // call.
+ match unsafe {
+ selinux_bindgen::selinux_check_access(
+ source.as_ptr(),
+ target.as_ptr(),
+ c_tclass.as_ptr(),
+ c_perm.as_ptr(),
+ ptr::null_mut(),
+ )
+ } {
+ 0 => Ok(()),
+ _ => Err(anyhow!(io::Error::last_os_error())).with_context(|| {
+ format!(
+ "check_access: Failed with sctx: {:?} tctx: {:?} tclass: {:?} perm {:?}",
+ source, target, tclass, perm
+ )
+ }),
+ }
+}
+
+pub fn check_tee_service_permission(caller_ctx: &SeContext, tee_services: &[String]) -> Result<()> {
+ init_logger_once();
+
+ let backend = TeeServiceSelinuxBackend::new()?;
+
+ for tee_service in tee_services {
+ let tee_service_ctx = backend.lookup(tee_service)?;
+ check_access(caller_ctx, &tee_service_ctx, "tee_service", "use")
+ .with_context(|| format!("permission denied for {:?}", tee_service))?;
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ #[ignore = "disabling test while investigating b/379087641"]
+ fn test_check_tee_service_permission_has_permission() -> Result<()> {
+ if cfg!(not(tee_services_allowlist)) {
+ // Skip test on release configurations without tee_services_allowlist feature enabled.
+ return Ok(());
+ }
+
+ let caller_ctx = SeContext::new("u:r:shell:s0")?;
+ let tee_services = [String::from("test_pkvm_tee_service")];
+ check_tee_service_permission(&caller_ctx, &tee_services)
+ }
+
+ #[test]
+ #[ignore = "disabling test while investigating b/379087641"]
+ fn test_check_tee_service_permission_invalid_tee_service() -> Result<()> {
+ if cfg!(not(tee_services_allowlist)) {
+ // Skip test on release configurations without tee_services_allowlist feature enabled.
+ return Ok(());
+ }
+
+ let caller_ctx = SeContext::new("u:r:shell:s0")?;
+ let tee_services = [String::from("test_tee_service_does_not_exist")];
+ let ret = check_tee_service_permission(&caller_ctx, &tee_services);
+ assert!(ret.is_err());
+ Ok(())
+ }
+}
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index 9123742..114a851 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -130,6 +130,9 @@
/** Additional parameters to pass to the VM's kernel cmdline. */
String[] extraKernelCmdlineParams;
+
+ /** List of tee services this VM wants to access */
+ String[] teeServices;
}
/** Configuration parameters guarded by android.permission.USE_CUSTOM_VIRTUAL_MACHINE */
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index 9f2a23e..5728a68 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -110,4 +110,7 @@
/** Enable or disable USB passthrough support */
@nullable UsbConfig usbConfig;
+
+ /** List of tee services this VM wants to access */
+ String[] teeServices;
}
diff --git a/android/vm/src/main.rs b/android/vm/src/main.rs
index 110e0ca..7bfd957 100644
--- a/android/vm/src/main.rs
+++ b/android/vm/src/main.rs
@@ -72,6 +72,11 @@
/// Boost uclamp to stablise results for benchmarks.
#[arg(short, long)]
boost_uclamp: bool,
+
+ /// Secure services this VM wants to access.
+ #[cfg(tee_services_allowlist)]
+ #[arg(long)]
+ tee_services: Vec<String>,
}
impl CommonConfig {
@@ -84,6 +89,16 @@
}
}
}
+
+ fn tee_services(&self) -> &[String] {
+ cfg_if::cfg_if! {
+ if #[cfg(tee_services_allowlist)] {
+ &self.tee_services
+ } else {
+ &[]
+ }
+ }
+ }
}
#[derive(Args, Default)]
diff --git a/android/vm/src/run.rs b/android/vm/src/run.rs
index b07a472..2157ea8 100644
--- a/android/vm/src/run.rs
+++ b/android/vm/src/run.rs
@@ -156,6 +156,7 @@
})
.collect::<Result<_, _>>()?,
networkSupported: config.common.network_supported(),
+ teeServices: config.common.tee_services().to_vec(),
..Default::default()
};
@@ -263,8 +264,8 @@
if let Some(mem) = config.common.mem {
vm_config.memoryMib = mem as i32;
}
- if let Some(name) = config.common.name {
- vm_config.name = name;
+ if let Some(ref name) = config.common.name {
+ vm_config.name = name.to_string();
} else {
vm_config.name = String::from("VmRun");
}
@@ -274,6 +275,7 @@
vm_config.cpuTopology = config.common.cpu_topology;
vm_config.hugePages = config.common.hugepages;
vm_config.boostUclamp = config.common.boost_uclamp;
+ vm_config.teeServices = config.common.tee_services().to_vec();
run(
get_service()?.as_ref(),
&VirtualMachineConfig::RawConfig(vm_config),
diff --git a/build/Android.bp b/build/Android.bp
index 4c4fbba..2b97927 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -41,6 +41,9 @@
}) + select(release_flag("RELEASE_AVF_ENABLE_VENDOR_MODULES"), {
true: ["vendor_modules"],
default: [],
+ }) + select(release_flag("RELEASE_AVF_ENABLE_VM_TO_TEE_SERVICES_ALLOWLIST"), {
+ true: ["tee_services_allowlist"],
+ default: [],
}) + select(release_flag("RELEASE_AVF_ENABLE_VIRT_CPUFREQ"), {
true: ["virt_cpufreq"],
default: [],
diff --git a/build/debian/fai_config/package_config/AVF b/build/debian/fai_config/package_config/AVF
index 7d86d41..2e55e90 100644
--- a/build/debian/fai_config/package_config/AVF
+++ b/build/debian/fai_config/package_config/AVF
@@ -1,4 +1,5 @@
PACKAGES install
-# Just for testing
-tree
+bpfcc-tools
+linux-headers-generic
+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/docs/debug/README.md b/docs/debug/README.md
index 4b42531..6e51efa 100644
--- a/docs/debug/README.md
+++ b/docs/debug/README.md
@@ -45,6 +45,25 @@
Note: `--debug full` is the default option when omitted. You need to explicitly
use `--debug none` to set the debug level to NONE.
+### Dump device tree
+
+The VMs device tree can be dumped on creation by adding the `--dump_device_tree`
+argument and passing a path where the device tree gets dumped to, as follows:
+
+```shell
+adb shell /apex/com.android.virt/bin/vm run-microdroid --dump-device-tree PATH
+```
+
+Note: you can set the system property
+`hypervisor.virtualizationmanager.dump_device_tree` to true to always dump the
+device tree to `/data/misc/virtualizationservice/$CID/device_tree.dtb` where
+$CID is the CID of the VM. To set the property, run:
+
+```shell
+adb root
+adb shell setprop hypervisor.virtualizationmanager.dump_device_tree true
+```
+
### Debug policy
Debug policy is a per-device property which forcibly enables selected debugging
diff --git a/guest/forwarder_guest_launcher/Cargo.toml b/guest/forwarder_guest_launcher/Cargo.toml
index 03fda56..c875484 100644
--- a/guest/forwarder_guest_launcher/Cargo.toml
+++ b/guest/forwarder_guest_launcher/Cargo.toml
@@ -7,9 +7,13 @@
[dependencies]
anyhow = "1.0.91"
clap = { version = "4.5.20", features = ["derive"] }
+csv-async = { version = "1.3.0", features = ["tokio"] }
env_logger = "0.11.5"
+futures = "0.3.31"
+listeners = "0.2.1"
log = "0.4.22"
prost = "0.13.3"
+serde = { version = "1.0.215", features = ["derive"] }
tokio = { version = "1.40.0", features = ["process", "rt-multi-thread"] }
tonic = "0.12.3"
vsock = "0.5.1"
diff --git a/guest/forwarder_guest_launcher/src/main.rs b/guest/forwarder_guest_launcher/src/main.rs
index abb39f6..c3fdd7e 100644
--- a/guest/forwarder_guest_launcher/src/main.rs
+++ b/guest/forwarder_guest_launcher/src/main.rs
@@ -14,19 +14,39 @@
//! Launcher of forwarder_guest
-use anyhow::Context;
+use anyhow::{anyhow, Context};
use clap::Parser;
+use csv_async::AsyncReader;
use debian_service::debian_service_client::DebianServiceClient;
-use debian_service::QueueOpeningRequest;
-use log::debug;
+use debian_service::{QueueOpeningRequest, ReportVmActivePortsRequest};
+use futures::stream::StreamExt;
+use log::{debug, error};
+use serde::Deserialize;
+use std::collections::HashSet;
+use std::process::Stdio;
+use tokio::io::BufReader;
use tokio::process::Command;
-use tonic::transport::Endpoint;
+use tokio::try_join;
+use tonic::transport::{Channel, Endpoint};
use tonic::Request;
mod debian_service {
tonic::include_proto!("com.android.virtualization.vmlauncher.proto");
}
+const NON_PREVILEGED_PORT_RANGE_START: i32 = 1024;
+const TCPSTATES_IP_4: i8 = 4;
+const TCPSTATES_STATE_LISTEN: &str = "LISTEN";
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "UPPERCASE")]
+struct TcpStateRow {
+ ip: i8,
+ lport: i32,
+ oldstate: String,
+ newstate: String,
+}
+
#[derive(Parser)]
/// Flags for running command
pub struct Args {
@@ -40,15 +60,9 @@
grpc_port: String,
}
-#[tokio::main]
-async fn main() -> Result<(), Box<dyn std::error::Error>> {
- env_logger::init();
- debug!("Starting forwarder_guest_launcher");
- let args = Args::parse();
- let addr = format!("https://{}:{}", args.host_addr, args.grpc_port);
-
- let channel = Endpoint::from_shared(addr)?.connect().await?;
- let mut client = DebianServiceClient::new(channel);
+async fn process_forwarding_request_queue(
+ mut client: DebianServiceClient<Channel>,
+) -> Result<(), Box<dyn std::error::Error>> {
let cid = vsock::get_local_cid().context("Failed to get CID of VM")?;
let mut res_stream = client
.open_forwarding_request_queue(Request::new(QueueOpeningRequest { cid: cid as i32 }))
@@ -72,5 +86,85 @@
.arg(format!("vsock:2:{}", vsock_port))
.spawn();
}
+ Err(anyhow!("process_forwarding_request_queue is terminated").into())
+}
+
+async fn send_active_ports_report(
+ listening_ports: HashSet<i32>,
+ client: &mut DebianServiceClient<Channel>,
+) -> Result<(), Box<dyn std::error::Error>> {
+ let res = client
+ .report_vm_active_ports(Request::new(ReportVmActivePortsRequest {
+ ports: listening_ports.into_iter().collect(),
+ }))
+ .await?
+ .into_inner();
+ if res.success {
+ debug!("Successfully reported active ports to the host");
+ } else {
+ error!("Failure response received from the host for reporting active ports");
+ }
+ Ok(())
+}
+
+async fn report_active_ports(
+ mut client: DebianServiceClient<Channel>,
+) -> Result<(), Box<dyn std::error::Error>> {
+ let mut cmd = Command::new("python3")
+ .arg("-u")
+ .arg("/usr/sbin/tcpstates-bpfcc")
+ .arg("-s")
+ .stdout(Stdio::piped())
+ .spawn()?;
+ let stdout = cmd.stdout.take().context("Failed to get stdout of tcpstates")?;
+ let mut csv_reader = AsyncReader::from_reader(BufReader::new(stdout));
+ let header = csv_reader.headers().await?.clone();
+
+ // TODO(b/340126051): Consider using NETLINK_SOCK_DIAG for the optimization.
+ let listeners = listeners::get_all()?;
+ // TODO(b/340126051): Support distinguished port forwarding for ipv6 as well.
+ let mut listening_ports: HashSet<_> = listeners
+ .iter()
+ .map(|x| x.socket)
+ .filter(|x| x.is_ipv4())
+ .map(|x| x.port().into())
+ .filter(|x| *x >= NON_PREVILEGED_PORT_RANGE_START) // Ignore privileged ports
+ .collect();
+ send_active_ports_report(listening_ports.clone(), &mut client).await?;
+
+ let mut records = csv_reader.records();
+ while let Some(record) = records.next().await {
+ let row: TcpStateRow = record?.deserialize(Some(&header))?;
+ if row.ip != TCPSTATES_IP_4 {
+ continue;
+ }
+ if row.lport < NON_PREVILEGED_PORT_RANGE_START {
+ continue;
+ }
+ match (row.oldstate.as_str(), row.newstate.as_str()) {
+ (_, TCPSTATES_STATE_LISTEN) => {
+ listening_ports.insert(row.lport);
+ }
+ (TCPSTATES_STATE_LISTEN, _) => {
+ listening_ports.remove(&row.lport);
+ }
+ (_, _) => continue,
+ }
+ send_active_ports_report(listening_ports.clone(), &mut client).await?;
+ }
+
+ Err(anyhow!("report_active_ports is terminated").into())
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+ env_logger::init();
+ debug!("Starting forwarder_guest_launcher");
+ let args = Args::parse();
+ let addr = format!("https://{}:{}", args.host_addr, args.grpc_port);
+ let channel = Endpoint::from_shared(addr)?.connect().await?;
+ let client = DebianServiceClient::new(channel);
+
+ try_join!(process_forwarding_request_queue(client.clone()), report_active_ports(client))?;
Ok(())
}
diff --git a/guest/microdroid_manager/src/main.rs b/guest/microdroid_manager/src/main.rs
index fa089fa..c5fa864 100644
--- a/guest/microdroid_manager/src/main.rs
+++ b/guest/microdroid_manager/src/main.rs
@@ -669,7 +669,9 @@
});
}
- command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
+ if !is_debuggable()? {
+ command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
+ }
info!("notifying payload started");
service.notifyPayloadStarted()?;
diff --git a/guest/trusty/security_vm/launcher/Android.bp b/guest/trusty/security_vm/launcher/Android.bp
index e482e02..ef32740 100644
--- a/guest/trusty/security_vm/launcher/Android.bp
+++ b/guest/trusty/security_vm/launcher/Android.bp
@@ -40,12 +40,15 @@
// python -c "import hashlib; print(hashlib.sha256(b'trusty_security_vm_salt').hexdigest())"
trusty_security_vm_salt = "75a71e967c1a1e0f805cca20465e7acf83e6a04e567a67c426d8b5a94f8d61c5"
+TRUSTY_SECURITY_VM_VERSION = 1
+
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,
+ rollback_index: TRUSTY_SECURITY_VM_VERSION,
src: ":empty_file",
enabled: false,
arch: {
diff --git a/guest/trusty/security_vm/launcher/src/main.rs b/guest/trusty/security_vm/launcher/src/main.rs
index bdb4ed8..9611f26 100644
--- a/guest/trusty/security_vm/launcher/src/main.rs
+++ b/guest/trusty/security_vm/launcher/src/main.rs
@@ -15,8 +15,8 @@
//! A client for trusty security VMs during early boot.
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
- IVirtualizationService::IVirtualizationService, VirtualMachineConfig::VirtualMachineConfig,
- VirtualMachineRawConfig::VirtualMachineRawConfig,
+ CpuTopology::CpuTopology, IVirtualizationService::IVirtualizationService,
+ VirtualMachineConfig::VirtualMachineConfig, VirtualMachineRawConfig::VirtualMachineRawConfig,
};
use android_system_virtualizationservice::binder::{ParcelFileDescriptor, Strong};
use anyhow::{Context, Result};
@@ -26,7 +26,8 @@
use vmclient::VmInstance;
#[derive(Parser)]
-struct Args {
+/// Collection of CLI for trusty_security_vm_launcher
+pub struct Args {
/// Path to the trusty kernel image.
#[arg(long)]
kernel: PathBuf,
@@ -42,6 +43,10 @@
/// Memory size of the VM in MiB
#[arg(long, default_value_t = 128)]
memory_size_mib: i32,
+
+ /// CPU Topology exposed to the VM <one-cpu|match-host>
+ #[arg(long, default_value = "one-cpu", value_parser = parse_cpu_topology)]
+ cpu_topology: CpuTopology,
}
fn get_service() -> Result<Strong<dyn IVirtualizationService>> {
@@ -50,6 +55,14 @@
virtmgr.connect().context("Failed to connect to VirtualizationService")
}
+fn parse_cpu_topology(s: &str) -> Result<CpuTopology, String> {
+ match s {
+ "one-cpu" => Ok(CpuTopology::ONE_CPU),
+ "match-host" => Ok(CpuTopology::MATCH_HOST),
+ _ => Err(format!("Invalid cpu topology {}", s)),
+ }
+}
+
fn main() -> Result<()> {
let args = Args::parse();
@@ -63,6 +76,7 @@
kernel: Some(ParcelFileDescriptor::new(kernel)),
protectedVm: args.protected,
memoryMib: args.memory_size_mib,
+ cpuTopology: args.cpu_topology,
platformVersion: "~1.0".to_owned(),
// TODO: add instanceId
..Default::default()
diff --git a/libs/cborutil/src/lib.rs b/libs/cborutil/src/lib.rs
index b218c82..4d308c1 100644
--- a/libs/cborutil/src/lib.rs
+++ b/libs/cborutil/src/lib.rs
@@ -21,10 +21,7 @@
use alloc::string::String;
use alloc::vec::Vec;
use ciborium::value::{Integer, Value};
-use coset::{
- iana::{self, EnumI64},
- CborSerializable, CoseError, CoseKey, Label, Result,
-};
+use coset::{CborSerializable, CoseError, CoseKey, Label, Result};
use log::error;
use serde::{de::DeserializeOwned, Serialize};
@@ -135,19 +132,3 @@
.ok_or(CoseError::UnexpectedItem("", "Label not found in CoseKey"))?
.1)
}
-
-/// Converts the provided COSE key algorithm integer to an `iana::Algorithm` used
-/// by DICE chains.
-pub fn dice_cose_key_alg(cose_key_alg: i32) -> Result<iana::Algorithm> {
- let key_alg = iana::Algorithm::from_i64(cose_key_alg as i64).ok_or_else(|| {
- error!("Unsupported COSE key algorithm for DICE: {cose_key_alg}");
- CoseError::UnexpectedItem("COSE key algorithm", "")
- })?;
- match key_alg {
- iana::Algorithm::EdDSA | iana::Algorithm::ES256 | iana::Algorithm::ES384 => Ok(key_alg),
- _ => {
- error!("Unsupported COSE key algorithm for DICE: {key_alg:?}");
- Err(CoseError::UnexpectedItem("-8, -7 or -35", ""))
- }
- }
-}
diff --git a/libs/dice/open_dice/Android.bp b/libs/dice/open_dice/Android.bp
index d1129fb..c60260e 100644
--- a/libs/dice/open_dice/Android.bp
+++ b/libs/dice/open_dice/Android.bp
@@ -14,13 +14,11 @@
name: "libdiced_open_dice_nostd",
defaults: ["libdiced_open_dice_defaults"],
rustlibs: [
+ "libcoset_nostd",
"libopen_dice_android_bindgen_nostd",
"libopen_dice_cbor_bindgen_nostd",
"libzeroize_nostd",
],
- features: [
- "alloc",
- ],
whole_static_libs: [
"libcrypto_baremetal",
],
@@ -35,13 +33,13 @@
host_supported: true,
vendor_available: true,
rustlibs: [
+ "libcoset",
"libopen_dice_android_bindgen",
"libopen_dice_cbor_bindgen",
"libserde",
"libzeroize",
],
features: [
- "alloc",
"serde_derive",
"std",
],
@@ -154,10 +152,7 @@
"--allowlist-var=DICE_INLINE_CONFIG_SIZE",
"--allowlist-var=DICE_PRIVATE_KEY_SEED_SIZE",
"--allowlist-var=DICE_ID_SIZE",
- "--allowlist-var=DICE_PUBLIC_KEY_SIZE",
"--allowlist-var=DICE_PRIVATE_KEY_SIZE",
- "--allowlist-var=DICE_SIGNATURE_SIZE",
- "--allowlist-var=DICE_COSE_KEY_ALG_VALUE",
],
}
diff --git a/libs/dice/open_dice/src/dice.rs b/libs/dice/open_dice/src/dice.rs
index e330e00..6404508 100644
--- a/libs/dice/open_dice/src/dice.rs
+++ b/libs/dice/open_dice/src/dice.rs
@@ -16,12 +16,12 @@
//! This module mirrors the content in open-dice/include/dice/dice.h
use crate::error::{check_result, Result};
+use coset::iana;
pub use open_dice_cbor_bindgen::DiceMode;
use open_dice_cbor_bindgen::{
DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed, DiceInputValues,
DiceMainFlow, DICE_CDI_SIZE, DICE_HASH_SIZE, DICE_HIDDEN_SIZE, DICE_ID_SIZE,
DICE_INLINE_CONFIG_SIZE, DICE_PRIVATE_KEY_SEED_SIZE, DICE_PRIVATE_KEY_SIZE,
- DICE_PUBLIC_KEY_SIZE, DICE_SIGNATURE_SIZE,
};
#[cfg(feature = "serde_derive")]
use serde_derive::{Deserialize, Serialize};
@@ -40,10 +40,6 @@
pub const PRIVATE_KEY_SEED_SIZE: usize = DICE_PRIVATE_KEY_SEED_SIZE as usize;
/// The size of a private key.
pub const PRIVATE_KEY_SIZE: usize = DICE_PRIVATE_KEY_SIZE as usize;
-/// The size of a public key.
-pub const PUBLIC_KEY_SIZE: usize = DICE_PUBLIC_KEY_SIZE as usize;
-/// The size of a signature.
-pub const SIGNATURE_SIZE: usize = DICE_SIGNATURE_SIZE as usize;
/// The size of an ID.
pub const ID_SIZE: usize = DICE_ID_SIZE as usize;
@@ -55,13 +51,69 @@
pub type InlineConfig = [u8; INLINE_CONFIG_SIZE];
/// Array type of CDIs.
pub type Cdi = [u8; CDI_SIZE];
-/// Array type of the public key.
-pub type PublicKey = [u8; PUBLIC_KEY_SIZE];
-/// Array type of the signature.
-pub type Signature = [u8; SIGNATURE_SIZE];
/// Array type of DICE ID.
pub type DiceId = [u8; ID_SIZE];
+/// Key algorithm used for DICE.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum KeyAlgorithm {
+ /// Ed25519.
+ Ed25519,
+ /// ECDSA using P-256 curve.
+ EcdsaP256,
+ /// ECDSA using P-384 curve.
+ EcdsaP384,
+}
+
+impl From<KeyAlgorithm> for iana::Algorithm {
+ fn from(alg: KeyAlgorithm) -> Self {
+ match alg {
+ KeyAlgorithm::Ed25519 => iana::Algorithm::EdDSA,
+ KeyAlgorithm::EcdsaP256 => iana::Algorithm::ES256,
+ KeyAlgorithm::EcdsaP384 => iana::Algorithm::ES384,
+ }
+ }
+}
+
+/// Key algorithm used within different components in VMs.
+///
+/// This algorithm serves two primary purposes:
+///
+/// * **pvmfw Handover:** In pvmfw, a vendor DICE chain, potentially using various algorithms, is
+/// transitioned to this specific algorithm.
+/// * **Post-Handover Consistency:** In components following pvmfw (e.g., the Microdroid OS), this
+/// algorithm is used consistently for both the authority and subject keys in DICE derivation.
+pub const VM_KEY_ALGORITHM: KeyAlgorithm = KeyAlgorithm::Ed25519;
+
+impl KeyAlgorithm {
+ /// Returns the size of the public key.
+ pub fn public_key_size(&self) -> usize {
+ match self {
+ KeyAlgorithm::Ed25519 => 32,
+ KeyAlgorithm::EcdsaP256 => 64,
+ KeyAlgorithm::EcdsaP384 => 96,
+ }
+ }
+
+ /// Returns the size of the signature.
+ pub fn signature_size(&self) -> usize {
+ match self {
+ KeyAlgorithm::Ed25519 => 64,
+ KeyAlgorithm::EcdsaP256 => 64,
+ KeyAlgorithm::EcdsaP384 => 96,
+ }
+ }
+
+ /// Returns the size of the private key.
+ pub fn private_key_size(&self) -> usize {
+ match self {
+ KeyAlgorithm::Ed25519 => 64,
+ KeyAlgorithm::EcdsaP256 => 32,
+ KeyAlgorithm::EcdsaP384 => 48,
+ }
+ }
+}
+
/// A trait for types that represent Dice artifacts, which include:
///
/// - Attestation CDI
diff --git a/libs/dice/open_dice/src/lib.rs b/libs/dice/open_dice/src/lib.rs
index 085a2cd..a347d46 100644
--- a/libs/dice/open_dice/src/lib.rs
+++ b/libs/dice/open_dice/src/lib.rs
@@ -17,7 +17,6 @@
#![cfg_attr(not(feature = "std"), no_std)]
-#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(not(feature = "std"))]
@@ -27,7 +26,6 @@
mod dice;
mod error;
mod ops;
-#[cfg(feature = "alloc")]
mod retry;
pub use bcc::{
@@ -36,18 +34,14 @@
};
pub use dice::{
derive_cdi_certificate_id, derive_cdi_private_key_seed, dice_main_flow, Cdi, CdiValues, Config,
- DiceArtifacts, DiceMode, Hash, Hidden, InlineConfig, InputValues, PrivateKey, PrivateKeySeed,
- PublicKey, Signature, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
+ DiceArtifacts, DiceMode, Hash, Hidden, InlineConfig, InputValues, KeyAlgorithm, PrivateKey,
+ PrivateKeySeed, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
+ VM_KEY_ALGORITHM,
};
pub use error::{DiceError, Result};
-// Currently, open-dice library only supports a single signing and verification algorithm.
-// The value of DICE_COSE_KEY_ALG_VALUE depends on the algorithm chosen by the underlying C
-// library at build time. Refer to b/342333212 for more information.
-pub use open_dice_cbor_bindgen::DICE_COSE_KEY_ALG_VALUE;
pub use ops::{
derive_cdi_leaf_priv, generate_certificate, hash, kdf, keypair_from_seed, sign, verify,
};
-#[cfg(feature = "alloc")]
pub use retry::{
retry_bcc_format_config_descriptor, retry_bcc_main_flow, retry_dice_main_flow,
retry_generate_certificate, OwnedDiceArtifacts,
diff --git a/libs/dice/open_dice/src/ops.rs b/libs/dice/open_dice/src/ops.rs
index 47b5244..137736f 100644
--- a/libs/dice/open_dice/src/ops.rs
+++ b/libs/dice/open_dice/src/ops.rs
@@ -17,10 +17,11 @@
//! main DICE functions depend on.
use crate::dice::{
- derive_cdi_private_key_seed, DiceArtifacts, Hash, InputValues, PrivateKey, PublicKey,
- Signature, HASH_SIZE, PRIVATE_KEY_SEED_SIZE, PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE, SIGNATURE_SIZE,
+ derive_cdi_private_key_seed, DiceArtifacts, Hash, InputValues, PrivateKey, HASH_SIZE,
+ PRIVATE_KEY_SEED_SIZE, PRIVATE_KEY_SIZE, VM_KEY_ALGORITHM,
};
-use crate::error::{check_result, Result};
+use crate::error::{check_result, DiceError, Result};
+use alloc::{vec, vec::Vec};
use open_dice_cbor_bindgen::{
DiceGenerateCertificate, DiceHash, DiceKdf, DiceKeypairFromSeed, DiceSign, DiceVerify,
};
@@ -71,8 +72,8 @@
/// Deterministically generates a public and private key pair from `seed`.
/// Since this is deterministic, `seed` is as sensitive as a private key and can
/// be used directly as the private key.
-pub fn keypair_from_seed(seed: &[u8; PRIVATE_KEY_SEED_SIZE]) -> Result<(PublicKey, PrivateKey)> {
- let mut public_key = [0u8; PUBLIC_KEY_SIZE];
+pub fn keypair_from_seed(seed: &[u8; PRIVATE_KEY_SEED_SIZE]) -> Result<(Vec<u8>, PrivateKey)> {
+ let mut public_key = vec![0u8; VM_KEY_ALGORITHM.public_key_size()];
let mut private_key = PrivateKey::default();
check_result(
// SAFETY: The function writes to the `public_key` and `private_key` within the given
@@ -106,8 +107,8 @@
}
/// Signs the `message` with the give `private_key` using `DiceSign`.
-pub fn sign(message: &[u8], private_key: &[u8; PRIVATE_KEY_SIZE]) -> Result<Signature> {
- let mut signature = [0u8; SIGNATURE_SIZE];
+pub fn sign(message: &[u8], private_key: &[u8; PRIVATE_KEY_SIZE]) -> Result<Vec<u8>> {
+ let mut signature = vec![0u8; VM_KEY_ALGORITHM.signature_size()];
check_result(
// SAFETY: The function writes to the `signature` within the given bounds, and only reads
// the message and the private key. The first argument context is not used in this
@@ -127,7 +128,12 @@
}
/// Verifies the `signature` of the `message` with the given `public_key` using `DiceVerify`.
-pub fn verify(message: &[u8], signature: &Signature, public_key: &PublicKey) -> Result<()> {
+pub fn verify(message: &[u8], signature: &[u8], public_key: &[u8]) -> Result<()> {
+ if signature.len() != VM_KEY_ALGORITHM.signature_size()
+ || public_key.len() != VM_KEY_ALGORITHM.public_key_size()
+ {
+ return Err(DiceError::InvalidInput);
+ }
check_result(
// SAFETY: only reads the messages, signature and public key as constant values.
// The first argument context is not used in this function.
diff --git a/libs/dice/open_dice/src/retry.rs b/libs/dice/open_dice/src/retry.rs
index d9551f3..6e75e91 100644
--- a/libs/dice/open_dice/src/retry.rs
+++ b/libs/dice/open_dice/src/retry.rs
@@ -23,7 +23,6 @@
};
use crate::error::{DiceError, Result};
use crate::ops::generate_certificate;
-#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "serde_derive")]
use serde_derive::{Deserialize, Serialize};
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
index 3d1964d..8230166 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -744,6 +744,7 @@
return usbConfig;
})
.orElse(null);
+ config.teeServices = EMPTY_STRING_ARRAY;
return config;
}
@@ -798,6 +799,7 @@
new VirtualMachineAppConfig.CustomConfig();
customConfig.devices = EMPTY_STRING_ARRAY;
customConfig.extraKernelCmdlineParams = EMPTY_STRING_ARRAY;
+ customConfig.teeServices = EMPTY_STRING_ARRAY;
try {
customConfig.vendorImage =
ParcelFileDescriptor.open(mVendorDiskImage, MODE_READ_ONLY);
diff --git a/libs/libclient_vm_csr/src/lib.rs b/libs/libclient_vm_csr/src/lib.rs
index 77cfea9..e5a6036 100644
--- a/libs/libclient_vm_csr/src/lib.rs
+++ b/libs/libclient_vm_csr/src/lib.rs
@@ -20,9 +20,7 @@
iana, CborSerializable, CoseKey, CoseKeyBuilder, CoseSign, CoseSignBuilder, CoseSignature,
CoseSignatureBuilder, HeaderBuilder,
};
-use diced_open_dice::{
- derive_cdi_leaf_priv, sign, DiceArtifacts, PrivateKey, DICE_COSE_KEY_ALG_VALUE,
-};
+use diced_open_dice::{derive_cdi_leaf_priv, sign, DiceArtifacts, PrivateKey, VM_KEY_ALGORITHM};
use openssl::{
bn::{BigNum, BigNumContext},
ec::{EcGroup, EcKey, EcKeyRef},
@@ -93,8 +91,7 @@
cdi_leaf_priv: &PrivateKey,
attestation_key: &EcKeyRef<Private>,
) -> Result<CoseSign> {
- let dice_key_alg = cbor_util::dice_cose_key_alg(DICE_COSE_KEY_ALG_VALUE)?;
- let cdi_leaf_sig_headers = build_signature_headers(dice_key_alg);
+ let cdi_leaf_sig_headers = build_signature_headers(VM_KEY_ALGORITHM.into());
let attestation_key_sig_headers = build_signature_headers(ATTESTATION_KEY_ALGO);
let aad = &[];
let signed_data = CoseSignBuilder::new()
diff --git a/libs/libservice_vm_requests/src/rkp.rs b/libs/libservice_vm_requests/src/rkp.rs
index 7de7cd5..2c26b16 100644
--- a/libs/libservice_vm_requests/src/rkp.rs
+++ b/libs/libservice_vm_requests/src/rkp.rs
@@ -28,7 +28,7 @@
use core::result;
use coset::{AsCborValue, CoseSign1, CoseSign1Builder, HeaderBuilder};
use diced_open_dice::{
- derive_cdi_leaf_priv, kdf, sign, DiceArtifacts, PrivateKey, DICE_COSE_KEY_ALG_VALUE,
+ derive_cdi_leaf_priv, kdf, sign, DiceArtifacts, PrivateKey, VM_KEY_ALGORITHM,
};
use log::{debug, error};
use service_vm_comm::{EcdsaP256KeyPair, GenerateCertificateRequestParams, RequestProcessingError};
@@ -152,8 +152,7 @@
error!("Failed to derive the CDI_Leaf_Priv: {e}");
RequestProcessingError::InternalError
})?;
- let dice_key_alg = cbor_util::dice_cose_key_alg(DICE_COSE_KEY_ALG_VALUE)?;
- let protected = HeaderBuilder::new().algorithm(dice_key_alg).build();
+ let protected = HeaderBuilder::new().algorithm(VM_KEY_ALGORITHM.into()).build();
let signed_data = CoseSign1Builder::new()
.protected(protected)
.payload(cbor_util::serialize(payload)?)
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 1f65fcb..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
@@ -59,20 +59,6 @@
mCallback = callback;
mContext = context;
mSharedPref = mContext.getSharedPreferences(PREFERENCE_FILE_KEY, Context.MODE_PRIVATE);
- // TODO(b/340126051): Instead of putting fixed value, receive active port list info from the
- // guest.
- if (!mSharedPref.contains(PREFERENCE_FORWARDING_PORTS)) {
- SharedPreferences.Editor editor = mSharedPref.edit();
- Set<String> ports = new HashSet<>();
- for (int port = 8080; port < 8090; port++) {
- ports.add(Integer.toString(port));
- editor.putBoolean(
- PREFERENCE_FORWARDING_PORT_IS_ENABLED_PREFIX + Integer.toString(port),
- false);
- }
- editor.putStringSet(PREFERENCE_FORWARDING_PORTS, ports);
- editor.apply();
- }
}
@Override
@@ -80,7 +66,21 @@
ReportVmActivePortsRequest request,
StreamObserver<ReportVmActivePortsResponse> responseObserver) {
Log.d(DebianServiceImpl.TAG, "reportVmActivePorts: " + request.toString());
- // TODO(b/340126051): Modify shared preference based on information in the request.
+
+ 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);
diff --git a/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java b/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java
index 6400438..fd07973 100644
--- a/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java
+++ b/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java
@@ -56,7 +56,6 @@
Intent intent = new Intent(mTargetContext, InstallerActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(InstallerActivity.EXTRA_AUTO_DOWNLOAD, true);
if (mInstr.startActivitySync(intent) instanceof InstallerActivity activity) {
assertTrue(
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index adab521..03d7fef 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -1345,9 +1345,8 @@
}
@Test
- @Parameters(method = "gkiVersions")
+ @Parameters(method = "osVersions")
@TestCaseName("{method}_os_{0}")
- @Ignore("b/360388014") // TODO(b/360388014): fix & re-enable
public void microdroidDeviceTreeCompat(String os) throws Exception {
assumeArm64Supported();
final String configPath = "assets/vm_config.json";
@@ -1374,9 +1373,8 @@
}
@Test
- @Parameters(method = "gkiVersions")
+ @Parameters(method = "osVersions")
@TestCaseName("{method}_os_{0}")
- @Ignore("b/360388014") // TODO(b/360388014): fix & re-enable
public void microdroidProtectedDeviceTreeCompat(String os) throws Exception {
assumeArm64Supported();
final String configPath = "assets/vm_config.json";
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..b1485e3 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -83,6 +83,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
@@ -221,6 +222,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 +251,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 +270,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();
@@ -1935,6 +1939,7 @@
}
@Test
+ @Ignore("b/372874464")
public void outputIsNotRedirectedToLogcatIfNotDebuggable() throws Exception {
assumeSupportedDevice();