Merge "[LSC] Add LOCAL_LICENSE_KINDS to packages/modules/Virtualization"
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index 32ffc0a..395e2e9 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -25,14 +25,13 @@
mod fsverity;
use anyhow::{bail, Result};
-use binder::unstable_api::AsNative;
+use binder_common::rpc_server::run_rpc_server;
use log::{debug, error};
use std::cmp::min;
use std::collections::BTreeMap;
use std::convert::TryInto;
use std::fs::File;
use std::io;
-use std::os::raw;
use std::os::unix::fs::FileExt;
use std::os::unix::io::{AsRawFd, FromRawFd};
@@ -342,23 +341,17 @@
);
let args = parse_args()?;
-
- let mut service = FdService::new_binder(args.fd_pool).as_binder();
- let mut ready_notifier = ReadyNotifier(args.ready_fd);
+ let service = FdService::new_binder(args.fd_pool).as_binder();
debug!("fd_server is starting as a rpc service.");
- // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
- // Plus the binder objects are threadsafe.
- // RunRpcServerCallback does not retain a reference to ready_callback, and only ever
- // calls it with the param we provide during the lifetime of ready_notifier.
- let retval = unsafe {
- binder_rpc_unstable_bindgen::RunRpcServerCallback(
- service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
- RPC_SERVICE_PORT,
- Some(ReadyNotifier::ready_callback),
- ready_notifier.as_void_ptr(),
- )
- };
+
+ let mut ready_fd = args.ready_fd;
+ let retval = run_rpc_server(service, RPC_SERVICE_PORT, || {
+ debug!("fd_server is ready");
+ // Close the ready-fd if we were given one to signal our readiness.
+ drop(ready_fd.take());
+ });
+
if retval {
debug!("RPC server has shut down gracefully");
Ok(())
@@ -366,25 +359,3 @@
bail!("Premature termination of RPC server");
}
}
-
-struct ReadyNotifier(Option<File>);
-
-impl ReadyNotifier {
- fn notify(&mut self) {
- debug!("fd_server is ready");
- // Close the ready-fd if we were given one to signal our readiness.
- drop(self.0.take());
- }
-
- fn as_void_ptr(&mut self) -> *mut raw::c_void {
- self as *mut _ as *mut raw::c_void
- }
-
- unsafe extern "C" fn ready_callback(param: *mut raw::c_void) {
- // SAFETY: This is only ever called by RunRpcServerCallback, within the lifetime of the
- // ReadyNotifier, with param taking the value returned by as_void_ptr (so a properly aligned
- // non-null pointer to an initialized instance).
- let ready_notifier = param as *mut Self;
- ready_notifier.as_mut().unwrap().notify()
- }
-}
diff --git a/binder_common/Android.bp b/binder_common/Android.bp
index 9e6d590..209955d 100644
--- a/binder_common/Android.bp
+++ b/binder_common/Android.bp
@@ -9,6 +9,7 @@
edition: "2018",
rustlibs: [
"libbinder_rs",
+ "libbinder_rpc_unstable_bindgen",
"liblazy_static",
],
apex_available: [
diff --git a/binder_common/lib.rs b/binder_common/lib.rs
index 055688a..f2391e3 100644
--- a/binder_common/lib.rs
+++ b/binder_common/lib.rs
@@ -17,6 +17,7 @@
//! Common items useful for binder clients and/or servers.
pub mod lazy_service;
+pub mod rpc_server;
use binder::public_api::{ExceptionCode, Status};
use std::ffi::CString;
diff --git a/binder_common/rpc_server.rs b/binder_common/rpc_server.rs
new file mode 100644
index 0000000..36075cf
--- /dev/null
+++ b/binder_common/rpc_server.rs
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+//! Helpers for implementing an RPC Binder server.
+
+use binder::public_api::SpIBinder;
+use binder::unstable_api::AsNative;
+use std::os::raw;
+
+/// Run a binder RPC server, serving the supplied binder service implementation on the given vsock
+/// port.
+/// If and when the server is ready for connections (it is listening on the port) on_ready
+/// is called to allow appropriate action to be taken - e.g. to notify clients they
+/// may now attempt to connect.
+/// The current thread is joined to the binder thread pool to handle incoming messages.
+/// Returns true if the server has shutdown normally, false if it failed in some way.
+pub fn run_rpc_server<F>(service: SpIBinder, port: u32, on_ready: F) -> bool
+where
+ F: FnOnce(),
+{
+ let mut ready_notifier = ReadyNotifier(Some(on_ready));
+ ready_notifier.run_server(service, port)
+}
+
+struct ReadyNotifier<F>(Option<F>)
+where
+ F: FnOnce();
+
+impl<F> ReadyNotifier<F>
+where
+ F: FnOnce(),
+{
+ fn run_server(&mut self, mut service: SpIBinder, port: u32) -> bool {
+ let service = service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder;
+ let param = self.as_void_ptr();
+
+ // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+ // Plus the binder objects are threadsafe.
+ // RunRpcServerCallback does not retain a reference to ready_callback, and only ever
+ // calls it with the param we provide during the lifetime of self.
+ unsafe {
+ binder_rpc_unstable_bindgen::RunRpcServerCallback(
+ service,
+ port,
+ Some(Self::ready_callback),
+ param,
+ )
+ }
+ }
+
+ fn as_void_ptr(&mut self) -> *mut raw::c_void {
+ self as *mut _ as *mut raw::c_void
+ }
+
+ unsafe extern "C" fn ready_callback(param: *mut raw::c_void) {
+ // SAFETY: This is only ever called by RunRpcServerCallback, within the lifetime of the
+ // ReadyNotifier, with param taking the value returned by as_void_ptr (so a properly aligned
+ // non-null pointer to an initialized instance).
+ let ready_notifier = param as *mut Self;
+ ready_notifier.as_mut().unwrap().notify()
+ }
+
+ fn notify(&mut self) {
+ if let Some(on_ready) = self.0.take() {
+ on_ready();
+ }
+ }
+}
diff --git a/compos/src/compsvc_main.rs b/compos/src/compsvc_main.rs
index d0c920a..6887947 100644
--- a/compos/src/compsvc_main.rs
+++ b/compos/src/compsvc_main.rs
@@ -30,14 +30,14 @@
};
use anyhow::{anyhow, bail, Context, Result};
use binder::{
- unstable_api::{new_spibinder, AIBinder, AsNative},
+ unstable_api::{new_spibinder, AIBinder},
FromIBinder,
};
+use binder_common::rpc_server::run_rpc_server;
use compos_common::COMPOS_VSOCK_PORT;
use log::{debug, error};
use nix::ioctl_read_bad;
use std::fs::OpenOptions;
-use std::os::raw;
use std::os::unix::io::AsRawFd;
/// The CID representing the host VM
@@ -62,23 +62,17 @@
);
}
- let mut service = compsvc::new_binder()?.as_binder();
+ let service = compsvc::new_binder()?.as_binder();
+ let vm_service = get_vm_service()?;
+ let local_cid = get_local_cid()?;
+
debug!("compsvc is starting as a rpc service.");
- let mut ready_notifier = ReadyNotifier::new()?;
-
- // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
- // Plus the binder objects are threadsafe.
- // RunRpcServerCallback does not retain a reference to ready_callback, and only ever
- // calls it with the param we provide during the lifetime of ready_notifier.
- let retval = unsafe {
- binder_rpc_unstable_bindgen::RunRpcServerCallback(
- service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
- COMPOS_VSOCK_PORT,
- Some(ReadyNotifier::ready_callback),
- ready_notifier.as_void_ptr(),
- )
- };
+ let retval = run_rpc_server(service, COMPOS_VSOCK_PORT, || {
+ if let Err(e) = vm_service.notifyPayloadReady(local_cid as i32) {
+ error!("Unable to notify ready: {}", e);
+ }
+ });
if retval {
debug!("RPC server has shut down gracefully");
Ok(())
@@ -87,61 +81,32 @@
}
}
-struct ReadyNotifier {
- vm_service: Strong<dyn IVirtualMachineService>,
- local_cid: u32,
+fn get_vm_service() -> Result<Strong<dyn IVirtualMachineService>> {
+ // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership
+ // can be safely taken by new_spibinder.
+ let ibinder = unsafe {
+ new_spibinder(binder_rpc_unstable_bindgen::RpcClient(
+ VMADDR_CID_HOST,
+ VM_BINDER_SERVICE_PORT as u32,
+ ) as *mut AIBinder)
+ }
+ .ok_or_else(|| anyhow!("Failed to connect to IVirtualMachineService"))?;
+
+ FromIBinder::try_from(ibinder).context("Connecting to IVirtualMachineService")
}
-impl ReadyNotifier {
- fn new() -> Result<Self> {
- Ok(Self { vm_service: Self::get_vm_service()?, local_cid: Self::get_local_cid()? })
- }
-
- fn notify(&self) {
- if let Err(e) = self.vm_service.notifyPayloadReady(self.local_cid as i32) {
- error!("Unable to notify ready: {}", e);
- }
- }
-
- fn as_void_ptr(&mut self) -> *mut raw::c_void {
- self as *mut _ as *mut raw::c_void
- }
-
- unsafe extern "C" fn ready_callback(param: *mut raw::c_void) {
- // SAFETY: This is only ever called by RunRpcServerCallback, within the lifetime of the
- // ReadyNotifier, with param taking the value returned by as_void_ptr (so a properly aligned
- // non-null pointer to an initialized instance).
- let ready_notifier = param as *mut Self;
- ready_notifier.as_ref().unwrap().notify()
- }
-
- fn get_vm_service() -> Result<Strong<dyn IVirtualMachineService>> {
- // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership
- // can be safely taken by new_spibinder.
- let ibinder = unsafe {
- new_spibinder(binder_rpc_unstable_bindgen::RpcClient(
- VMADDR_CID_HOST,
- VM_BINDER_SERVICE_PORT as u32,
- ) as *mut AIBinder)
- }
- .ok_or_else(|| anyhow!("Failed to connect to IVirtualMachineService"))?;
-
- FromIBinder::try_from(ibinder).context("Connecting to IVirtualMachineService")
- }
-
- // TODO(b/199259751): remove this after VS can check the peer addresses of binder clients
- fn get_local_cid() -> Result<u32> {
- let f = OpenOptions::new()
- .read(true)
- .write(false)
- .open("/dev/vsock")
- .context("Failed to open /dev/vsock")?;
- let mut cid = 0;
- // SAFETY: the kernel only modifies the given u32 integer.
- unsafe { vm_sockets_get_local_cid(f.as_raw_fd(), &mut cid) }
- .context("Failed to get local CID")?;
- Ok(cid)
- }
+// TODO(b/199259751): remove this after VS can check the peer addresses of binder clients
+fn get_local_cid() -> Result<u32> {
+ let f = OpenOptions::new()
+ .read(true)
+ .write(false)
+ .open("/dev/vsock")
+ .context("Failed to open /dev/vsock")?;
+ let mut cid = 0;
+ // SAFETY: the kernel only modifies the given u32 integer.
+ unsafe { vm_sockets_get_local_cid(f.as_raw_fd(), &mut cid) }
+ .context("Failed to get local CID")?;
+ Ok(cid)
}
// TODO(b/199259751): remove this after VS can check the peer addresses of binder clients
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
index 59ad81c..3520d9f 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -21,13 +21,14 @@
use android_system_virtualizationservice::binder::ParcelFileDescriptor;
use anyhow::{anyhow, Context, Result};
use binder::{wait_for_interface, Strong};
-use log::info;
+use log::{error, info};
use microdroid_metadata::{ApexPayload, ApkPayload, Metadata};
use microdroid_payload_config::{ApexConfig, VmPayloadConfig};
use once_cell::sync::OnceCell;
use packagemanager_aidl::aidl::android::content::pm::IPackageManagerNative::IPackageManagerNative;
use serde::Deserialize;
use serde_xml_rs::from_reader;
+use std::env;
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use vmconfig::open_parcel_file;
@@ -107,17 +108,17 @@
fn make_metadata_file(
config_path: &str,
- apexes: &[ApexConfig],
+ apex_names: &[String],
temporary_directory: &Path,
) -> Result<ParcelFileDescriptor> {
let metadata_path = temporary_directory.join("metadata");
let metadata = Metadata {
version: 1,
- apexes: apexes
+ apexes: apex_names
.iter()
.enumerate()
- .map(|(i, apex)| ApexPayload {
- name: apex.name.clone(),
+ .map(|(i, apex_name)| ApexPayload {
+ name: apex_name.clone(),
partition_name: format!("microdroid-apex-{}", i),
..Default::default()
})
@@ -157,7 +158,7 @@
apk_file: File,
idsig_file: File,
config_path: &str,
- apexes: &[ApexConfig],
+ apexes: &[String],
prefer_staged: bool,
temporary_directory: &Path,
) -> Result<DiskImage> {
@@ -171,7 +172,7 @@
let pm = PackageManager::new()?;
for (i, apex) in apexes.iter().enumerate() {
- let apex_path = pm.get_apex_path(&apex.name, prefer_staged)?;
+ let apex_path = pm.get_apex_path(apex, prefer_staged)?;
let apex_file = open_parcel_file(&apex_path, false)?;
partitions.push(Partition {
label: format!("microdroid-apex-{}", i),
@@ -193,6 +194,47 @@
Ok(DiskImage { image: None, partitions, writable: false })
}
+fn find_apex_names_in_classpath_env(classpath_env_var: &str) -> Vec<String> {
+ let val = env::var(classpath_env_var).unwrap_or_else(|e| {
+ error!("Reading {} failed: {}", classpath_env_var, e);
+ String::from("")
+ });
+ val.split(':')
+ .filter_map(|path| {
+ Path::new(path)
+ .strip_prefix("/apex/")
+ .map(|stripped| {
+ let first = stripped.iter().next().unwrap();
+ first.to_str().unwrap().to_string()
+ })
+ .ok()
+ })
+ .collect()
+}
+
+// Collect APEX names from config
+fn collect_apex_names(apexes: &[ApexConfig]) -> Vec<String> {
+ // Process pseudo names like "{BOOTCLASSPATH}".
+ // For now we have following pseudo APEX names:
+ // - {BOOTCLASSPATH}: represents APEXes contributing "BOOTCLASSPATH" environment variable
+ // - {DEX2OATBOOTCLASSPATH}: represents APEXes contributing "DEX2OATBOOTCLASSPATH" environment variable
+ // - {SYSTEMSERVERCLASSPATH}: represents APEXes contributing "SYSTEMSERVERCLASSPATH" environment variable
+ let mut apex_names: Vec<String> = apexes
+ .iter()
+ .flat_map(|apex| match apex.name.as_str() {
+ "{BOOTCLASSPATH}" => find_apex_names_in_classpath_env("BOOTCLASSPATH"),
+ "{DEX2OATBOOTCLASSPATH}" => find_apex_names_in_classpath_env("DEX2OATBOOTCLASSPATH"),
+ "{SYSTEMSERVERCLASSPATH}" => find_apex_names_in_classpath_env("SYSTEMSERVERCLASSPATH"),
+ _ => vec![apex.name.clone()],
+ })
+ .collect();
+ // Add required APEXes
+ apex_names.extend(MICRODROID_REQUIRED_APEXES.iter().map(|name| name.to_string()));
+ apex_names.sort();
+ apex_names.dedup();
+ apex_names
+}
+
pub fn add_microdroid_images(
config: &VirtualMachineAppConfig,
temporary_directory: &Path,
@@ -202,12 +244,9 @@
vm_payload_config: &VmPayloadConfig,
vm_config: &mut VirtualMachineRawConfig,
) -> Result<()> {
- let mut apexes = vm_payload_config.apexes.clone();
- apexes.extend(
- MICRODROID_REQUIRED_APEXES.iter().map(|name| ApexConfig { name: name.to_string() }),
- );
- apexes.dedup_by(|a, b| a.name == b.name);
-
+ // collect APEX names from config
+ let apexes = collect_apex_names(&vm_payload_config.apexes);
+ info!("Microdroid payload APEXes: {:?}", apexes);
vm_config.disks.push(make_payload_disk(
apk_file,
idsig_file,
@@ -237,3 +276,18 @@
Ok(())
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[test]
+ fn test_find_apex_names_in_classpath_env() {
+ let key = "TEST_BOOTCLASSPATH";
+ let classpath = "/apex/com.android.foo/javalib/foo.jar:/system/framework/framework.jar:/apex/com.android.bar/javalib/bar.jar";
+ env::set_var(key, classpath);
+ assert_eq!(
+ find_apex_names_in_classpath_env(key),
+ vec!["com.android.foo".to_owned(), "com.android.bar".to_owned()]
+ );
+ }
+}