xref: /aosp_15_r20/system/security/keystore2/src/apc.rs (revision e1997b9af69e3155ead6e072d106a0077849ffba)
1*e1997b9aSAndroid Build Coastguard Worker // Copyright 2020, The Android Open Source Project
2*e1997b9aSAndroid Build Coastguard Worker //
3*e1997b9aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*e1997b9aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*e1997b9aSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*e1997b9aSAndroid Build Coastguard Worker //
7*e1997b9aSAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*e1997b9aSAndroid Build Coastguard Worker //
9*e1997b9aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*e1997b9aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*e1997b9aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e1997b9aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*e1997b9aSAndroid Build Coastguard Worker // limitations under the License.
14*e1997b9aSAndroid Build Coastguard Worker 
15*e1997b9aSAndroid Build Coastguard Worker //! This module implements the Android Protected Confirmation (APC) service as defined
16*e1997b9aSAndroid Build Coastguard Worker //! in the android.security.apc AIDL spec.
17*e1997b9aSAndroid Build Coastguard Worker 
18*e1997b9aSAndroid Build Coastguard Worker use std::{
19*e1997b9aSAndroid Build Coastguard Worker     cmp::PartialEq,
20*e1997b9aSAndroid Build Coastguard Worker     collections::HashMap,
21*e1997b9aSAndroid Build Coastguard Worker     sync::{mpsc::Sender, Arc, Mutex},
22*e1997b9aSAndroid Build Coastguard Worker };
23*e1997b9aSAndroid Build Coastguard Worker 
24*e1997b9aSAndroid Build Coastguard Worker use crate::error::anyhow_error_to_cstring;
25*e1997b9aSAndroid Build Coastguard Worker use crate::ks_err;
26*e1997b9aSAndroid Build Coastguard Worker use crate::utils::{compat_2_response_code, ui_opts_2_compat, watchdog as wd};
27*e1997b9aSAndroid Build Coastguard Worker use android_security_apc::aidl::android::security::apc::{
28*e1997b9aSAndroid Build Coastguard Worker     IConfirmationCallback::IConfirmationCallback,
29*e1997b9aSAndroid Build Coastguard Worker     IProtectedConfirmation::{BnProtectedConfirmation, IProtectedConfirmation},
30*e1997b9aSAndroid Build Coastguard Worker     ResponseCode::ResponseCode,
31*e1997b9aSAndroid Build Coastguard Worker };
32*e1997b9aSAndroid Build Coastguard Worker use android_security_apc::binder::{
33*e1997b9aSAndroid Build Coastguard Worker     BinderFeatures, ExceptionCode, Interface, Result as BinderResult, SpIBinder,
34*e1997b9aSAndroid Build Coastguard Worker     Status as BinderStatus, Strong, ThreadState,
35*e1997b9aSAndroid Build Coastguard Worker };
36*e1997b9aSAndroid Build Coastguard Worker use anyhow::{Context, Result};
37*e1997b9aSAndroid Build Coastguard Worker use keystore2_apc_compat::ApcHal;
38*e1997b9aSAndroid Build Coastguard Worker use keystore2_selinux as selinux;
39*e1997b9aSAndroid Build Coastguard Worker use std::time::{Duration, Instant};
40*e1997b9aSAndroid Build Coastguard Worker 
41*e1997b9aSAndroid Build Coastguard Worker /// This is the main APC error type, it wraps binder exceptions and the
42*e1997b9aSAndroid Build Coastguard Worker /// APC ResponseCode.
43*e1997b9aSAndroid Build Coastguard Worker #[derive(Debug, thiserror::Error, PartialEq, Eq)]
44*e1997b9aSAndroid Build Coastguard Worker pub enum Error {
45*e1997b9aSAndroid Build Coastguard Worker     /// Wraps an Android Protected Confirmation (APC) response code as defined by the
46*e1997b9aSAndroid Build Coastguard Worker     /// android.security.apc AIDL interface specification.
47*e1997b9aSAndroid Build Coastguard Worker     #[error("Error::Rc({0:?})")]
48*e1997b9aSAndroid Build Coastguard Worker     Rc(ResponseCode),
49*e1997b9aSAndroid Build Coastguard Worker     /// Wraps a Binder exception code other than a service specific exception.
50*e1997b9aSAndroid Build Coastguard Worker     #[error("Binder exception code {0:?}, {1:?}")]
51*e1997b9aSAndroid Build Coastguard Worker     Binder(ExceptionCode, i32),
52*e1997b9aSAndroid Build Coastguard Worker }
53*e1997b9aSAndroid Build Coastguard Worker 
54*e1997b9aSAndroid Build Coastguard Worker impl Error {
55*e1997b9aSAndroid Build Coastguard Worker     /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
sys() -> Self56*e1997b9aSAndroid Build Coastguard Worker     pub fn sys() -> Self {
57*e1997b9aSAndroid Build Coastguard Worker         Error::Rc(ResponseCode::SYSTEM_ERROR)
58*e1997b9aSAndroid Build Coastguard Worker     }
59*e1997b9aSAndroid Build Coastguard Worker 
60*e1997b9aSAndroid Build Coastguard Worker     /// Short hand for `Error::Rc(ResponseCode::OPERATION_PENDING)`
pending() -> Self61*e1997b9aSAndroid Build Coastguard Worker     pub fn pending() -> Self {
62*e1997b9aSAndroid Build Coastguard Worker         Error::Rc(ResponseCode::OPERATION_PENDING)
63*e1997b9aSAndroid Build Coastguard Worker     }
64*e1997b9aSAndroid Build Coastguard Worker 
65*e1997b9aSAndroid Build Coastguard Worker     /// Short hand for `Error::Rc(ResponseCode::IGNORED)`
ignored() -> Self66*e1997b9aSAndroid Build Coastguard Worker     pub fn ignored() -> Self {
67*e1997b9aSAndroid Build Coastguard Worker         Error::Rc(ResponseCode::IGNORED)
68*e1997b9aSAndroid Build Coastguard Worker     }
69*e1997b9aSAndroid Build Coastguard Worker 
70*e1997b9aSAndroid Build Coastguard Worker     /// Short hand for `Error::Rc(ResponseCode::UNIMPLEMENTED)`
unimplemented() -> Self71*e1997b9aSAndroid Build Coastguard Worker     pub fn unimplemented() -> Self {
72*e1997b9aSAndroid Build Coastguard Worker         Error::Rc(ResponseCode::UNIMPLEMENTED)
73*e1997b9aSAndroid Build Coastguard Worker     }
74*e1997b9aSAndroid Build Coastguard Worker }
75*e1997b9aSAndroid Build Coastguard Worker 
76*e1997b9aSAndroid Build Coastguard Worker /// Translate an error into a service-specific exception, logging along the way.
77*e1997b9aSAndroid Build Coastguard Worker ///
78*e1997b9aSAndroid Build Coastguard Worker /// `Error::Rc(x)` variants get mapped onto a service specific error code of `x`.
79*e1997b9aSAndroid Build Coastguard Worker /// `selinux::Error::perm()` is mapped on `ResponseCode::PERMISSION_DENIED`.
80*e1997b9aSAndroid Build Coastguard Worker ///
81*e1997b9aSAndroid Build Coastguard Worker /// All non `Error` error conditions get mapped onto ResponseCode::SYSTEM_ERROR`.
into_logged_binder(e: anyhow::Error) -> BinderStatus82*e1997b9aSAndroid Build Coastguard Worker pub fn into_logged_binder(e: anyhow::Error) -> BinderStatus {
83*e1997b9aSAndroid Build Coastguard Worker     log::error!("{:#?}", e);
84*e1997b9aSAndroid Build Coastguard Worker     let root_cause = e.root_cause();
85*e1997b9aSAndroid Build Coastguard Worker     let rc = match root_cause.downcast_ref::<Error>() {
86*e1997b9aSAndroid Build Coastguard Worker         Some(Error::Rc(rcode)) => rcode.0,
87*e1997b9aSAndroid Build Coastguard Worker         Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
88*e1997b9aSAndroid Build Coastguard Worker         None => match root_cause.downcast_ref::<selinux::Error>() {
89*e1997b9aSAndroid Build Coastguard Worker             Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
90*e1997b9aSAndroid Build Coastguard Worker             _ => ResponseCode::SYSTEM_ERROR.0,
91*e1997b9aSAndroid Build Coastguard Worker         },
92*e1997b9aSAndroid Build Coastguard Worker     };
93*e1997b9aSAndroid Build Coastguard Worker     BinderStatus::new_service_specific_error(rc, anyhow_error_to_cstring(&e).as_deref())
94*e1997b9aSAndroid Build Coastguard Worker }
95*e1997b9aSAndroid Build Coastguard Worker 
96*e1997b9aSAndroid Build Coastguard Worker /// Rate info records how many failed attempts a client has made to display a protected
97*e1997b9aSAndroid Build Coastguard Worker /// confirmation prompt. Clients are penalized for attempts that get declined by the user
98*e1997b9aSAndroid Build Coastguard Worker /// or attempts that get aborted by the client itself.
99*e1997b9aSAndroid Build Coastguard Worker ///
100*e1997b9aSAndroid Build Coastguard Worker /// After the third failed attempt the client has to cool down for 30 seconds before it
101*e1997b9aSAndroid Build Coastguard Worker /// it can retry. After the sixth failed attempt, the time doubles with every failed attempt
102*e1997b9aSAndroid Build Coastguard Worker /// until it goes into saturation at 24h.
103*e1997b9aSAndroid Build Coastguard Worker ///
104*e1997b9aSAndroid Build Coastguard Worker /// A successful user prompt resets the counter.
105*e1997b9aSAndroid Build Coastguard Worker #[derive(Debug, Clone)]
106*e1997b9aSAndroid Build Coastguard Worker struct RateInfo {
107*e1997b9aSAndroid Build Coastguard Worker     counter: u32,
108*e1997b9aSAndroid Build Coastguard Worker     timestamp: Instant,
109*e1997b9aSAndroid Build Coastguard Worker }
110*e1997b9aSAndroid Build Coastguard Worker 
111*e1997b9aSAndroid Build Coastguard Worker impl RateInfo {
112*e1997b9aSAndroid Build Coastguard Worker     const ONE_DAY: Duration = Duration::from_secs(60u64 * 60u64 * 24u64);
113*e1997b9aSAndroid Build Coastguard Worker 
get_remaining_back_off(&self) -> Option<Duration>114*e1997b9aSAndroid Build Coastguard Worker     fn get_remaining_back_off(&self) -> Option<Duration> {
115*e1997b9aSAndroid Build Coastguard Worker         let back_off = match self.counter {
116*e1997b9aSAndroid Build Coastguard Worker             // The first three attempts come without penalty.
117*e1997b9aSAndroid Build Coastguard Worker             0..=2 => return None,
118*e1997b9aSAndroid Build Coastguard Worker             // The next three attempts are are penalized with 30 seconds back off time.
119*e1997b9aSAndroid Build Coastguard Worker             3..=5 => Duration::from_secs(30),
120*e1997b9aSAndroid Build Coastguard Worker             // After that we double the back off time the with every additional attempt
121*e1997b9aSAndroid Build Coastguard Worker             // until we reach 1024m (~17h).
122*e1997b9aSAndroid Build Coastguard Worker             6..=16 => Duration::from_secs(60)
123*e1997b9aSAndroid Build Coastguard Worker                 .checked_mul(1u32 << (self.counter - 6))
124*e1997b9aSAndroid Build Coastguard Worker                 .unwrap_or(Self::ONE_DAY),
125*e1997b9aSAndroid Build Coastguard Worker             // After that we cap of at 24h between attempts.
126*e1997b9aSAndroid Build Coastguard Worker             _ => Self::ONE_DAY,
127*e1997b9aSAndroid Build Coastguard Worker         };
128*e1997b9aSAndroid Build Coastguard Worker         let elapsed = self.timestamp.elapsed();
129*e1997b9aSAndroid Build Coastguard Worker         // This does exactly what we want.
130*e1997b9aSAndroid Build Coastguard Worker         // `back_off - elapsed` is the remaining back off duration or None if elapsed is larger
131*e1997b9aSAndroid Build Coastguard Worker         // than back_off. Also, this operation cannot overflow as long as elapsed is less than
132*e1997b9aSAndroid Build Coastguard Worker         // back_off, which is all that we care about.
133*e1997b9aSAndroid Build Coastguard Worker         back_off.checked_sub(elapsed)
134*e1997b9aSAndroid Build Coastguard Worker     }
135*e1997b9aSAndroid Build Coastguard Worker }
136*e1997b9aSAndroid Build Coastguard Worker 
137*e1997b9aSAndroid Build Coastguard Worker impl Default for RateInfo {
default() -> Self138*e1997b9aSAndroid Build Coastguard Worker     fn default() -> Self {
139*e1997b9aSAndroid Build Coastguard Worker         Self { counter: 0u32, timestamp: Instant::now() }
140*e1997b9aSAndroid Build Coastguard Worker     }
141*e1997b9aSAndroid Build Coastguard Worker }
142*e1997b9aSAndroid Build Coastguard Worker 
143*e1997b9aSAndroid Build Coastguard Worker /// The APC session state represents the state of an APC session.
144*e1997b9aSAndroid Build Coastguard Worker struct ApcSessionState {
145*e1997b9aSAndroid Build Coastguard Worker     /// A reference to the APC HAL backend.
146*e1997b9aSAndroid Build Coastguard Worker     hal: Arc<ApcHal>,
147*e1997b9aSAndroid Build Coastguard Worker     /// The client callback object.
148*e1997b9aSAndroid Build Coastguard Worker     cb: SpIBinder,
149*e1997b9aSAndroid Build Coastguard Worker     /// The uid of the owner of this APC session.
150*e1997b9aSAndroid Build Coastguard Worker     uid: u32,
151*e1997b9aSAndroid Build Coastguard Worker     /// The time when this session was started.
152*e1997b9aSAndroid Build Coastguard Worker     start: Instant,
153*e1997b9aSAndroid Build Coastguard Worker     /// This is set when the client calls abort.
154*e1997b9aSAndroid Build Coastguard Worker     /// This is used by the rate limiting logic to determine
155*e1997b9aSAndroid Build Coastguard Worker     /// if the client needs to be penalized for this attempt.
156*e1997b9aSAndroid Build Coastguard Worker     client_aborted: bool,
157*e1997b9aSAndroid Build Coastguard Worker }
158*e1997b9aSAndroid Build Coastguard Worker 
159*e1997b9aSAndroid Build Coastguard Worker struct ApcState {
160*e1997b9aSAndroid Build Coastguard Worker     session: Option<ApcSessionState>,
161*e1997b9aSAndroid Build Coastguard Worker     rate_limiting: HashMap<u32, RateInfo>,
162*e1997b9aSAndroid Build Coastguard Worker     confirmation_token_sender: Sender<Vec<u8>>,
163*e1997b9aSAndroid Build Coastguard Worker }
164*e1997b9aSAndroid Build Coastguard Worker 
165*e1997b9aSAndroid Build Coastguard Worker impl ApcState {
new(confirmation_token_sender: Sender<Vec<u8>>) -> Self166*e1997b9aSAndroid Build Coastguard Worker     fn new(confirmation_token_sender: Sender<Vec<u8>>) -> Self {
167*e1997b9aSAndroid Build Coastguard Worker         Self { session: None, rate_limiting: Default::default(), confirmation_token_sender }
168*e1997b9aSAndroid Build Coastguard Worker     }
169*e1997b9aSAndroid Build Coastguard Worker }
170*e1997b9aSAndroid Build Coastguard Worker 
171*e1997b9aSAndroid Build Coastguard Worker /// Implementation of the APC service.
172*e1997b9aSAndroid Build Coastguard Worker pub struct ApcManager {
173*e1997b9aSAndroid Build Coastguard Worker     state: Arc<Mutex<ApcState>>,
174*e1997b9aSAndroid Build Coastguard Worker }
175*e1997b9aSAndroid Build Coastguard Worker 
176*e1997b9aSAndroid Build Coastguard Worker impl Interface for ApcManager {}
177*e1997b9aSAndroid Build Coastguard Worker 
178*e1997b9aSAndroid Build Coastguard Worker impl ApcManager {
179*e1997b9aSAndroid Build Coastguard Worker     /// Create a new instance of the Android Protected Confirmation service.
new_native_binder( confirmation_token_sender: Sender<Vec<u8>>, ) -> Result<Strong<dyn IProtectedConfirmation>>180*e1997b9aSAndroid Build Coastguard Worker     pub fn new_native_binder(
181*e1997b9aSAndroid Build Coastguard Worker         confirmation_token_sender: Sender<Vec<u8>>,
182*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<Strong<dyn IProtectedConfirmation>> {
183*e1997b9aSAndroid Build Coastguard Worker         Ok(BnProtectedConfirmation::new_binder(
184*e1997b9aSAndroid Build Coastguard Worker             Self { state: Arc::new(Mutex::new(ApcState::new(confirmation_token_sender))) },
185*e1997b9aSAndroid Build Coastguard Worker             BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
186*e1997b9aSAndroid Build Coastguard Worker         ))
187*e1997b9aSAndroid Build Coastguard Worker     }
188*e1997b9aSAndroid Build Coastguard Worker 
result( state: Arc<Mutex<ApcState>>, rc: u32, data_confirmed: Option<&[u8]>, confirmation_token: Option<&[u8]>, )189*e1997b9aSAndroid Build Coastguard Worker     fn result(
190*e1997b9aSAndroid Build Coastguard Worker         state: Arc<Mutex<ApcState>>,
191*e1997b9aSAndroid Build Coastguard Worker         rc: u32,
192*e1997b9aSAndroid Build Coastguard Worker         data_confirmed: Option<&[u8]>,
193*e1997b9aSAndroid Build Coastguard Worker         confirmation_token: Option<&[u8]>,
194*e1997b9aSAndroid Build Coastguard Worker     ) {
195*e1997b9aSAndroid Build Coastguard Worker         let mut state = state.lock().unwrap();
196*e1997b9aSAndroid Build Coastguard Worker         let (callback, uid, start, client_aborted) = match state.session.take() {
197*e1997b9aSAndroid Build Coastguard Worker             None => return, // Nothing to do
198*e1997b9aSAndroid Build Coastguard Worker             Some(ApcSessionState { cb: callback, uid, start, client_aborted, .. }) => {
199*e1997b9aSAndroid Build Coastguard Worker                 (callback, uid, start, client_aborted)
200*e1997b9aSAndroid Build Coastguard Worker             }
201*e1997b9aSAndroid Build Coastguard Worker         };
202*e1997b9aSAndroid Build Coastguard Worker 
203*e1997b9aSAndroid Build Coastguard Worker         let rc = compat_2_response_code(rc);
204*e1997b9aSAndroid Build Coastguard Worker 
205*e1997b9aSAndroid Build Coastguard Worker         // Update rate limiting information.
206*e1997b9aSAndroid Build Coastguard Worker         match (rc, client_aborted, confirmation_token) {
207*e1997b9aSAndroid Build Coastguard Worker             // If the user confirmed the dialog.
208*e1997b9aSAndroid Build Coastguard Worker             (ResponseCode::OK, _, Some(confirmation_token)) => {
209*e1997b9aSAndroid Build Coastguard Worker                 // Reset counter.
210*e1997b9aSAndroid Build Coastguard Worker                 state.rate_limiting.remove(&uid);
211*e1997b9aSAndroid Build Coastguard Worker                 // Send confirmation token to the enforcement module.
212*e1997b9aSAndroid Build Coastguard Worker                 if let Err(e) = state.confirmation_token_sender.send(confirmation_token.to_vec()) {
213*e1997b9aSAndroid Build Coastguard Worker                     log::error!("Got confirmation token, but receiver would not have it. {:?}", e);
214*e1997b9aSAndroid Build Coastguard Worker                 }
215*e1997b9aSAndroid Build Coastguard Worker             }
216*e1997b9aSAndroid Build Coastguard Worker             // If cancelled by the user or if aborted by the client.
217*e1997b9aSAndroid Build Coastguard Worker             (ResponseCode::CANCELLED, _, _) | (ResponseCode::ABORTED, true, _) => {
218*e1997b9aSAndroid Build Coastguard Worker                 // Penalize.
219*e1997b9aSAndroid Build Coastguard Worker                 let rate_info = state.rate_limiting.entry(uid).or_default();
220*e1997b9aSAndroid Build Coastguard Worker                 rate_info.counter += 1;
221*e1997b9aSAndroid Build Coastguard Worker                 rate_info.timestamp = start;
222*e1997b9aSAndroid Build Coastguard Worker             }
223*e1997b9aSAndroid Build Coastguard Worker             (ResponseCode::OK, _, None) => {
224*e1997b9aSAndroid Build Coastguard Worker                 log::error!(
225*e1997b9aSAndroid Build Coastguard Worker                     "Confirmation prompt was successful but no confirmation token was returned."
226*e1997b9aSAndroid Build Coastguard Worker                 );
227*e1997b9aSAndroid Build Coastguard Worker             }
228*e1997b9aSAndroid Build Coastguard Worker             // In any other case this try does not count at all.
229*e1997b9aSAndroid Build Coastguard Worker             _ => {}
230*e1997b9aSAndroid Build Coastguard Worker         }
231*e1997b9aSAndroid Build Coastguard Worker         drop(state);
232*e1997b9aSAndroid Build Coastguard Worker 
233*e1997b9aSAndroid Build Coastguard Worker         if let Ok(listener) = callback.into_interface::<dyn IConfirmationCallback>() {
234*e1997b9aSAndroid Build Coastguard Worker             if let Err(e) = listener.onCompleted(rc, data_confirmed) {
235*e1997b9aSAndroid Build Coastguard Worker                 log::error!("Reporting completion to client failed {:?}", e)
236*e1997b9aSAndroid Build Coastguard Worker             }
237*e1997b9aSAndroid Build Coastguard Worker         } else {
238*e1997b9aSAndroid Build Coastguard Worker             log::error!("SpIBinder is not a IConfirmationCallback.");
239*e1997b9aSAndroid Build Coastguard Worker         }
240*e1997b9aSAndroid Build Coastguard Worker     }
241*e1997b9aSAndroid Build Coastguard Worker 
present_prompt( &self, listener: &binder::Strong<dyn IConfirmationCallback>, prompt_text: &str, extra_data: &[u8], locale: &str, ui_option_flags: i32, ) -> Result<()>242*e1997b9aSAndroid Build Coastguard Worker     fn present_prompt(
243*e1997b9aSAndroid Build Coastguard Worker         &self,
244*e1997b9aSAndroid Build Coastguard Worker         listener: &binder::Strong<dyn IConfirmationCallback>,
245*e1997b9aSAndroid Build Coastguard Worker         prompt_text: &str,
246*e1997b9aSAndroid Build Coastguard Worker         extra_data: &[u8],
247*e1997b9aSAndroid Build Coastguard Worker         locale: &str,
248*e1997b9aSAndroid Build Coastguard Worker         ui_option_flags: i32,
249*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<()> {
250*e1997b9aSAndroid Build Coastguard Worker         let mut state = self.state.lock().unwrap();
251*e1997b9aSAndroid Build Coastguard Worker         if state.session.is_some() {
252*e1997b9aSAndroid Build Coastguard Worker             return Err(Error::pending()).context(ks_err!("APC Session pending."));
253*e1997b9aSAndroid Build Coastguard Worker         }
254*e1997b9aSAndroid Build Coastguard Worker 
255*e1997b9aSAndroid Build Coastguard Worker         // Perform rate limiting.
256*e1997b9aSAndroid Build Coastguard Worker         let uid = ThreadState::get_calling_uid();
257*e1997b9aSAndroid Build Coastguard Worker         match state.rate_limiting.get(&uid) {
258*e1997b9aSAndroid Build Coastguard Worker             None => {}
259*e1997b9aSAndroid Build Coastguard Worker             Some(rate_info) => {
260*e1997b9aSAndroid Build Coastguard Worker                 if let Some(back_off) = rate_info.get_remaining_back_off() {
261*e1997b9aSAndroid Build Coastguard Worker                     return Err(Error::sys()).context(ks_err!(
262*e1997b9aSAndroid Build Coastguard Worker                         "APC Cooling down. Remaining back-off: {}s",
263*e1997b9aSAndroid Build Coastguard Worker                         back_off.as_secs()
264*e1997b9aSAndroid Build Coastguard Worker                     ));
265*e1997b9aSAndroid Build Coastguard Worker                 }
266*e1997b9aSAndroid Build Coastguard Worker             }
267*e1997b9aSAndroid Build Coastguard Worker         }
268*e1997b9aSAndroid Build Coastguard Worker 
269*e1997b9aSAndroid Build Coastguard Worker         let hal = ApcHal::try_get_service();
270*e1997b9aSAndroid Build Coastguard Worker         let hal = match hal {
271*e1997b9aSAndroid Build Coastguard Worker             None => {
272*e1997b9aSAndroid Build Coastguard Worker                 return Err(Error::unimplemented()).context(ks_err!("APC not supported."));
273*e1997b9aSAndroid Build Coastguard Worker             }
274*e1997b9aSAndroid Build Coastguard Worker             Some(h) => Arc::new(h),
275*e1997b9aSAndroid Build Coastguard Worker         };
276*e1997b9aSAndroid Build Coastguard Worker 
277*e1997b9aSAndroid Build Coastguard Worker         let ui_opts = ui_opts_2_compat(ui_option_flags);
278*e1997b9aSAndroid Build Coastguard Worker 
279*e1997b9aSAndroid Build Coastguard Worker         let state_clone = self.state.clone();
280*e1997b9aSAndroid Build Coastguard Worker         hal.prompt_user_confirmation(
281*e1997b9aSAndroid Build Coastguard Worker             prompt_text,
282*e1997b9aSAndroid Build Coastguard Worker             extra_data,
283*e1997b9aSAndroid Build Coastguard Worker             locale,
284*e1997b9aSAndroid Build Coastguard Worker             ui_opts,
285*e1997b9aSAndroid Build Coastguard Worker             move |rc, data_confirmed, confirmation_token| {
286*e1997b9aSAndroid Build Coastguard Worker                 Self::result(state_clone, rc, data_confirmed, confirmation_token)
287*e1997b9aSAndroid Build Coastguard Worker             },
288*e1997b9aSAndroid Build Coastguard Worker         )
289*e1997b9aSAndroid Build Coastguard Worker         .map_err(|rc| Error::Rc(compat_2_response_code(rc)))
290*e1997b9aSAndroid Build Coastguard Worker         .context(ks_err!("APC Failed to present prompt."))?;
291*e1997b9aSAndroid Build Coastguard Worker         state.session = Some(ApcSessionState {
292*e1997b9aSAndroid Build Coastguard Worker             hal,
293*e1997b9aSAndroid Build Coastguard Worker             cb: listener.as_binder(),
294*e1997b9aSAndroid Build Coastguard Worker             uid,
295*e1997b9aSAndroid Build Coastguard Worker             start: Instant::now(),
296*e1997b9aSAndroid Build Coastguard Worker             client_aborted: false,
297*e1997b9aSAndroid Build Coastguard Worker         });
298*e1997b9aSAndroid Build Coastguard Worker         Ok(())
299*e1997b9aSAndroid Build Coastguard Worker     }
300*e1997b9aSAndroid Build Coastguard Worker 
cancel_prompt(&self, listener: &binder::Strong<dyn IConfirmationCallback>) -> Result<()>301*e1997b9aSAndroid Build Coastguard Worker     fn cancel_prompt(&self, listener: &binder::Strong<dyn IConfirmationCallback>) -> Result<()> {
302*e1997b9aSAndroid Build Coastguard Worker         let mut state = self.state.lock().unwrap();
303*e1997b9aSAndroid Build Coastguard Worker         let hal = match &mut state.session {
304*e1997b9aSAndroid Build Coastguard Worker             None => {
305*e1997b9aSAndroid Build Coastguard Worker                 return Err(Error::ignored())
306*e1997b9aSAndroid Build Coastguard Worker                     .context(ks_err!("Attempt to cancel non existing session. Ignoring."));
307*e1997b9aSAndroid Build Coastguard Worker             }
308*e1997b9aSAndroid Build Coastguard Worker             Some(session) => {
309*e1997b9aSAndroid Build Coastguard Worker                 if session.cb != listener.as_binder() {
310*e1997b9aSAndroid Build Coastguard Worker                     return Err(Error::ignored()).context(ks_err!(
311*e1997b9aSAndroid Build Coastguard Worker                         "Attempt to cancel session not belonging to caller. Ignoring."
312*e1997b9aSAndroid Build Coastguard Worker                     ));
313*e1997b9aSAndroid Build Coastguard Worker                 }
314*e1997b9aSAndroid Build Coastguard Worker                 session.client_aborted = true;
315*e1997b9aSAndroid Build Coastguard Worker                 session.hal.clone()
316*e1997b9aSAndroid Build Coastguard Worker             }
317*e1997b9aSAndroid Build Coastguard Worker         };
318*e1997b9aSAndroid Build Coastguard Worker         drop(state);
319*e1997b9aSAndroid Build Coastguard Worker         hal.abort();
320*e1997b9aSAndroid Build Coastguard Worker         Ok(())
321*e1997b9aSAndroid Build Coastguard Worker     }
322*e1997b9aSAndroid Build Coastguard Worker 
is_supported() -> Result<bool>323*e1997b9aSAndroid Build Coastguard Worker     fn is_supported() -> Result<bool> {
324*e1997b9aSAndroid Build Coastguard Worker         Ok(ApcHal::try_get_service().is_some())
325*e1997b9aSAndroid Build Coastguard Worker     }
326*e1997b9aSAndroid Build Coastguard Worker }
327*e1997b9aSAndroid Build Coastguard Worker 
328*e1997b9aSAndroid Build Coastguard Worker impl IProtectedConfirmation for ApcManager {
presentPrompt( &self, listener: &binder::Strong<dyn IConfirmationCallback>, prompt_text: &str, extra_data: &[u8], locale: &str, ui_option_flags: i32, ) -> BinderResult<()>329*e1997b9aSAndroid Build Coastguard Worker     fn presentPrompt(
330*e1997b9aSAndroid Build Coastguard Worker         &self,
331*e1997b9aSAndroid Build Coastguard Worker         listener: &binder::Strong<dyn IConfirmationCallback>,
332*e1997b9aSAndroid Build Coastguard Worker         prompt_text: &str,
333*e1997b9aSAndroid Build Coastguard Worker         extra_data: &[u8],
334*e1997b9aSAndroid Build Coastguard Worker         locale: &str,
335*e1997b9aSAndroid Build Coastguard Worker         ui_option_flags: i32,
336*e1997b9aSAndroid Build Coastguard Worker     ) -> BinderResult<()> {
337*e1997b9aSAndroid Build Coastguard Worker         // presentPrompt can take more time than other operations.
338*e1997b9aSAndroid Build Coastguard Worker         let _wp = wd::watch_millis("IProtectedConfirmation::presentPrompt", 3000);
339*e1997b9aSAndroid Build Coastguard Worker         self.present_prompt(listener, prompt_text, extra_data, locale, ui_option_flags)
340*e1997b9aSAndroid Build Coastguard Worker             .map_err(into_logged_binder)
341*e1997b9aSAndroid Build Coastguard Worker     }
cancelPrompt( &self, listener: &binder::Strong<dyn IConfirmationCallback>, ) -> BinderResult<()>342*e1997b9aSAndroid Build Coastguard Worker     fn cancelPrompt(
343*e1997b9aSAndroid Build Coastguard Worker         &self,
344*e1997b9aSAndroid Build Coastguard Worker         listener: &binder::Strong<dyn IConfirmationCallback>,
345*e1997b9aSAndroid Build Coastguard Worker     ) -> BinderResult<()> {
346*e1997b9aSAndroid Build Coastguard Worker         let _wp = wd::watch("IProtectedConfirmation::cancelPrompt");
347*e1997b9aSAndroid Build Coastguard Worker         self.cancel_prompt(listener).map_err(into_logged_binder)
348*e1997b9aSAndroid Build Coastguard Worker     }
isSupported(&self) -> BinderResult<bool>349*e1997b9aSAndroid Build Coastguard Worker     fn isSupported(&self) -> BinderResult<bool> {
350*e1997b9aSAndroid Build Coastguard Worker         let _wp = wd::watch("IProtectedConfirmation::isSupported");
351*e1997b9aSAndroid Build Coastguard Worker         Self::is_supported().map_err(into_logged_binder)
352*e1997b9aSAndroid Build Coastguard Worker     }
353*e1997b9aSAndroid Build Coastguard Worker }
354