blob: be13c88696894322beb6ffc3f371a7ab498cadfe [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
Seth Moore71940352021-04-14 23:03:56 +000018use keystore2_system_property_bindgen::prop_info as PropInfo;
Paul Crowley7c57bf12021-02-02 16:26:57 -080019use std::os::raw::c_char;
Seth Moore71940352021-04-14 23:03:56 +000020use std::ptr::null;
Paul Crowley7c57bf12021-02-02 16:26:57 -080021use std::{
22 ffi::{c_void, CStr, CString},
23 str::Utf8Error,
24};
25use thiserror::Error;
26
27/// Errors this crate can generate
28#[derive(Error, Debug)]
29pub enum PropertyWatcherError {
30 /// We can't watch for a property whose name contains a NUL character.
31 #[error("Cannot convert name to C string")]
32 BadNameError(#[from] std::ffi::NulError),
33 /// We can only watch for properties that exist when the watcher is created.
34 #[error("System property is absent")]
35 SystemPropertyAbsent,
36 /// __system_property_wait timed out despite being given no timeout.
37 #[error("Wait failed")]
38 WaitFailed,
39 /// read callback was not called
40 #[error("__system_property_read_callback did not call callback")]
41 ReadCallbackNotCalled,
42 /// read callback gave us a NULL pointer
43 #[error("__system_property_read_callback gave us a NULL pointer instead of a string")]
44 MissingCString,
45 /// read callback gave us a bad C string
46 #[error("__system_property_read_callback gave us a non-UTF8 C string")]
47 BadCString(#[from] Utf8Error),
48 /// read callback returned an error
49 #[error("Callback failed")]
50 CallbackError(#[from] anyhow::Error),
51}
52
53/// Result type specific for this crate.
54pub type Result<T> = std::result::Result<T, PropertyWatcherError>;
55
56/// PropertyWatcher takes the name of an Android system property such
57/// as `keystore.boot_level`; it can report the current value of this
58/// property, or wait for it to change.
59pub struct PropertyWatcher {
Seth Moore71940352021-04-14 23:03:56 +000060 prop_name: CString,
61 prop_info: *const PropInfo,
Paul Crowley7c57bf12021-02-02 16:26:57 -080062 serial: keystore2_system_property_bindgen::__uint32_t,
63}
64
65impl PropertyWatcher {
66 /// Create a PropertyWatcher for the named system property.
67 pub fn new(name: &str) -> Result<Self> {
Seth Moore71940352021-04-14 23:03:56 +000068 Ok(Self { prop_name: CString::new(name)?, prop_info: null(), serial: 0 })
69 }
70
71 // Lazy-initializing accessor for self.prop_info.
72 fn get_prop_info(&mut self) -> Option<*const PropInfo> {
73 if self.prop_info.is_null() {
74 // Unsafe required for FFI call. Input and output are both const.
75 // The returned pointer is valid for the lifetime of the program.
76 self.prop_info = unsafe {
77 keystore2_system_property_bindgen::__system_property_find(self.prop_name.as_ptr())
78 };
79 }
80 if self.prop_info.is_null() {
81 None
Paul Crowley7c57bf12021-02-02 16:26:57 -080082 } else {
Seth Moore71940352021-04-14 23:03:56 +000083 Some(self.prop_info)
Paul Crowley7c57bf12021-02-02 16:26:57 -080084 }
85 }
86
Seth Moore71940352021-04-14 23:03:56 +000087 fn read_raw(prop_info: *const PropInfo, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
Paul Crowley7c57bf12021-02-02 16:26:57 -080088 // Unsafe function converts values passed to us by
89 // __system_property_read_callback to Rust form
90 // and pass them to inner callback.
91 unsafe extern "C" fn callback(
92 res_p: *mut c_void,
93 name: *const c_char,
94 value: *const c_char,
95 _: keystore2_system_property_bindgen::__uint32_t,
96 ) {
97 let name = if name.is_null() { None } else { Some(CStr::from_ptr(name)) };
98 let value = if value.is_null() { None } else { Some(CStr::from_ptr(value)) };
99 let f = &mut *res_p.cast::<&mut dyn FnMut(Option<&CStr>, Option<&CStr>)>();
100 f(name, value);
101 }
102
103 let mut f: &mut dyn FnOnce(Option<&CStr>, Option<&CStr>) = &mut f;
104
105 // Unsafe block for FFI call. We convert the FnOnce
106 // to a void pointer, and unwrap it in our callback.
107 unsafe {
108 keystore2_system_property_bindgen::__system_property_read_callback(
Seth Moore71940352021-04-14 23:03:56 +0000109 prop_info,
Paul Crowley7c57bf12021-02-02 16:26:57 -0800110 Some(callback),
111 &mut f as *mut _ as *mut c_void,
112 )
113 }
114 }
115
116 /// Call the passed function, passing it the name and current value
117 /// of this system property. See documentation for
118 /// `__system_property_read_callback` for details.
Seth Moore71940352021-04-14 23:03:56 +0000119 /// Returns an error if the property is empty or doesn't exist.
120 pub fn read<T, F>(&mut self, f: F) -> Result<T>
Paul Crowley7c57bf12021-02-02 16:26:57 -0800121 where
122 F: FnOnce(&str, &str) -> anyhow::Result<T>,
123 {
Seth Moore71940352021-04-14 23:03:56 +0000124 let prop_info = self.get_prop_info().ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
Paul Crowley7c57bf12021-02-02 16:26:57 -0800125 let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
Seth Moore71940352021-04-14 23:03:56 +0000126 Self::read_raw(prop_info, |name, value| {
Paul Crowley7c57bf12021-02-02 16:26:57 -0800127 // use a wrapping closure as an erzatz try block.
128 result = (|| {
129 let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
130 let value = value.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
131 f(name, value).map_err(PropertyWatcherError::CallbackError)
132 })()
133 });
134 result
135 }
136
Seth Moore71940352021-04-14 23:03:56 +0000137 // Waits for the property that self is watching to be created. Returns immediately if the
138 // property already exists.
139 fn wait_for_property_creation(&mut self) -> Result<()> {
140 let mut global_serial = 0;
141 loop {
142 match self.get_prop_info() {
143 Some(_) => return Ok(()),
144 None => {
145 // Unsafe call for FFI. The function modifies only global_serial, and has
146 // no side-effects.
147 if !unsafe {
148 // Wait for a global serial number change, then try again. On success,
149 // the function will update global_serial with the last version seen.
150 keystore2_system_property_bindgen::__system_property_wait(
151 null(),
152 global_serial,
153 &mut global_serial,
154 null(),
155 )
156 } {
157 return Err(PropertyWatcherError::WaitFailed);
158 }
159 }
160 }
161 }
162 }
163
Paul Crowley7c57bf12021-02-02 16:26:57 -0800164 /// Wait for the system property to change. This
165 /// records the serial number of the last change, so
166 /// race conditions are avoided.
167 pub fn wait(&mut self) -> Result<()> {
Seth Moore71940352021-04-14 23:03:56 +0000168 // If the property is null, then wait for it to be created. Subsequent waits will
169 // skip this step and wait for our specific property to change.
170 if self.prop_info.is_null() {
171 return self.wait_for_property_creation();
172 }
173
Paul Crowley7c57bf12021-02-02 16:26:57 -0800174 let mut new_serial = self.serial;
175 // Unsafe block to call __system_property_wait.
176 // All arguments are private to PropertyWatcher so we
177 // can be confident they are valid.
178 if !unsafe {
179 keystore2_system_property_bindgen::__system_property_wait(
180 self.prop_info,
181 self.serial,
182 &mut new_serial,
Seth Moore71940352021-04-14 23:03:56 +0000183 null(),
Paul Crowley7c57bf12021-02-02 16:26:57 -0800184 )
185 } {
186 return Err(PropertyWatcherError::WaitFailed);
187 }
188 self.serial = new_serial;
189 Ok(())
190 }
191}