1 // Copyright 2024 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 use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::{
16     ISecretkeeper::ISecretkeeper, SecretId::SecretId,
17 };
18 use android_system_virtualizationmaintenance::aidl::android::system::virtualizationmaintenance;
19 use anyhow::{anyhow, Context, Result};
20 use binder::Strong;
21 use log::{error, info, warn};
22 use virtualizationmaintenance::IVirtualizationReconciliationCallback::IVirtualizationReconciliationCallback;
23 
24 mod vmdb;
25 use vmdb::{VmId, VmIdDb};
26 
27 /// Interface name for the Secretkeeper HAL.
28 const SECRETKEEPER_SERVICE: &str = "android.hardware.security.secretkeeper.ISecretkeeper/default";
29 
30 /// Directory in which to write persistent state.
31 const PERSISTENT_DIRECTORY: &str = "/data/misc/apexdata/com.android.virt";
32 
33 /// Maximum number of VM IDs to delete at once.  Needs to be smaller than both the maximum
34 /// number of SQLite parameters (999) and also small enough that an ISecretkeeper::deleteIds
35 /// parcel fits within max AIDL message size.
36 const DELETE_MAX_BATCH_SIZE: usize = 100;
37 
38 /// Maximum number of VM IDs that a single app can have.
39 const MAX_VM_IDS_PER_APP: usize = 400;
40 
41 /// State related to VM secrets.
42 pub struct State {
43     /// The real state, lazily created when we first need it.
44     inner: Option<InnerState>,
45 }
46 
47 struct InnerState {
48     sk: binder::Strong<dyn ISecretkeeper>,
49     /// Database of VM IDs,
50     vm_id_db: VmIdDb,
51     batch_size: usize,
52 }
53 
54 impl State {
new() -> Option<Self>55     pub fn new() -> Option<Self> {
56         if is_sk_present() {
57             // Don't instantiate the inner state yet, that will happen when it is needed.
58             Some(Self { inner: None })
59         } else {
60             // If the Secretkeeper HAL doesn't exist, there's never any point in trying to
61             // handle maintenance for it.
62             info!("Failed to find a Secretkeeper instance; skipping secret management");
63             None
64         }
65     }
66 
67     /// Return the existing inner state, or create one if there isn't one.
68     /// This is done on demand as in early boot (before we need Secretkeeper) it may not be
69     /// available to connect to. See b/331417880.
get_inner(&mut self) -> Result<&mut InnerState>70     fn get_inner(&mut self) -> Result<&mut InnerState> {
71         if self.inner.is_none() {
72             self.inner = Some(InnerState::new()?);
73         }
74         Ok(self.inner.as_mut().unwrap())
75     }
76 
77     /// Record a new VM ID.  If there is an existing owner (user_id, app_id) for the VM ID,
78     /// it will be replaced.
add_id(&mut self, vm_id: &VmId, user_id: u32, app_id: u32) -> Result<()>79     pub fn add_id(&mut self, vm_id: &VmId, user_id: u32, app_id: u32) -> Result<()> {
80         self.get_inner()?.add_id(vm_id, user_id, app_id)
81     }
82 
83     /// Delete the VM IDs associated with Android user ID `user_id`.
delete_ids_for_user(&mut self, user_id: i32) -> Result<()>84     pub fn delete_ids_for_user(&mut self, user_id: i32) -> Result<()> {
85         self.get_inner()?.delete_ids_for_user(user_id)
86     }
87 
88     /// Delete the VM IDs associated with `(user_id, app_id)`.
delete_ids_for_app(&mut self, user_id: i32, app_id: i32) -> Result<()>89     pub fn delete_ids_for_app(&mut self, user_id: i32, app_id: i32) -> Result<()> {
90         self.get_inner()?.delete_ids_for_app(user_id, app_id)
91     }
92 
93     /// Delete the provided VM ID associated with `(user_id, app_id)` from both Secretkeeper and
94     /// the database.
delete_id(&mut self, vm_id: &VmId, user_id: u32, app_id: u32)95     pub fn delete_id(&mut self, vm_id: &VmId, user_id: u32, app_id: u32) {
96         let Ok(inner) = self.get_inner() else {
97             warn!("No Secretkeeper available, not deleting secrets");
98             return;
99         };
100 
101         inner.delete_id_for_app(vm_id, user_id, app_id)
102     }
103 
104     /// Perform reconciliation to allow for possibly missed notifications of user or app removal.
reconcile( &mut self, callback: &Strong<dyn IVirtualizationReconciliationCallback>, ) -> Result<()>105     pub fn reconcile(
106         &mut self,
107         callback: &Strong<dyn IVirtualizationReconciliationCallback>,
108     ) -> Result<()> {
109         self.get_inner()?.reconcile(callback)
110     }
111 }
112 
113 impl InnerState {
new() -> Result<Self>114     fn new() -> Result<Self> {
115         info!("Connecting to {SECRETKEEPER_SERVICE}");
116         let sk = binder::wait_for_interface::<dyn ISecretkeeper>(SECRETKEEPER_SERVICE)
117             .context("Connecting to {SECRETKEEPER_SERVICE}")?;
118         let (vm_id_db, created) = VmIdDb::new(PERSISTENT_DIRECTORY)
119             .context("Connecting to secret management database")?;
120         if created {
121             // If the database did not previously exist, then this appears to be the first run of
122             // `virtualizationservice` since device setup or factory reset.  In case of the latter,
123             // delete any secrets that may be left over from before reset, thus ensuring that the
124             // local database state matches that of the TA (i.e. empty).
125             warn!("no existing VM ID DB; clearing any previous secrets to match fresh DB");
126             if let Err(e) = sk.deleteAll() {
127                 error!("failed to delete previous secrets, dropping database: {e:?}");
128                 vm_id_db.delete_db_file(PERSISTENT_DIRECTORY);
129                 return Err(e.into());
130             }
131         } else {
132             info!("re-using existing VM ID DB");
133         }
134         Ok(Self { sk, vm_id_db, batch_size: DELETE_MAX_BATCH_SIZE })
135     }
136 
add_id(&mut self, vm_id: &VmId, user_id: u32, app_id: u32) -> Result<()>137     fn add_id(&mut self, vm_id: &VmId, user_id: u32, app_id: u32) -> Result<()> {
138         let user_id: i32 = user_id.try_into().context(format!("user_id {user_id} out of range"))?;
139         let app_id: i32 = app_id.try_into().context(format!("app_id {app_id} out of range"))?;
140 
141         // To prevent unbounded growth of VM IDs (and the associated state) for an app, limit the
142         // number of VM IDs per app.
143         let count = self
144             .vm_id_db
145             .count_vm_ids_for_app(user_id, app_id)
146             .context("failed to determine VM count")?;
147         if count >= MAX_VM_IDS_PER_APP {
148             // The owner has too many VM IDs, so delete the oldest IDs so that the new VM ID
149             // creation can progress/succeed.
150             let purge = 1 + count - MAX_VM_IDS_PER_APP;
151             let old_vm_ids = self
152                 .vm_id_db
153                 .oldest_vm_ids_for_app(user_id, app_id, purge)
154                 .context("failed to find oldest VM IDs")?;
155             error!("Deleting {purge} of {count} VM IDs for user_id={user_id}, app_id={app_id}");
156             self.delete_ids(&old_vm_ids);
157         }
158         self.vm_id_db.add_vm_id(vm_id, user_id, app_id)
159     }
160 
delete_id_for_app(&mut self, vm_id: &VmId, user_id: u32, app_id: u32)161     fn delete_id_for_app(&mut self, vm_id: &VmId, user_id: u32, app_id: u32) {
162         if !self.vm_id_db.is_vm_id_for_app(vm_id, user_id, app_id).unwrap_or(false) {
163             info!(
164                 "delete_id_for_app - VM id not associated with user_id={user_id}, app_id={app_id}"
165             );
166             return;
167         }
168         self.delete_ids(&[*vm_id])
169     }
170 
delete_ids_for_user(&mut self, user_id: i32) -> Result<()>171     fn delete_ids_for_user(&mut self, user_id: i32) -> Result<()> {
172         let vm_ids = self.vm_id_db.vm_ids_for_user(user_id)?;
173         info!(
174             "delete_ids_for_user(user_id={user_id}) triggers deletion of {} secrets",
175             vm_ids.len()
176         );
177         self.delete_ids(&vm_ids);
178         Ok(())
179     }
180 
delete_ids_for_app(&mut self, user_id: i32, app_id: i32) -> Result<()>181     fn delete_ids_for_app(&mut self, user_id: i32, app_id: i32) -> Result<()> {
182         let vm_ids = self.vm_id_db.vm_ids_for_app(user_id, app_id)?;
183         info!(
184             "delete_ids_for_app(user_id={user_id}, app_id={app_id}) removes {} secrets",
185             vm_ids.len()
186         );
187         self.delete_ids(&vm_ids);
188         Ok(())
189     }
190 
delete_ids(&mut self, mut vm_ids: &[VmId])191     fn delete_ids(&mut self, mut vm_ids: &[VmId]) {
192         while !vm_ids.is_empty() {
193             let len = std::cmp::min(vm_ids.len(), self.batch_size);
194             let batch = &vm_ids[..len];
195             self.delete_ids_batch(batch);
196             vm_ids = &vm_ids[len..];
197         }
198     }
199 
200     /// Delete a batch of VM IDs from both Secretkeeper and the database. The batch is assumed
201     /// to be smaller than both:
202     /// - the corresponding limit for number of database parameters
203     /// - the corresponding limit for maximum size of a single AIDL message for `ISecretkeeper`.
delete_ids_batch(&mut self, vm_ids: &[VmId])204     fn delete_ids_batch(&mut self, vm_ids: &[VmId]) {
205         let secret_ids: Vec<SecretId> = vm_ids.iter().map(|id| SecretId { id: *id }).collect();
206         if let Err(e) = self.sk.deleteIds(&secret_ids) {
207             error!("failed to delete all secrets from Secretkeeper: {e:?}");
208         }
209         if let Err(e) = self.vm_id_db.delete_vm_ids(vm_ids) {
210             error!("failed to remove secret IDs from database: {e:?}");
211         }
212     }
213 
reconcile( &mut self, callback: &Strong<dyn IVirtualizationReconciliationCallback>, ) -> Result<()>214     fn reconcile(
215         &mut self,
216         callback: &Strong<dyn IVirtualizationReconciliationCallback>,
217     ) -> Result<()> {
218         // First, retrieve all (user_id, app_id) pairs that own a VM.
219         let owners = self.vm_id_db.get_all_owners().context("failed to retrieve owners from DB")?;
220         if owners.is_empty() {
221             info!("no VM owners, nothing to do");
222             return Ok(());
223         }
224 
225         // Look for absent users.
226         let mut users: Vec<i32> = owners.iter().map(|(u, _a)| *u).collect();
227         users.sort();
228         users.dedup();
229         let users_exist = callback
230             .doUsersExist(&users)
231             .context(format!("failed to determine if {} users exist", users.len()))?;
232         if users_exist.len() != users.len() {
233             error!("callback returned {} bools for {} inputs!", users_exist.len(), users.len());
234             return Err(anyhow!("unexpected number of results from callback"));
235         }
236 
237         for (user_id, present) in users.into_iter().zip(users_exist.into_iter()) {
238             if present {
239                 // User is still present, but are all of the associated apps?
240                 let mut apps: Vec<i32> = owners
241                     .iter()
242                     .filter_map(|(u, a)| if *u == user_id { Some(*a) } else { None })
243                     .collect();
244                 apps.sort();
245                 apps.dedup();
246 
247                 let apps_exist = callback
248                     .doAppsExist(user_id, &apps)
249                     .context(format!("failed to check apps for user {user_id}"))?;
250                 if apps_exist.len() != apps.len() {
251                     error!(
252                         "callback returned {} bools for {} inputs!",
253                         apps_exist.len(),
254                         apps.len()
255                     );
256                     return Err(anyhow!("unexpected number of results from callback"));
257                 }
258 
259                 let missing_apps: Vec<i32> = apps
260                     .iter()
261                     .zip(apps_exist.into_iter())
262                     .filter_map(|(app_id, present)| if present { None } else { Some(*app_id) })
263                     .collect();
264 
265                 for app_id in missing_apps {
266                     if core_app_id(app_id) {
267                         info!("Skipping deletion for core app {app_id} for user {user_id}");
268                         continue;
269                     }
270                     info!("App {app_id} for user {user_id} absent, deleting associated VM IDs");
271                     if let Err(err) = self.delete_ids_for_app(user_id, app_id) {
272                         error!("Failed to delete VM ID for user {user_id} app {app_id}: {err:?}");
273                     }
274                 }
275             } else {
276                 info!("user {user_id} no longer present, deleting associated VM IDs");
277                 if let Err(err) = self.delete_ids_for_user(user_id) {
278                     error!("Failed to delete VM IDs for user {user_id} : {err:?}");
279                 }
280             }
281         }
282 
283         Ok(())
284     }
285 }
286 
287 /// Indicate whether an app ID belongs to a system core component.
core_app_id(app_id: i32) -> bool288 fn core_app_id(app_id: i32) -> bool {
289     app_id < 10000
290 }
291 
is_sk_present() -> bool292 fn is_sk_present() -> bool {
293     matches!(binder::is_declared(SECRETKEEPER_SERVICE), Ok(true))
294 }
295 
296 #[cfg(test)]
297 mod tests {
298     use super::*;
299     use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph;
300     use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper;
301     use authgraph::IAuthGraphKeyExchange::IAuthGraphKeyExchange;
302     use secretkeeper::ISecretkeeper::BnSecretkeeper;
303     use std::sync::{Arc, Mutex};
304     use virtualizationmaintenance::IVirtualizationReconciliationCallback::BnVirtualizationReconciliationCallback;
305 
306     /// Fake implementation of Secretkeeper that keeps a history of what operations were invoked.
307     #[derive(Default)]
308     struct FakeSk {
309         history: Arc<Mutex<Vec<SkOp>>>,
310     }
311 
312     #[derive(Clone, PartialEq, Eq, Debug)]
313     enum SkOp {
314         Management,
315         DeleteIds(Vec<VmId>),
316         DeleteAll,
317     }
318 
319     impl ISecretkeeper for FakeSk {
processSecretManagementRequest(&self, _req: &[u8]) -> binder::Result<Vec<u8>>320         fn processSecretManagementRequest(&self, _req: &[u8]) -> binder::Result<Vec<u8>> {
321             self.history.lock().unwrap().push(SkOp::Management);
322             Ok(vec![])
323         }
324 
getAuthGraphKe(&self) -> binder::Result<binder::Strong<dyn IAuthGraphKeyExchange>>325         fn getAuthGraphKe(&self) -> binder::Result<binder::Strong<dyn IAuthGraphKeyExchange>> {
326             unimplemented!()
327         }
328 
deleteIds(&self, ids: &[SecretId]) -> binder::Result<()>329         fn deleteIds(&self, ids: &[SecretId]) -> binder::Result<()> {
330             self.history.lock().unwrap().push(SkOp::DeleteIds(ids.iter().map(|s| s.id).collect()));
331             Ok(())
332         }
333 
deleteAll(&self) -> binder::Result<()>334         fn deleteAll(&self) -> binder::Result<()> {
335             self.history.lock().unwrap().push(SkOp::DeleteAll);
336             Ok(())
337         }
338     }
339     impl binder::Interface for FakeSk {}
340 
new_test_state(history: Arc<Mutex<Vec<SkOp>>>, batch_size: usize) -> State341     fn new_test_state(history: Arc<Mutex<Vec<SkOp>>>, batch_size: usize) -> State {
342         let vm_id_db = vmdb::new_test_db();
343         let sk = FakeSk { history };
344         let sk = BnSecretkeeper::new_binder(sk, binder::BinderFeatures::default());
345         let inner = InnerState { sk, vm_id_db, batch_size };
346         State { inner: Some(inner) }
347     }
348 
get_db(state: &mut State) -> &mut VmIdDb349     fn get_db(state: &mut State) -> &mut VmIdDb {
350         &mut state.inner.as_mut().unwrap().vm_id_db
351     }
352 
353     struct Reconciliation {
354         gone_users: Vec<i32>,
355         gone_apps: Vec<i32>,
356     }
357 
358     impl IVirtualizationReconciliationCallback for Reconciliation {
doUsersExist(&self, user_ids: &[i32]) -> binder::Result<Vec<bool>>359         fn doUsersExist(&self, user_ids: &[i32]) -> binder::Result<Vec<bool>> {
360             Ok(user_ids.iter().map(|user_id| !self.gone_users.contains(user_id)).collect())
361         }
doAppsExist(&self, _user_id: i32, app_ids: &[i32]) -> binder::Result<Vec<bool>>362         fn doAppsExist(&self, _user_id: i32, app_ids: &[i32]) -> binder::Result<Vec<bool>> {
363             Ok(app_ids.iter().map(|app_id| !self.gone_apps.contains(app_id)).collect())
364         }
365     }
366     impl binder::Interface for Reconciliation {}
367 
368     const VM_ID1: VmId = [1u8; 64];
369     const VM_ID2: VmId = [2u8; 64];
370     const VM_ID3: VmId = [3u8; 64];
371     const VM_ID4: VmId = [4u8; 64];
372     const VM_ID5: VmId = [5u8; 64];
373 
374     const USER1: i32 = 1;
375     const USER2: i32 = 2;
376     const USER3: i32 = 3;
377     const APP_A: i32 = 10050;
378     const APP_B: i32 = 10060;
379     const APP_C: i32 = 10070;
380     const CORE_APP_A: i32 = 45;
381 
382     #[test]
test_sk_state_batching()383     fn test_sk_state_batching() {
384         let history = Arc::new(Mutex::new(Vec::new()));
385         let sk_state = new_test_state(history.clone(), 2);
386         sk_state.inner.unwrap().delete_ids(&[VM_ID1, VM_ID2, VM_ID3, VM_ID4, VM_ID5]);
387         let got = (*history.lock().unwrap()).clone();
388         assert_eq!(
389             got,
390             vec![
391                 SkOp::DeleteIds(vec![VM_ID1, VM_ID2]),
392                 SkOp::DeleteIds(vec![VM_ID3, VM_ID4]),
393                 SkOp::DeleteIds(vec![VM_ID5]),
394             ]
395         );
396     }
397 
398     #[test]
test_sk_state_no_batching()399     fn test_sk_state_no_batching() {
400         let history = Arc::new(Mutex::new(Vec::new()));
401         let sk_state = new_test_state(history.clone(), 6);
402         sk_state.inner.unwrap().delete_ids(&[VM_ID1, VM_ID2, VM_ID3, VM_ID4, VM_ID5]);
403         let got = (*history.lock().unwrap()).clone();
404         assert_eq!(got, vec![SkOp::DeleteIds(vec![VM_ID1, VM_ID2, VM_ID3, VM_ID4, VM_ID5])]);
405     }
406 
407     #[test]
test_sk_state()408     fn test_sk_state() {
409         let history = Arc::new(Mutex::new(Vec::new()));
410         let mut sk_state = new_test_state(history.clone(), 2);
411 
412         get_db(&mut sk_state).add_vm_id(&VM_ID1, USER1, APP_A).unwrap();
413         get_db(&mut sk_state).add_vm_id(&VM_ID2, USER1, APP_A).unwrap();
414         get_db(&mut sk_state).add_vm_id(&VM_ID3, USER2, APP_B).unwrap();
415         get_db(&mut sk_state).add_vm_id(&VM_ID4, USER3, APP_A).unwrap();
416         get_db(&mut sk_state).add_vm_id(&VM_ID5, USER3, APP_C).unwrap();
417         assert_eq!((*history.lock().unwrap()).clone(), vec![]);
418 
419         sk_state.delete_ids_for_app(USER2, APP_B).unwrap();
420         assert_eq!((*history.lock().unwrap()).clone(), vec![SkOp::DeleteIds(vec![VM_ID3])]);
421 
422         sk_state.delete_ids_for_user(USER3).unwrap();
423         assert_eq!(
424             (*history.lock().unwrap()).clone(),
425             vec![SkOp::DeleteIds(vec![VM_ID3]), SkOp::DeleteIds(vec![VM_ID4, VM_ID5]),]
426         );
427 
428         assert_eq!(vec![VM_ID1, VM_ID2], get_db(&mut sk_state).vm_ids_for_user(USER1).unwrap());
429         assert_eq!(
430             vec![VM_ID1, VM_ID2],
431             get_db(&mut sk_state).vm_ids_for_app(USER1, APP_A).unwrap()
432         );
433         let empty: Vec<VmId> = Vec::new();
434         assert_eq!(empty, get_db(&mut sk_state).vm_ids_for_app(USER2, APP_B).unwrap());
435         assert_eq!(empty, get_db(&mut sk_state).vm_ids_for_user(USER3).unwrap());
436     }
437 
438     #[test]
test_sk_state_delete_id()439     fn test_sk_state_delete_id() {
440         let history = Arc::new(Mutex::new(Vec::new()));
441         let mut sk_state = new_test_state(history.clone(), 2);
442 
443         get_db(&mut sk_state).add_vm_id(&VM_ID1, USER1, APP_A).unwrap();
444         get_db(&mut sk_state).add_vm_id(&VM_ID2, USER1, APP_A).unwrap();
445         get_db(&mut sk_state).add_vm_id(&VM_ID3, USER2, APP_B).unwrap();
446         assert_eq!((*history.lock().unwrap()).clone(), vec![]);
447 
448         // A VM ID that doesn't exist anywhere - no delete
449         sk_state.delete_id(&VM_ID4, USER1 as u32, APP_A as u32);
450         assert_eq!((*history.lock().unwrap()).clone(), vec![]);
451 
452         // Wrong app ID - no delete
453         sk_state.delete_id(&VM_ID1, USER1 as u32, APP_B as u32);
454         assert_eq!((*history.lock().unwrap()).clone(), vec![]);
455 
456         // Wrong user ID - no delete
457         sk_state.delete_id(&VM_ID1, USER2 as u32, APP_A as u32);
458         assert_eq!((*history.lock().unwrap()).clone(), vec![]);
459 
460         // This porridge is just right.
461         sk_state.delete_id(&VM_ID1, USER1 as u32, APP_A as u32);
462         assert_eq!((*history.lock().unwrap()).clone(), vec![SkOp::DeleteIds(vec![VM_ID1])]);
463 
464         assert_eq!(vec![VM_ID2], get_db(&mut sk_state).vm_ids_for_user(USER1).unwrap());
465         assert_eq!(vec![VM_ID3], get_db(&mut sk_state).vm_ids_for_user(USER2).unwrap());
466     }
467 
468     #[test]
test_sk_state_reconcile()469     fn test_sk_state_reconcile() {
470         let history = Arc::new(Mutex::new(Vec::new()));
471         let mut sk_state = new_test_state(history.clone(), 20);
472 
473         get_db(&mut sk_state).add_vm_id(&VM_ID1, USER1, APP_A).unwrap();
474         get_db(&mut sk_state).add_vm_id(&VM_ID2, USER1, APP_A).unwrap();
475         get_db(&mut sk_state).add_vm_id(&VM_ID3, USER2, APP_B).unwrap();
476         get_db(&mut sk_state).add_vm_id(&VM_ID4, USER2, CORE_APP_A).unwrap();
477         get_db(&mut sk_state).add_vm_id(&VM_ID5, USER3, APP_C).unwrap();
478 
479         assert_eq!(vec![VM_ID1, VM_ID2], get_db(&mut sk_state).vm_ids_for_user(USER1).unwrap());
480         assert_eq!(
481             vec![VM_ID1, VM_ID2],
482             get_db(&mut sk_state).vm_ids_for_app(USER1, APP_A).unwrap()
483         );
484         assert_eq!(vec![VM_ID3], get_db(&mut sk_state).vm_ids_for_app(USER2, APP_B).unwrap());
485         assert_eq!(vec![VM_ID5], get_db(&mut sk_state).vm_ids_for_user(USER3).unwrap());
486 
487         // Perform a reconciliation and pretend that USER1 and [CORE_APP_A, APP_B] are gone.
488         let reconciliation =
489             Reconciliation { gone_users: vec![USER1], gone_apps: vec![CORE_APP_A, APP_B] };
490         let callback = BnVirtualizationReconciliationCallback::new_binder(
491             reconciliation,
492             binder::BinderFeatures::default(),
493         );
494         sk_state.reconcile(&callback).unwrap();
495 
496         let empty: Vec<VmId> = Vec::new();
497         assert_eq!(empty, get_db(&mut sk_state).vm_ids_for_user(USER1).unwrap());
498         assert_eq!(empty, get_db(&mut sk_state).vm_ids_for_app(USER1, APP_A).unwrap());
499         // VM for core app stays even though it's reported as absent.
500         assert_eq!(vec![VM_ID4], get_db(&mut sk_state).vm_ids_for_user(USER2).unwrap());
501         assert_eq!(empty, get_db(&mut sk_state).vm_ids_for_app(USER2, APP_B).unwrap());
502         assert_eq!(vec![VM_ID5], get_db(&mut sk_state).vm_ids_for_user(USER3).unwrap());
503     }
504 
505     #[test]
test_sk_state_too_many_vms()506     fn test_sk_state_too_many_vms() {
507         let history = Arc::new(Mutex::new(Vec::new()));
508         let mut sk_state = new_test_state(history.clone(), 20);
509 
510         // Every VM ID added up to the limit is kept.
511         for idx in 0..MAX_VM_IDS_PER_APP {
512             let mut vm_id = [0u8; 64];
513             vm_id[0..8].copy_from_slice(&(idx as u64).to_be_bytes());
514             sk_state.add_id(&vm_id, USER1 as u32, APP_A as u32).unwrap();
515             assert_eq!(idx + 1, get_db(&mut sk_state).count_vm_ids_for_app(USER1, APP_A).unwrap());
516         }
517         assert_eq!(
518             MAX_VM_IDS_PER_APP,
519             get_db(&mut sk_state).count_vm_ids_for_app(USER1, APP_A).unwrap()
520         );
521 
522         // Beyond the limit it's one in, one out.
523         for idx in MAX_VM_IDS_PER_APP..MAX_VM_IDS_PER_APP + 10 {
524             let mut vm_id = [0u8; 64];
525             vm_id[0..8].copy_from_slice(&(idx as u64).to_be_bytes());
526             sk_state.add_id(&vm_id, USER1 as u32, APP_A as u32).unwrap();
527             assert_eq!(
528                 MAX_VM_IDS_PER_APP,
529                 get_db(&mut sk_state).count_vm_ids_for_app(USER1, APP_A).unwrap()
530             );
531         }
532         assert_eq!(
533             MAX_VM_IDS_PER_APP,
534             get_db(&mut sk_state).count_vm_ids_for_app(USER1, APP_A).unwrap()
535         );
536     }
537 
538     struct Irreconcilable;
539 
540     impl IVirtualizationReconciliationCallback for Irreconcilable {
doUsersExist(&self, user_ids: &[i32]) -> binder::Result<Vec<bool>>541         fn doUsersExist(&self, user_ids: &[i32]) -> binder::Result<Vec<bool>> {
542             panic!("doUsersExist called with {user_ids:?}");
543         }
doAppsExist(&self, user_id: i32, app_ids: &[i32]) -> binder::Result<Vec<bool>>544         fn doAppsExist(&self, user_id: i32, app_ids: &[i32]) -> binder::Result<Vec<bool>> {
545             panic!("doAppsExist called with {user_id:?}, {app_ids:?}");
546         }
547     }
548     impl binder::Interface for Irreconcilable {}
549 
550     #[test]
test_sk_state_reconcile_not_needed()551     fn test_sk_state_reconcile_not_needed() {
552         let history = Arc::new(Mutex::new(Vec::new()));
553         let mut sk_state = new_test_state(history.clone(), 20);
554 
555         get_db(&mut sk_state).add_vm_id(&VM_ID1, USER1, APP_A).unwrap();
556         get_db(&mut sk_state).add_vm_id(&VM_ID2, USER1, APP_A).unwrap();
557         get_db(&mut sk_state).add_vm_id(&VM_ID3, USER2, APP_B).unwrap();
558         get_db(&mut sk_state).add_vm_id(&VM_ID5, USER3, APP_C).unwrap();
559         sk_state.delete_ids_for_user(USER1).unwrap();
560         sk_state.delete_ids_for_user(USER2).unwrap();
561         sk_state.delete_ids_for_user(USER3).unwrap();
562 
563         // No extant secrets, so reconciliation should not trigger the callback.
564         let callback = BnVirtualizationReconciliationCallback::new_binder(
565             Irreconcilable,
566             binder::BinderFeatures::default(),
567         );
568         sk_state.reconcile(&callback).unwrap();
569     }
570 }
571