blob: f14cf0e3ea8ab9c42b276eb43141b983ace2bd70 [file] [log] [blame]
Paul Crowley7c57bf12021-02-02 16:26:57 -08001// Copyright 2021, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! This crate provides the PropertyWatcher type, which watches for changes
16//! in Android system properties.
17
18use std::os::raw::c_char;
19use std::ptr::null_mut;
20use std::{
21 ffi::{c_void, CStr, CString},
22 str::Utf8Error,
23};
24use thiserror::Error;
25
26/// Errors this crate can generate
27#[derive(Error, Debug)]
28pub enum PropertyWatcherError {
29 /// We can't watch for a property whose name contains a NUL character.
30 #[error("Cannot convert name to C string")]
31 BadNameError(#[from] std::ffi::NulError),
32 /// We can only watch for properties that exist when the watcher is created.
33 #[error("System property is absent")]
34 SystemPropertyAbsent,
35 /// __system_property_wait timed out despite being given no timeout.
36 #[error("Wait failed")]
37 WaitFailed,
38 /// read callback was not called
39 #[error("__system_property_read_callback did not call callback")]
40 ReadCallbackNotCalled,
41 /// read callback gave us a NULL pointer
42 #[error("__system_property_read_callback gave us a NULL pointer instead of a string")]
43 MissingCString,
44 /// read callback gave us a bad C string
45 #[error("__system_property_read_callback gave us a non-UTF8 C string")]
46 BadCString(#[from] Utf8Error),
47 /// read callback returned an error
48 #[error("Callback failed")]
49 CallbackError(#[from] anyhow::Error),
50}
51
52/// Result type specific for this crate.
53pub type Result<T> = std::result::Result<T, PropertyWatcherError>;
54
55/// PropertyWatcher takes the name of an Android system property such
56/// as `keystore.boot_level`; it can report the current value of this
57/// property, or wait for it to change.
58pub struct PropertyWatcher {
59 prop_info: *const keystore2_system_property_bindgen::prop_info,
60 serial: keystore2_system_property_bindgen::__uint32_t,
61}
62
63impl PropertyWatcher {
64 /// Create a PropertyWatcher for the named system property.
65 pub fn new(name: &str) -> Result<Self> {
66 let cstr = CString::new(name)?;
67 // Unsafe FFI call. We generate the CStr in this function
68 // and so ensure it is valid during call.
69 // Returned pointer is valid for the lifetime of the program.
70 let prop_info =
71 unsafe { keystore2_system_property_bindgen::__system_property_find(cstr.as_ptr()) };
72 if prop_info.is_null() {
73 Err(PropertyWatcherError::SystemPropertyAbsent)
74 } else {
75 Ok(Self { prop_info, serial: 0 })
76 }
77 }
78
79 fn read_raw(&self, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
80 // Unsafe function converts values passed to us by
81 // __system_property_read_callback to Rust form
82 // and pass them to inner callback.
83 unsafe extern "C" fn callback(
84 res_p: *mut c_void,
85 name: *const c_char,
86 value: *const c_char,
87 _: keystore2_system_property_bindgen::__uint32_t,
88 ) {
89 let name = if name.is_null() { None } else { Some(CStr::from_ptr(name)) };
90 let value = if value.is_null() { None } else { Some(CStr::from_ptr(value)) };
91 let f = &mut *res_p.cast::<&mut dyn FnMut(Option<&CStr>, Option<&CStr>)>();
92 f(name, value);
93 }
94
95 let mut f: &mut dyn FnOnce(Option<&CStr>, Option<&CStr>) = &mut f;
96
97 // Unsafe block for FFI call. We convert the FnOnce
98 // to a void pointer, and unwrap it in our callback.
99 unsafe {
100 keystore2_system_property_bindgen::__system_property_read_callback(
101 self.prop_info,
102 Some(callback),
103 &mut f as *mut _ as *mut c_void,
104 )
105 }
106 }
107
108 /// Call the passed function, passing it the name and current value
109 /// of this system property. See documentation for
110 /// `__system_property_read_callback` for details.
111 pub fn read<T, F>(&self, f: F) -> Result<T>
112 where
113 F: FnOnce(&str, &str) -> anyhow::Result<T>,
114 {
115 let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
116 self.read_raw(|name, value| {
117 // use a wrapping closure as an erzatz try block.
118 result = (|| {
119 let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
120 let value = value.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
121 f(name, value).map_err(PropertyWatcherError::CallbackError)
122 })()
123 });
124 result
125 }
126
127 /// Wait for the system property to change. This
128 /// records the serial number of the last change, so
129 /// race conditions are avoided.
130 pub fn wait(&mut self) -> Result<()> {
131 let mut new_serial = self.serial;
132 // Unsafe block to call __system_property_wait.
133 // All arguments are private to PropertyWatcher so we
134 // can be confident they are valid.
135 if !unsafe {
136 keystore2_system_property_bindgen::__system_property_wait(
137 self.prop_info,
138 self.serial,
139 &mut new_serial,
140 null_mut(),
141 )
142 } {
143 return Err(PropertyWatcherError::WaitFailed);
144 }
145 self.serial = new_serial;
146 Ok(())
147 }
148}