Update PropertyWatcher to wait for property create
Previously, PropertyWatcher would only wait for changes to a property,
however, if a property had not yet been created then PropertyWatcher
would fail. With this change, PropertyWatcher::wait will wait for
properties to be created as well as changed.
Bug: 172013262
Test: atest system/keystore/keystore2
Test: statsd_testdrive 10103
Change-Id: Ic2759581459759738c11e0c452c1457a4a95feea
diff --git a/keystore2/system_property/lib.rs b/keystore2/system_property/lib.rs
index f14cf0e..be13c88 100644
--- a/keystore2/system_property/lib.rs
+++ b/keystore2/system_property/lib.rs
@@ -15,8 +15,9 @@
//! This crate provides the PropertyWatcher type, which watches for changes
//! in Android system properties.
+use keystore2_system_property_bindgen::prop_info as PropInfo;
use std::os::raw::c_char;
-use std::ptr::null_mut;
+use std::ptr::null;
use std::{
ffi::{c_void, CStr, CString},
str::Utf8Error,
@@ -56,27 +57,34 @@
/// as `keystore.boot_level`; it can report the current value of this
/// property, or wait for it to change.
pub struct PropertyWatcher {
- prop_info: *const keystore2_system_property_bindgen::prop_info,
+ prop_name: CString,
+ prop_info: *const PropInfo,
serial: keystore2_system_property_bindgen::__uint32_t,
}
impl PropertyWatcher {
/// Create a PropertyWatcher for the named system property.
pub fn new(name: &str) -> Result<Self> {
- let cstr = CString::new(name)?;
- // Unsafe FFI call. We generate the CStr in this function
- // and so ensure it is valid during call.
- // Returned pointer is valid for the lifetime of the program.
- let prop_info =
- unsafe { keystore2_system_property_bindgen::__system_property_find(cstr.as_ptr()) };
- if prop_info.is_null() {
- Err(PropertyWatcherError::SystemPropertyAbsent)
+ Ok(Self { prop_name: CString::new(name)?, prop_info: null(), serial: 0 })
+ }
+
+ // Lazy-initializing accessor for self.prop_info.
+ fn get_prop_info(&mut self) -> Option<*const PropInfo> {
+ if self.prop_info.is_null() {
+ // Unsafe required for FFI call. Input and output are both const.
+ // The returned pointer is valid for the lifetime of the program.
+ self.prop_info = unsafe {
+ keystore2_system_property_bindgen::__system_property_find(self.prop_name.as_ptr())
+ };
+ }
+ if self.prop_info.is_null() {
+ None
} else {
- Ok(Self { prop_info, serial: 0 })
+ Some(self.prop_info)
}
}
- fn read_raw(&self, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
+ fn read_raw(prop_info: *const PropInfo, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
// Unsafe function converts values passed to us by
// __system_property_read_callback to Rust form
// and pass them to inner callback.
@@ -98,7 +106,7 @@
// to a void pointer, and unwrap it in our callback.
unsafe {
keystore2_system_property_bindgen::__system_property_read_callback(
- self.prop_info,
+ prop_info,
Some(callback),
&mut f as *mut _ as *mut c_void,
)
@@ -108,12 +116,14 @@
/// Call the passed function, passing it the name and current value
/// of this system property. See documentation for
/// `__system_property_read_callback` for details.
- pub fn read<T, F>(&self, f: F) -> Result<T>
+ /// Returns an error if the property is empty or doesn't exist.
+ pub fn read<T, F>(&mut self, f: F) -> Result<T>
where
F: FnOnce(&str, &str) -> anyhow::Result<T>,
{
+ let prop_info = self.get_prop_info().ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
- self.read_raw(|name, value| {
+ Self::read_raw(prop_info, |name, value| {
// use a wrapping closure as an erzatz try block.
result = (|| {
let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
@@ -124,10 +134,43 @@
result
}
+ // Waits for the property that self is watching to be created. Returns immediately if the
+ // property already exists.
+ fn wait_for_property_creation(&mut self) -> Result<()> {
+ let mut global_serial = 0;
+ loop {
+ match self.get_prop_info() {
+ Some(_) => return Ok(()),
+ None => {
+ // Unsafe call for FFI. The function modifies only global_serial, and has
+ // no side-effects.
+ if !unsafe {
+ // Wait for a global serial number change, then try again. On success,
+ // the function will update global_serial with the last version seen.
+ keystore2_system_property_bindgen::__system_property_wait(
+ null(),
+ global_serial,
+ &mut global_serial,
+ null(),
+ )
+ } {
+ return Err(PropertyWatcherError::WaitFailed);
+ }
+ }
+ }
+ }
+ }
+
/// Wait for the system property to change. This
/// records the serial number of the last change, so
/// race conditions are avoided.
pub fn wait(&mut self) -> Result<()> {
+ // If the property is null, then wait for it to be created. Subsequent waits will
+ // skip this step and wait for our specific property to change.
+ if self.prop_info.is_null() {
+ return self.wait_for_property_creation();
+ }
+
let mut new_serial = self.serial;
// Unsafe block to call __system_property_wait.
// All arguments are private to PropertyWatcher so we
@@ -137,7 +180,7 @@
self.prop_info,
self.serial,
&mut new_serial,
- null_mut(),
+ null(),
)
} {
return Err(PropertyWatcherError::WaitFailed);