Move large test modules into separate files

This complies better with the Android Rust style guide, and makes it
easier to navigate the code.

Test: keystore2_test  libwatchdog_rs.test  librkpd_client.test
Change-Id: Iceb49e309af66ec16d31da66b328936b0312061a
diff --git a/keystore2/src/database/tests.rs b/keystore2/src/database/tests.rs
new file mode 100644
index 0000000..031d749
--- /dev/null
+++ b/keystore2/src/database/tests.rs
@@ -0,0 +1,2528 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Database tests.
+
+use super::*;
+use crate::key_parameter::{
+    Algorithm, BlockMode, Digest, EcCurve, HardwareAuthenticatorType, KeyOrigin, KeyParameter,
+    KeyParameterValue, KeyPurpose, PaddingMode, SecurityLevel,
+};
+use crate::key_perm_set;
+use crate::permission::{KeyPerm, KeyPermSet};
+use crate::super_key::{SuperKeyManager, USER_AFTER_FIRST_UNLOCK_SUPER_KEY, SuperEncryptionAlgorithm, SuperKeyType};
+use keystore2_test_utils::TempDir;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    HardwareAuthToken::HardwareAuthToken,
+    HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
+};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+    Timestamp::Timestamp,
+};
+use std::cell::RefCell;
+use std::collections::BTreeMap;
+use std::fmt::Write;
+use std::sync::atomic::{AtomicU8, Ordering};
+use std::sync::Arc;
+use std::thread;
+use std::time::{Duration, SystemTime};
+use crate::utils::AesGcm;
+#[cfg(disabled)]
+use std::time::Instant;
+
+pub fn new_test_db() -> Result<KeystoreDB> {
+    let conn = KeystoreDB::make_connection("file::memory:")?;
+
+    let mut db = KeystoreDB { conn, gc: None, perboot: Arc::new(perboot::PerbootDB::new()) };
+    db.with_transaction(Immediate("TX_new_test_db"), |tx| {
+        KeystoreDB::init_tables(tx).context("Failed to initialize tables.").no_gc()
+    })?;
+    Ok(db)
+}
+
+fn rebind_alias(
+    db: &mut KeystoreDB,
+    newid: &KeyIdGuard,
+    alias: &str,
+    domain: Domain,
+    namespace: i64,
+) -> Result<bool> {
+    db.with_transaction(Immediate("TX_rebind_alias"), |tx| {
+        KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace, KeyType::Client).no_gc()
+    })
+    .context(ks_err!())
+}
+
+#[test]
+fn datetime() -> Result<()> {
+    let conn = Connection::open_in_memory()?;
+    conn.execute("CREATE TABLE test (ts DATETIME);", [])?;
+    let now = SystemTime::now();
+    let duration = Duration::from_secs(1000);
+    let then = now.checked_sub(duration).unwrap();
+    let soon = now.checked_add(duration).unwrap();
+    conn.execute(
+        "INSERT INTO test (ts) VALUES (?), (?), (?);",
+        params![DateTime::try_from(now)?, DateTime::try_from(then)?, DateTime::try_from(soon)?],
+    )?;
+    let mut stmt = conn.prepare("SELECT ts FROM test ORDER BY ts ASC;")?;
+    let mut rows = stmt.query([])?;
+    assert_eq!(DateTime::try_from(then)?, rows.next()?.unwrap().get(0)?);
+    assert_eq!(DateTime::try_from(now)?, rows.next()?.unwrap().get(0)?);
+    assert_eq!(DateTime::try_from(soon)?, rows.next()?.unwrap().get(0)?);
+    assert!(rows.next()?.is_none());
+    assert!(DateTime::try_from(then)? < DateTime::try_from(now)?);
+    assert!(DateTime::try_from(then)? < DateTime::try_from(soon)?);
+    assert!(DateTime::try_from(now)? < DateTime::try_from(soon)?);
+    Ok(())
+}
+
+// Ensure that we're using the "injected" random function, not the real one.
+#[test]
+fn test_mocked_random() {
+    let rand1 = random();
+    let rand2 = random();
+    let rand3 = random();
+    if rand1 == rand2 {
+        assert_eq!(rand2 + 1, rand3);
+    } else {
+        assert_eq!(rand1 + 1, rand2);
+        assert_eq!(rand2, rand3);
+    }
+}
+
+// Test that we have the correct tables.
+#[test]
+fn test_tables() -> Result<()> {
+    let db = new_test_db()?;
+    let tables = db
+        .conn
+        .prepare("SELECT name from persistent.sqlite_master WHERE type='table' ORDER BY name;")?
+        .query_map(params![], |row| row.get(0))?
+        .collect::<rusqlite::Result<Vec<String>>>()?;
+    assert_eq!(tables.len(), 6);
+    assert_eq!(tables[0], "blobentry");
+    assert_eq!(tables[1], "blobmetadata");
+    assert_eq!(tables[2], "grant");
+    assert_eq!(tables[3], "keyentry");
+    assert_eq!(tables[4], "keymetadata");
+    assert_eq!(tables[5], "keyparameter");
+    Ok(())
+}
+
+#[test]
+fn test_auth_token_table_invariant() -> Result<()> {
+    let mut db = new_test_db()?;
+    let auth_token1 = HardwareAuthToken {
+        challenge: i64::MAX,
+        userId: 200,
+        authenticatorId: 200,
+        authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+        timestamp: Timestamp { milliSeconds: 500 },
+        mac: String::from("mac").into_bytes(),
+    };
+    db.insert_auth_token(&auth_token1);
+    let auth_tokens_returned = get_auth_tokens(&db);
+    assert_eq!(auth_tokens_returned.len(), 1);
+
+    // insert another auth token with the same values for the columns in the UNIQUE constraint
+    // of the auth token table and different value for timestamp
+    let auth_token2 = HardwareAuthToken {
+        challenge: i64::MAX,
+        userId: 200,
+        authenticatorId: 200,
+        authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+        timestamp: Timestamp { milliSeconds: 600 },
+        mac: String::from("mac").into_bytes(),
+    };
+
+    db.insert_auth_token(&auth_token2);
+    let mut auth_tokens_returned = get_auth_tokens(&db);
+    assert_eq!(auth_tokens_returned.len(), 1);
+
+    if let Some(auth_token) = auth_tokens_returned.pop() {
+        assert_eq!(auth_token.auth_token.timestamp.milliSeconds, 600);
+    }
+
+    // insert another auth token with the different values for the columns in the UNIQUE
+    // constraint of the auth token table
+    let auth_token3 = HardwareAuthToken {
+        challenge: i64::MAX,
+        userId: 201,
+        authenticatorId: 200,
+        authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+        timestamp: Timestamp { milliSeconds: 600 },
+        mac: String::from("mac").into_bytes(),
+    };
+
+    db.insert_auth_token(&auth_token3);
+    let auth_tokens_returned = get_auth_tokens(&db);
+    assert_eq!(auth_tokens_returned.len(), 2);
+
+    Ok(())
+}
+
+// utility function for test_auth_token_table_invariant()
+fn get_auth_tokens(db: &KeystoreDB) -> Vec<AuthTokenEntry> {
+    db.perboot.get_all_auth_token_entries()
+}
+
+fn create_key_entry(
+    db: &mut KeystoreDB,
+    domain: &Domain,
+    namespace: &i64,
+    key_type: KeyType,
+    km_uuid: &Uuid,
+) -> Result<KeyIdGuard> {
+    db.with_transaction(Immediate("TX_create_key_entry"), |tx| {
+        KeystoreDB::create_key_entry_internal(tx, domain, namespace, key_type, km_uuid).no_gc()
+    })
+}
+
+#[test]
+fn test_persistence_for_files() -> Result<()> {
+    let temp_dir = TempDir::new("persistent_db_test")?;
+    let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+
+    create_key_entry(&mut db, &Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
+    let entries = get_keyentry(&db)?;
+    assert_eq!(entries.len(), 1);
+
+    let db = KeystoreDB::new(temp_dir.path(), None)?;
+
+    let entries_new = get_keyentry(&db)?;
+    assert_eq!(entries, entries_new);
+    Ok(())
+}
+
+#[test]
+fn test_create_key_entry() -> Result<()> {
+    fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>, Uuid) {
+        (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref(), ke.km_uuid.unwrap())
+    }
+
+    let mut db = new_test_db()?;
+
+    create_key_entry(&mut db, &Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
+    create_key_entry(&mut db, &Domain::SELINUX, &101, KeyType::Client, &KEYSTORE_UUID)?;
+
+    let entries = get_keyentry(&db)?;
+    assert_eq!(entries.len(), 2);
+    assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None, KEYSTORE_UUID));
+    assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None, KEYSTORE_UUID));
+
+    // Test that we must pass in a valid Domain.
+    check_result_is_error_containing_string(
+        create_key_entry(&mut db, &Domain::GRANT, &102, KeyType::Client, &KEYSTORE_UUID),
+        &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
+    );
+    check_result_is_error_containing_string(
+        create_key_entry(&mut db, &Domain::BLOB, &103, KeyType::Client, &KEYSTORE_UUID),
+        &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
+    );
+    check_result_is_error_containing_string(
+        create_key_entry(&mut db, &Domain::KEY_ID, &104, KeyType::Client, &KEYSTORE_UUID),
+        &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
+    );
+
+    Ok(())
+}
+
+#[test]
+fn test_rebind_alias() -> Result<()> {
+    fn extractor(ke: &KeyEntryRow) -> (Option<Domain>, Option<i64>, Option<&str>, Option<Uuid>) {
+        (ke.domain, ke.namespace, ke.alias.as_deref(), ke.km_uuid)
+    }
+
+    let mut db = new_test_db()?;
+    create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+    create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+    let entries = get_keyentry(&db)?;
+    assert_eq!(entries.len(), 2);
+    assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID)));
+    assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID)));
+
+    // Test that the first call to rebind_alias sets the alias.
+    rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[0].id), "foo", Domain::APP, 42)?;
+    let entries = get_keyentry(&db)?;
+    assert_eq!(entries.len(), 2);
+    assert_eq!(
+        extractor(&entries[0]),
+        (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+    );
+    assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID)));
+
+    // Test that the second call to rebind_alias also empties the old one.
+    rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[1].id), "foo", Domain::APP, 42)?;
+    let entries = get_keyentry(&db)?;
+    assert_eq!(entries.len(), 2);
+    assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
+    assert_eq!(
+        extractor(&entries[1]),
+        (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+    );
+
+    // Test that we must pass in a valid Domain.
+    check_result_is_error_containing_string(
+        rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::GRANT, 42),
+        &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
+    );
+    check_result_is_error_containing_string(
+        rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::BLOB, 42),
+        &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
+    );
+    check_result_is_error_containing_string(
+        rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::KEY_ID, 42),
+        &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
+    );
+
+    // Test that we correctly handle setting an alias for something that does not exist.
+    check_result_is_error_containing_string(
+        rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::SELINUX, 42),
+        "Expected to update a single entry but instead updated 0",
+    );
+    // Test that we correctly abort the transaction in this case.
+    let entries = get_keyentry(&db)?;
+    assert_eq!(entries.len(), 2);
+    assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
+    assert_eq!(
+        extractor(&entries[1]),
+        (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+    );
+
+    Ok(())
+}
+
+#[test]
+fn test_grant_ungrant() -> Result<()> {
+    const CALLER_UID: u32 = 15;
+    const GRANTEE_UID: u32 = 12;
+    const SELINUX_NAMESPACE: i64 = 7;
+
+    let mut db = new_test_db()?;
+    db.conn.execute(
+        "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias, state, km_uuid)
+                VALUES (1, 0, 0, 15, 'key', 1, ?), (2, 0, 2, 7, 'yek', 1, ?);",
+        params![KEYSTORE_UUID, KEYSTORE_UUID],
+    )?;
+    let app_key = KeyDescriptor {
+        domain: super::Domain::APP,
+        nspace: 0,
+        alias: Some("key".to_string()),
+        blob: None,
+    };
+    const PVEC1: KeyPermSet = key_perm_set![KeyPerm::Use, KeyPerm::GetInfo];
+    const PVEC2: KeyPermSet = key_perm_set![KeyPerm::Use];
+
+    // Reset totally predictable random number generator in case we
+    // are not the first test running on this thread.
+    reset_random();
+    let next_random = 0i64;
+
+    let app_granted_key = db
+        .grant(&app_key, CALLER_UID, GRANTEE_UID, PVEC1, |k, a| {
+            assert_eq!(*a, PVEC1);
+            assert_eq!(
+                *k,
+                KeyDescriptor {
+                    domain: super::Domain::APP,
+                    // namespace must be set to the caller_uid.
+                    nspace: CALLER_UID as i64,
+                    alias: Some("key".to_string()),
+                    blob: None,
+                }
+            );
+            Ok(())
+        })
+        .unwrap();
+
+    assert_eq!(
+        app_granted_key,
+        KeyDescriptor {
+            domain: super::Domain::GRANT,
+            // The grantid is next_random due to the mock random number generator.
+            nspace: next_random,
+            alias: None,
+            blob: None,
+        }
+    );
+
+    let selinux_key = KeyDescriptor {
+        domain: super::Domain::SELINUX,
+        nspace: SELINUX_NAMESPACE,
+        alias: Some("yek".to_string()),
+        blob: None,
+    };
+
+    let selinux_granted_key = db
+        .grant(&selinux_key, CALLER_UID, 12, PVEC1, |k, a| {
+            assert_eq!(*a, PVEC1);
+            assert_eq!(
+                *k,
+                KeyDescriptor {
+                    domain: super::Domain::SELINUX,
+                    // namespace must be the supplied SELinux
+                    // namespace.
+                    nspace: SELINUX_NAMESPACE,
+                    alias: Some("yek".to_string()),
+                    blob: None,
+                }
+            );
+            Ok(())
+        })
+        .unwrap();
+
+    assert_eq!(
+        selinux_granted_key,
+        KeyDescriptor {
+            domain: super::Domain::GRANT,
+            // The grantid is next_random + 1 due to the mock random number generator.
+            nspace: next_random + 1,
+            alias: None,
+            blob: None,
+        }
+    );
+
+    // This should update the existing grant with PVEC2.
+    let selinux_granted_key = db
+        .grant(&selinux_key, CALLER_UID, 12, PVEC2, |k, a| {
+            assert_eq!(*a, PVEC2);
+            assert_eq!(
+                *k,
+                KeyDescriptor {
+                    domain: super::Domain::SELINUX,
+                    // namespace must be the supplied SELinux
+                    // namespace.
+                    nspace: SELINUX_NAMESPACE,
+                    alias: Some("yek".to_string()),
+                    blob: None,
+                }
+            );
+            Ok(())
+        })
+        .unwrap();
+
+    assert_eq!(
+        selinux_granted_key,
+        KeyDescriptor {
+            domain: super::Domain::GRANT,
+            // Same grant id as before. The entry was only updated.
+            nspace: next_random + 1,
+            alias: None,
+            blob: None,
+        }
+    );
+
+    {
+        // Limiting scope of stmt, because it borrows db.
+        let mut stmt = db
+            .conn
+            .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
+        let mut rows = stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>([], |row| {
+            Ok((row.get(0)?, row.get(1)?, row.get(2)?, KeyPermSet::from(row.get::<_, i32>(3)?)))
+        })?;
+
+        let r = rows.next().unwrap().unwrap();
+        assert_eq!(r, (next_random, GRANTEE_UID, 1, PVEC1));
+        let r = rows.next().unwrap().unwrap();
+        assert_eq!(r, (next_random + 1, GRANTEE_UID, 2, PVEC2));
+        assert!(rows.next().is_none());
+    }
+
+    debug_dump_keyentry_table(&mut db)?;
+    println!("app_key {:?}", app_key);
+    println!("selinux_key {:?}", selinux_key);
+
+    db.ungrant(&app_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
+    db.ungrant(&selinux_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
+
+    Ok(())
+}
+
+static TEST_KEY_BLOB: &[u8] = b"my test blob";
+static TEST_CERT_BLOB: &[u8] = b"my test cert";
+static TEST_CERT_CHAIN_BLOB: &[u8] = b"my test cert_chain";
+
+#[test]
+fn test_set_blob() -> Result<()> {
+    let key_id = KEY_ID_LOCK.get(3000);
+    let mut db = new_test_db()?;
+    let mut blob_metadata = BlobMetaData::new();
+    blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+    db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+    db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+    db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+    drop(key_id);
+
+    let mut stmt = db.conn.prepare(
+        "SELECT subcomponent_type, keyentryid, blob, id FROM persistent.blobentry
+                ORDER BY subcomponent_type ASC;",
+    )?;
+    let mut rows = stmt.query_map::<((SubComponentType, i64, Vec<u8>), i64), _, _>([], |row| {
+        Ok(((row.get(0)?, row.get(1)?, row.get(2)?), row.get(3)?))
+    })?;
+    let (r, id) = rows.next().unwrap().unwrap();
+    assert_eq!(r, (SubComponentType::KEY_BLOB, 3000, TEST_KEY_BLOB.to_vec()));
+    let (r, _) = rows.next().unwrap().unwrap();
+    assert_eq!(r, (SubComponentType::CERT, 3000, TEST_CERT_BLOB.to_vec()));
+    let (r, _) = rows.next().unwrap().unwrap();
+    assert_eq!(r, (SubComponentType::CERT_CHAIN, 3000, TEST_CERT_CHAIN_BLOB.to_vec()));
+
+    drop(rows);
+    drop(stmt);
+
+    assert_eq!(
+        db.with_transaction(Immediate("TX_test"), |tx| {
+            BlobMetaData::load_from_db(id, tx).no_gc()
+        })
+        .expect("Should find blob metadata."),
+        blob_metadata
+    );
+    Ok(())
+}
+
+static TEST_ALIAS: &str = "my super duper key";
+
+#[test]
+fn test_insert_and_load_full_keyentry_domain_app() -> Result<()> {
+    let mut db = new_test_db()?;
+    let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
+        .context("test_insert_and_load_full_keyentry_domain_app")?
+        .0;
+    let (_key_guard, key_entry) = db
+        .load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 0,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            1,
+            |_k, _av| Ok(()),
+        )
+        .unwrap();
+    assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+    db.unbind_key(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: 0,
+            alias: Some(TEST_ALIAS.to_string()),
+            blob: None,
+        },
+        KeyType::Client,
+        1,
+        |_, _| Ok(()),
+    )
+    .unwrap();
+
+    assert_eq!(
+        Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+        db.load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 0,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::NONE,
+            1,
+            |_k, _av| Ok(()),
+        )
+        .unwrap_err()
+        .root_cause()
+        .downcast_ref::<KsError>()
+    );
+
+    Ok(())
+}
+
+#[test]
+fn test_insert_and_load_certificate_entry_domain_app() -> Result<()> {
+    let mut db = new_test_db()?;
+
+    db.store_new_certificate(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: 1,
+            alias: Some(TEST_ALIAS.to_string()),
+            blob: None,
+        },
+        KeyType::Client,
+        TEST_CERT_BLOB,
+        &KEYSTORE_UUID,
+    )
+    .expect("Trying to insert cert.");
+
+    let (_key_guard, mut key_entry) = db
+        .load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 1,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::PUBLIC,
+            1,
+            |_k, _av| Ok(()),
+        )
+        .expect("Trying to read certificate entry.");
+
+    assert!(key_entry.pure_cert());
+    assert!(key_entry.cert().is_none());
+    assert_eq!(key_entry.take_cert_chain(), Some(TEST_CERT_BLOB.to_vec()));
+
+    db.unbind_key(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: 1,
+            alias: Some(TEST_ALIAS.to_string()),
+            blob: None,
+        },
+        KeyType::Client,
+        1,
+        |_, _| Ok(()),
+    )
+    .unwrap();
+
+    assert_eq!(
+        Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+        db.load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 1,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::NONE,
+            1,
+            |_k, _av| Ok(()),
+        )
+        .unwrap_err()
+        .root_cause()
+        .downcast_ref::<KsError>()
+    );
+
+    Ok(())
+}
+
+#[test]
+fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> {
+    let mut db = new_test_db()?;
+    let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
+        .context("test_insert_and_load_full_keyentry_domain_selinux")?
+        .0;
+    let (_key_guard, key_entry) = db
+        .load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::SELINUX,
+                nspace: 1,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            1,
+            |_k, _av| Ok(()),
+        )
+        .unwrap();
+    assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+    db.unbind_key(
+        &KeyDescriptor {
+            domain: Domain::SELINUX,
+            nspace: 1,
+            alias: Some(TEST_ALIAS.to_string()),
+            blob: None,
+        },
+        KeyType::Client,
+        1,
+        |_, _| Ok(()),
+    )
+    .unwrap();
+
+    assert_eq!(
+        Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+        db.load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::SELINUX,
+                nspace: 1,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::NONE,
+            1,
+            |_k, _av| Ok(()),
+        )
+        .unwrap_err()
+        .root_cause()
+        .downcast_ref::<KsError>()
+    );
+
+    Ok(())
+}
+
+#[test]
+fn test_insert_and_load_full_keyentry_domain_key_id() -> Result<()> {
+    let mut db = new_test_db()?;
+    let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
+        .context("test_insert_and_load_full_keyentry_domain_key_id")?
+        .0;
+    let (_, key_entry) = db
+        .load_key_entry(
+            &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            1,
+            |_k, _av| Ok(()),
+        )
+        .unwrap();
+
+    assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+    db.unbind_key(
+        &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+        KeyType::Client,
+        1,
+        |_, _| Ok(()),
+    )
+    .unwrap();
+
+    assert_eq!(
+        Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+        db.load_key_entry(
+            &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+            KeyType::Client,
+            KeyEntryLoadBits::NONE,
+            1,
+            |_k, _av| Ok(()),
+        )
+        .unwrap_err()
+        .root_cause()
+        .downcast_ref::<KsError>()
+    );
+
+    Ok(())
+}
+
+#[test]
+fn test_check_and_update_key_usage_count_with_limited_use_key() -> Result<()> {
+    let mut db = new_test_db()?;
+    let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(123))
+        .context("test_check_and_update_key_usage_count_with_limited_use_key")?
+        .0;
+    // Update the usage count of the limited use key.
+    db.check_and_update_key_usage_count(key_id)?;
+
+    let (_key_guard, key_entry) = db.load_key_entry(
+        &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+        KeyType::Client,
+        KeyEntryLoadBits::BOTH,
+        1,
+        |_k, _av| Ok(()),
+    )?;
+
+    // The usage count is decremented now.
+    assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, Some(122)));
+
+    Ok(())
+}
+
+#[test]
+fn test_check_and_update_key_usage_count_with_exhausted_limited_use_key() -> Result<()> {
+    let mut db = new_test_db()?;
+    let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(1))
+        .context("test_check_and_update_key_usage_count_with_exhausted_limited_use_key")?
+        .0;
+    // Update the usage count of the limited use key.
+    db.check_and_update_key_usage_count(key_id).expect(concat!(
+        "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
+        "This should succeed."
+    ));
+
+    // Try to update the exhausted limited use key.
+    let e = db.check_and_update_key_usage_count(key_id).expect_err(concat!(
+        "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
+        "This should fail."
+    ));
+    assert_eq!(
+        &KsError::Km(ErrorCode::INVALID_KEY_BLOB),
+        e.root_cause().downcast_ref::<KsError>().unwrap()
+    );
+
+    Ok(())
+}
+
+#[test]
+fn test_insert_and_load_full_keyentry_from_grant() -> Result<()> {
+    let mut db = new_test_db()?;
+    let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
+        .context("test_insert_and_load_full_keyentry_from_grant")?
+        .0;
+
+    let granted_key = db
+        .grant(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 0,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            1,
+            2,
+            key_perm_set![KeyPerm::Use],
+            |_k, _av| Ok(()),
+        )
+        .unwrap();
+
+    debug_dump_grant_table(&mut db)?;
+
+    let (_key_guard, key_entry) = db
+        .load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::BOTH, 2, |k, av| {
+            assert_eq!(Domain::GRANT, k.domain);
+            assert!(av.unwrap().includes(KeyPerm::Use));
+            Ok(())
+        })
+        .unwrap();
+
+    assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+    db.unbind_key(&granted_key, KeyType::Client, 2, |_, _| Ok(())).unwrap();
+
+    assert_eq!(
+        Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+        db.load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::NONE, 2, |_k, _av| Ok(
+            ()
+        ),)
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+    );
+
+    Ok(())
+}
+
+// This test attempts to load a key by key id while the caller is not the owner
+// but a grant exists for the given key and the caller.
+#[test]
+fn test_insert_and_load_full_keyentry_from_grant_by_key_id() -> Result<()> {
+    let mut db = new_test_db()?;
+    const OWNER_UID: u32 = 1u32;
+    const GRANTEE_UID: u32 = 2u32;
+    const SOMEONE_ELSE_UID: u32 = 3u32;
+    let key_id = make_test_key_entry(&mut db, Domain::APP, OWNER_UID as i64, TEST_ALIAS, None)
+        .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?
+        .0;
+
+    db.grant(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: 0,
+            alias: Some(TEST_ALIAS.to_string()),
+            blob: None,
+        },
+        OWNER_UID,
+        GRANTEE_UID,
+        key_perm_set![KeyPerm::Use],
+        |_k, _av| Ok(()),
+    )
+    .unwrap();
+
+    debug_dump_grant_table(&mut db)?;
+
+    let id_descriptor =
+        KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, ..Default::default() };
+
+    let (_, key_entry) = db
+        .load_key_entry(
+            &id_descriptor,
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            GRANTEE_UID,
+            |k, av| {
+                assert_eq!(Domain::APP, k.domain);
+                assert_eq!(OWNER_UID as i64, k.nspace);
+                assert!(av.unwrap().includes(KeyPerm::Use));
+                Ok(())
+            },
+        )
+        .unwrap();
+
+    assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+    let (_, key_entry) = db
+        .load_key_entry(
+            &id_descriptor,
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            SOMEONE_ELSE_UID,
+            |k, av| {
+                assert_eq!(Domain::APP, k.domain);
+                assert_eq!(OWNER_UID as i64, k.nspace);
+                assert!(av.is_none());
+                Ok(())
+            },
+        )
+        .unwrap();
+
+    assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+    db.unbind_key(&id_descriptor, KeyType::Client, OWNER_UID, |_, _| Ok(())).unwrap();
+
+    assert_eq!(
+        Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+        db.load_key_entry(
+            &id_descriptor,
+            KeyType::Client,
+            KeyEntryLoadBits::NONE,
+            GRANTEE_UID,
+            |_k, _av| Ok(()),
+        )
+        .unwrap_err()
+        .root_cause()
+        .downcast_ref::<KsError>()
+    );
+
+    Ok(())
+}
+
+// Creates a key migrates it to a different location and then tries to access it by the old
+// and new location.
+#[test]
+fn test_migrate_key_app_to_app() -> Result<()> {
+    let mut db = new_test_db()?;
+    const SOURCE_UID: u32 = 1u32;
+    const DESTINATION_UID: u32 = 2u32;
+    static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+    static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+    let key_id_guard =
+        make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+            .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+    let source_descriptor: KeyDescriptor = KeyDescriptor {
+        domain: Domain::APP,
+        nspace: -1,
+        alias: Some(SOURCE_ALIAS.to_string()),
+        blob: None,
+    };
+
+    let destination_descriptor: KeyDescriptor = KeyDescriptor {
+        domain: Domain::APP,
+        nspace: -1,
+        alias: Some(DESTINATION_ALIAS.to_string()),
+        blob: None,
+    };
+
+    let key_id = key_id_guard.id();
+
+    db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| Ok(()))
+        .unwrap();
+
+    let (_, key_entry) = db
+        .load_key_entry(
+            &destination_descriptor,
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            DESTINATION_UID,
+            |k, av| {
+                assert_eq!(Domain::APP, k.domain);
+                assert_eq!(DESTINATION_UID as i64, k.nspace);
+                assert!(av.is_none());
+                Ok(())
+            },
+        )
+        .unwrap();
+
+    assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+    assert_eq!(
+        Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+        db.load_key_entry(
+            &source_descriptor,
+            KeyType::Client,
+            KeyEntryLoadBits::NONE,
+            SOURCE_UID,
+            |_k, _av| Ok(()),
+        )
+        .unwrap_err()
+        .root_cause()
+        .downcast_ref::<KsError>()
+    );
+
+    Ok(())
+}
+
+// Creates a key migrates it to a different location and then tries to access it by the old
+// and new location.
+#[test]
+fn test_migrate_key_app_to_selinux() -> Result<()> {
+    let mut db = new_test_db()?;
+    const SOURCE_UID: u32 = 1u32;
+    const DESTINATION_UID: u32 = 2u32;
+    const DESTINATION_NAMESPACE: i64 = 1000i64;
+    static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+    static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+    let key_id_guard =
+        make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+            .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+    let source_descriptor: KeyDescriptor = KeyDescriptor {
+        domain: Domain::APP,
+        nspace: -1,
+        alias: Some(SOURCE_ALIAS.to_string()),
+        blob: None,
+    };
+
+    let destination_descriptor: KeyDescriptor = KeyDescriptor {
+        domain: Domain::SELINUX,
+        nspace: DESTINATION_NAMESPACE,
+        alias: Some(DESTINATION_ALIAS.to_string()),
+        blob: None,
+    };
+
+    let key_id = key_id_guard.id();
+
+    db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| Ok(()))
+        .unwrap();
+
+    let (_, key_entry) = db
+        .load_key_entry(
+            &destination_descriptor,
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            DESTINATION_UID,
+            |k, av| {
+                assert_eq!(Domain::SELINUX, k.domain);
+                assert_eq!(DESTINATION_NAMESPACE, k.nspace);
+                assert!(av.is_none());
+                Ok(())
+            },
+        )
+        .unwrap();
+
+    assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+    assert_eq!(
+        Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+        db.load_key_entry(
+            &source_descriptor,
+            KeyType::Client,
+            KeyEntryLoadBits::NONE,
+            SOURCE_UID,
+            |_k, _av| Ok(()),
+        )
+        .unwrap_err()
+        .root_cause()
+        .downcast_ref::<KsError>()
+    );
+
+    Ok(())
+}
+
+// Creates two keys and tries to migrate the first to the location of the second which
+// is expected to fail.
+#[test]
+fn test_migrate_key_destination_occupied() -> Result<()> {
+    let mut db = new_test_db()?;
+    const SOURCE_UID: u32 = 1u32;
+    const DESTINATION_UID: u32 = 2u32;
+    static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+    static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+    let key_id_guard =
+        make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+            .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+    make_test_key_entry(&mut db, Domain::APP, DESTINATION_UID as i64, DESTINATION_ALIAS, None)
+        .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+    let destination_descriptor: KeyDescriptor = KeyDescriptor {
+        domain: Domain::APP,
+        nspace: -1,
+        alias: Some(DESTINATION_ALIAS.to_string()),
+        blob: None,
+    };
+
+    assert_eq!(
+        Some(&KsError::Rc(ResponseCode::INVALID_ARGUMENT)),
+        db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| Ok(
+            ()
+        ))
+        .unwrap_err()
+        .root_cause()
+        .downcast_ref::<KsError>()
+    );
+
+    Ok(())
+}
+
+#[test]
+fn test_upgrade_0_to_1() {
+    const ALIAS1: &str = "test_upgrade_0_to_1_1";
+    const ALIAS2: &str = "test_upgrade_0_to_1_2";
+    const ALIAS3: &str = "test_upgrade_0_to_1_3";
+    const UID: u32 = 33;
+    let temp_dir = Arc::new(TempDir::new("test_upgrade_0_to_1").unwrap());
+    let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
+    let key_id_untouched1 =
+        make_test_key_entry(&mut db, Domain::APP, UID as i64, ALIAS1, None).unwrap().id();
+    let key_id_untouched2 =
+        make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS2, false).unwrap().id();
+    let key_id_deleted =
+        make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS3, true).unwrap().id();
+
+    let (_, key_entry) = db
+        .load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(ALIAS1.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            UID,
+            |k, av| {
+                assert_eq!(Domain::APP, k.domain);
+                assert_eq!(UID as i64, k.nspace);
+                assert!(av.is_none());
+                Ok(())
+            },
+        )
+        .unwrap();
+    assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
+    let (_, key_entry) = db
+        .load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(ALIAS2.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            UID,
+            |k, av| {
+                assert_eq!(Domain::APP, k.domain);
+                assert_eq!(UID as i64, k.nspace);
+                assert!(av.is_none());
+                Ok(())
+            },
+        )
+        .unwrap();
+    assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
+    let (_, key_entry) = db
+        .load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(ALIAS3.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            UID,
+            |k, av| {
+                assert_eq!(Domain::APP, k.domain);
+                assert_eq!(UID as i64, k.nspace);
+                assert!(av.is_none());
+                Ok(())
+            },
+        )
+        .unwrap();
+    assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_deleted, true));
+
+    db.with_transaction(Immediate("TX_test"), |tx| KeystoreDB::from_0_to_1(tx).no_gc()).unwrap();
+
+    let (_, key_entry) = db
+        .load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(ALIAS1.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            UID,
+            |k, av| {
+                assert_eq!(Domain::APP, k.domain);
+                assert_eq!(UID as i64, k.nspace);
+                assert!(av.is_none());
+                Ok(())
+            },
+        )
+        .unwrap();
+    assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
+    let (_, key_entry) = db
+        .load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(ALIAS2.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            UID,
+            |k, av| {
+                assert_eq!(Domain::APP, k.domain);
+                assert_eq!(UID as i64, k.nspace);
+                assert!(av.is_none());
+                Ok(())
+            },
+        )
+        .unwrap();
+    assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
+    assert_eq!(
+        Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+        db.load_key_entry(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(ALIAS3.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            UID,
+            |k, av| {
+                assert_eq!(Domain::APP, k.domain);
+                assert_eq!(UID as i64, k.nspace);
+                assert!(av.is_none());
+                Ok(())
+            },
+        )
+        .unwrap_err()
+        .root_cause()
+        .downcast_ref::<KsError>()
+    );
+}
+
+static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
+
+#[test]
+fn test_insert_and_load_full_keyentry_domain_app_concurrently() -> Result<()> {
+    let handle = {
+        let temp_dir = Arc::new(TempDir::new("id_lock_test")?);
+        let temp_dir_clone = temp_dir.clone();
+        let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+        let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS, None)
+            .context("test_insert_and_load_full_keyentry_domain_app")?
+            .0;
+        let (_key_guard, key_entry) = db
+            .load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: 0,
+                    alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                33,
+                |_k, _av| Ok(()),
+            )
+            .unwrap();
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+        let state = Arc::new(AtomicU8::new(1));
+        let state2 = state.clone();
+
+        // Spawning a second thread that attempts to acquire the key id lock
+        // for the same key as the primary thread. The primary thread then
+        // waits, thereby forcing the secondary thread into the second stage
+        // of acquiring the lock (see KEY ID LOCK 2/2 above).
+        // The test succeeds if the secondary thread observes the transition
+        // of `state` from 1 to 2, despite having a whole second to overtake
+        // the primary thread.
+        let handle = thread::spawn(move || {
+            let temp_dir = temp_dir_clone;
+            let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
+            assert!(db
+                .load_key_entry(
+                    &KeyDescriptor {
+                        domain: Domain::APP,
+                        nspace: 0,
+                        alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
+                        blob: None,
+                    },
+                    KeyType::Client,
+                    KeyEntryLoadBits::BOTH,
+                    33,
+                    |_k, _av| Ok(()),
+                )
+                .is_ok());
+            // We should only see a 2 here because we can only return
+            // from load_key_entry when the `_key_guard` expires,
+            // which happens at the end of the scope.
+            assert_eq!(2, state2.load(Ordering::Relaxed));
+        });
+
+        thread::sleep(std::time::Duration::from_millis(1000));
+
+        assert_eq!(Ok(1), state.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed));
+
+        // Return the handle from this scope so we can join with the
+        // secondary thread after the key id lock has expired.
+        handle
+        // This is where the `_key_guard` goes out of scope,
+        // which is the reason for concurrent load_key_entry on the same key
+        // to unblock.
+    };
+    // Join with the secondary thread and unwrap, to propagate failing asserts to the
+    // main test thread. We will not see failing asserts in secondary threads otherwise.
+    handle.join().unwrap();
+    Ok(())
+}
+
+#[test]
+fn test_database_busy_error_code() {
+    let temp_dir =
+        TempDir::new("test_database_busy_error_code_").expect("Failed to create temp dir.");
+
+    let mut db1 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database1.");
+    let mut db2 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database2.");
+
+    let _tx1 = db1
+        .conn
+        .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)
+        .expect("Failed to create first transaction.");
+
+    let error = db2
+        .conn
+        .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)
+        .context("Transaction begin failed.")
+        .expect_err("This should fail.");
+    let root_cause = error.root_cause();
+    if let Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. }) =
+        root_cause.downcast_ref::<rusqlite::ffi::Error>()
+    {
+        return;
+    }
+    panic!(
+        "Unexpected error {:?} \n{:?} \n{:?}",
+        error,
+        root_cause,
+        root_cause.downcast_ref::<rusqlite::ffi::Error>()
+    )
+}
+
+#[cfg(disabled)]
+#[test]
+fn test_large_number_of_concurrent_db_manipulations() -> Result<()> {
+    let temp_dir = Arc::new(
+        TempDir::new("test_large_number_of_concurrent_db_manipulations_")
+            .expect("Failed to create temp dir."),
+    );
+
+    let test_begin = Instant::now();
+
+    const KEY_COUNT: u32 = 500u32;
+    let mut db =
+        new_test_db_with_gc(temp_dir.path(), |_, _| Ok(())).expect("Failed to open database.");
+    const OPEN_DB_COUNT: u32 = 50u32;
+
+    let mut actual_key_count = KEY_COUNT;
+    // First insert KEY_COUNT keys.
+    for count in 0..KEY_COUNT {
+        if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
+            actual_key_count = count;
+            break;
+        }
+        let alias = format!("test_alias_{}", count);
+        make_test_key_entry(&mut db, Domain::APP, 1, &alias, None)
+            .expect("Failed to make key entry.");
+    }
+
+    // Insert more keys from a different thread and into a different namespace.
+    let temp_dir1 = temp_dir.clone();
+    let handle1 = thread::spawn(move || {
+        let mut db =
+            new_test_db_with_gc(temp_dir1.path(), |_, _| Ok(())).expect("Failed to open database.");
+
+        for count in 0..actual_key_count {
+            if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                return;
+            }
+            let alias = format!("test_alias_{}", count);
+            make_test_key_entry(&mut db, Domain::APP, 2, &alias, None)
+                .expect("Failed to make key entry.");
+        }
+
+        // then unbind them again.
+        for count in 0..actual_key_count {
+            if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                return;
+            }
+            let key = KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(format!("test_alias_{}", count)),
+                blob: None,
+            };
+            db.unbind_key(&key, KeyType::Client, 2, |_, _| Ok(())).expect("Unbind Failed.");
+        }
+    });
+
+    // And start unbinding the first set of keys.
+    let temp_dir2 = temp_dir.clone();
+    let handle2 = thread::spawn(move || {
+        let mut db =
+            new_test_db_with_gc(temp_dir2.path(), |_, _| Ok(())).expect("Failed to open database.");
+
+        for count in 0..actual_key_count {
+            if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                return;
+            }
+            let key = KeyDescriptor {
+                domain: Domain::APP,
+                nspace: -1,
+                alias: Some(format!("test_alias_{}", count)),
+                blob: None,
+            };
+            db.unbind_key(&key, KeyType::Client, 1, |_, _| Ok(())).expect("Unbind Failed.");
+        }
+    });
+
+    // While a lot of inserting and deleting is going on we have to open database connections
+    // successfully and use them.
+    // This clone is not redundant, because temp_dir needs to be kept alive until db goes
+    // out of scope.
+    #[allow(clippy::redundant_clone)]
+    let temp_dir4 = temp_dir.clone();
+    let handle4 = thread::spawn(move || {
+        for count in 0..OPEN_DB_COUNT {
+            if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                return;
+            }
+            let mut db = new_test_db_with_gc(temp_dir4.path(), |_, _| Ok(()))
+                .expect("Failed to open database.");
+
+            let alias = format!("test_alias_{}", count);
+            make_test_key_entry(&mut db, Domain::APP, 3, &alias, None)
+                .expect("Failed to make key entry.");
+            let key =
+                KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None };
+            db.unbind_key(&key, KeyType::Client, 3, |_, _| Ok(())).expect("Unbind Failed.");
+        }
+    });
+
+    handle1.join().expect("Thread 1 panicked.");
+    handle2.join().expect("Thread 2 panicked.");
+    handle4.join().expect("Thread 4 panicked.");
+
+    Ok(())
+}
+
+#[test]
+fn list() -> Result<()> {
+    let temp_dir = TempDir::new("list_test")?;
+    let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+    static LIST_O_ENTRIES: &[(Domain, i64, &str)] = &[
+        (Domain::APP, 1, "test1"),
+        (Domain::APP, 1, "test2"),
+        (Domain::APP, 1, "test3"),
+        (Domain::APP, 1, "test4"),
+        (Domain::APP, 1, "test5"),
+        (Domain::APP, 1, "test6"),
+        (Domain::APP, 1, "test7"),
+        (Domain::APP, 2, "test1"),
+        (Domain::APP, 2, "test2"),
+        (Domain::APP, 2, "test3"),
+        (Domain::APP, 2, "test4"),
+        (Domain::APP, 2, "test5"),
+        (Domain::APP, 2, "test6"),
+        (Domain::APP, 2, "test8"),
+        (Domain::SELINUX, 100, "test1"),
+        (Domain::SELINUX, 100, "test2"),
+        (Domain::SELINUX, 100, "test3"),
+        (Domain::SELINUX, 100, "test4"),
+        (Domain::SELINUX, 100, "test5"),
+        (Domain::SELINUX, 100, "test6"),
+        (Domain::SELINUX, 100, "test9"),
+    ];
+
+    let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES
+        .iter()
+        .map(|(domain, ns, alias)| {
+            let entry =
+                make_test_key_entry(&mut db, *domain, *ns, alias, None).unwrap_or_else(|e| {
+                    panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e)
+                });
+            (entry.id(), *ns)
+        })
+        .collect();
+
+    for (domain, namespace) in
+        &[(Domain::APP, 1i64), (Domain::APP, 2i64), (Domain::SELINUX, 100i64)]
+    {
+        let mut list_o_descriptors: Vec<KeyDescriptor> = LIST_O_ENTRIES
+            .iter()
+            .filter_map(|(domain, ns, alias)| match ns {
+                ns if *ns == *namespace => Some(KeyDescriptor {
+                    domain: *domain,
+                    nspace: *ns,
+                    alias: Some(alias.to_string()),
+                    blob: None,
+                }),
+                _ => None,
+            })
+            .collect();
+        list_o_descriptors.sort();
+        let mut list_result = db.list_past_alias(*domain, *namespace, KeyType::Client, None)?;
+        list_result.sort();
+        assert_eq!(list_o_descriptors, list_result);
+
+        let mut list_o_ids: Vec<i64> = list_o_descriptors
+            .into_iter()
+            .map(|d| {
+                let (_, entry) = db
+                    .load_key_entry(
+                        &d,
+                        KeyType::Client,
+                        KeyEntryLoadBits::NONE,
+                        *namespace as u32,
+                        |_, _| Ok(()),
+                    )
+                    .unwrap();
+                entry.id()
+            })
+            .collect();
+        list_o_ids.sort_unstable();
+        let mut loaded_entries: Vec<i64> = list_o_keys
+            .iter()
+            .filter_map(|(id, ns)| match ns {
+                ns if *ns == *namespace => Some(*id),
+                _ => None,
+            })
+            .collect();
+        loaded_entries.sort_unstable();
+        assert_eq!(list_o_ids, loaded_entries);
+    }
+    assert_eq!(
+        Vec::<KeyDescriptor>::new(),
+        db.list_past_alias(Domain::SELINUX, 101, KeyType::Client, None)?
+    );
+
+    Ok(())
+}
+
+// Helpers
+
+// Checks that the given result is an error containing the given string.
+fn check_result_is_error_containing_string<T>(result: Result<T>, target: &str) {
+    let error_str =
+        format!("{:#?}", result.err().unwrap_or_else(|| panic!("Expected the error: {}", target)));
+    assert!(
+        error_str.contains(target),
+        "The string \"{}\" should contain \"{}\"",
+        error_str,
+        target
+    );
+}
+
+#[derive(Debug, PartialEq)]
+struct KeyEntryRow {
+    id: i64,
+    key_type: KeyType,
+    domain: Option<Domain>,
+    namespace: Option<i64>,
+    alias: Option<String>,
+    state: KeyLifeCycle,
+    km_uuid: Option<Uuid>,
+}
+
+fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
+    db.conn
+        .prepare("SELECT * FROM persistent.keyentry;")?
+        .query_map([], |row| {
+            Ok(KeyEntryRow {
+                id: row.get(0)?,
+                key_type: row.get(1)?,
+                domain: row.get::<_, Option<_>>(2)?.map(Domain),
+                namespace: row.get(3)?,
+                alias: row.get(4)?,
+                state: row.get(5)?,
+                km_uuid: row.get(6)?,
+            })
+        })?
+        .map(|r| r.context("Could not read keyentry row."))
+        .collect::<Result<Vec<_>>>()
+}
+
+fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
+    make_test_params_with_sids(max_usage_count, &[42])
+}
+
+// Note: The parameters and SecurityLevel associations are nonsensical. This
+// collection is only used to check if the parameters are preserved as expected by the
+// database.
+fn make_test_params_with_sids(
+    max_usage_count: Option<i32>,
+    user_secure_ids: &[i64],
+) -> Vec<KeyParameter> {
+    let mut params = vec![
+        KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT),
+        KeyParameter::new(
+            KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::Algorithm(Algorithm::RSA),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::TRUSTED_ENVIRONMENT),
+        KeyParameter::new(
+            KeyParameterValue::BlockMode(BlockMode::ECB),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::BlockMode(BlockMode::GCM),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::Digest(Digest::NONE), SecurityLevel::STRONGBOX),
+        KeyParameter::new(
+            KeyParameterValue::Digest(Digest::MD5),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::Digest(Digest::SHA_2_224),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::Digest(Digest::SHA_2_256), SecurityLevel::STRONGBOX),
+        KeyParameter::new(
+            KeyParameterValue::PaddingMode(PaddingMode::NONE),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::PaddingMode(PaddingMode::RSA_OAEP),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS),
+            SecurityLevel::STRONGBOX,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::PaddingMode(PaddingMode::RSA_PKCS1_1_5_SIGN),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::TRUSTED_ENVIRONMENT),
+        KeyParameter::new(KeyParameterValue::MinMacLength(256), SecurityLevel::STRONGBOX),
+        KeyParameter::new(
+            KeyParameterValue::EcCurve(EcCurve::P_224),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::EcCurve(EcCurve::P_256), SecurityLevel::STRONGBOX),
+        KeyParameter::new(
+            KeyParameterValue::EcCurve(EcCurve::P_384),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::EcCurve(EcCurve::P_521),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::RSAPublicExponent(3),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::IncludeUniqueID, SecurityLevel::TRUSTED_ENVIRONMENT),
+        KeyParameter::new(KeyParameterValue::BootLoaderOnly, SecurityLevel::STRONGBOX),
+        KeyParameter::new(KeyParameterValue::RollbackResistance, SecurityLevel::STRONGBOX),
+        KeyParameter::new(KeyParameterValue::ActiveDateTime(1234567890), SecurityLevel::STRONGBOX),
+        KeyParameter::new(
+            KeyParameterValue::OriginationExpireDateTime(1234567890),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::UsageExpireDateTime(1234567890),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::MinSecondsBetweenOps(1234567890),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::MaxUsesPerBoot(1234567890),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX),
+        KeyParameter::new(KeyParameterValue::NoAuthRequired, SecurityLevel::TRUSTED_ENVIRONMENT),
+        KeyParameter::new(
+            KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::PASSWORD),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::AuthTimeout(1234567890), SecurityLevel::SOFTWARE),
+        KeyParameter::new(KeyParameterValue::AllowWhileOnBody, SecurityLevel::SOFTWARE),
+        KeyParameter::new(
+            KeyParameterValue::TrustedUserPresenceRequired,
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::TrustedConfirmationRequired,
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::UnlockedDeviceRequired,
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::ApplicationID(vec![1u8, 2u8, 3u8, 4u8]),
+            SecurityLevel::SOFTWARE,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::ApplicationData(vec![4u8, 3u8, 2u8, 1u8]),
+            SecurityLevel::SOFTWARE,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::CreationDateTime(12345677890),
+            SecurityLevel::SOFTWARE,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::RootOfTrust(vec![3u8, 2u8, 1u8, 4u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::OSVersion(1), SecurityLevel::TRUSTED_ENVIRONMENT),
+        KeyParameter::new(KeyParameterValue::OSPatchLevel(2), SecurityLevel::SOFTWARE),
+        KeyParameter::new(
+            KeyParameterValue::UniqueID(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::SOFTWARE,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::AttestationChallenge(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::AttestationApplicationID(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::AttestationIdBrand(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::AttestationIdDevice(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::AttestationIdProduct(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::AttestationIdSerial(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::AttestationIdIMEI(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::AttestationIdSecondIMEI(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::AttestationIdMEID(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::AttestationIdManufacturer(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::AttestationIdModel(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::VendorPatchLevel(3),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::BootPatchLevel(4), SecurityLevel::TRUSTED_ENVIRONMENT),
+        KeyParameter::new(
+            KeyParameterValue::AssociatedData(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::Nonce(vec![4u8, 3u8, 1u8, 2u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(KeyParameterValue::MacLength(256), SecurityLevel::TRUSTED_ENVIRONMENT),
+        KeyParameter::new(
+            KeyParameterValue::ResetSinceIdRotation,
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+        KeyParameter::new(
+            KeyParameterValue::ConfirmationToken(vec![5u8, 5u8, 5u8, 5u8]),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ),
+    ];
+    if let Some(value) = max_usage_count {
+        params.push(KeyParameter::new(
+            KeyParameterValue::UsageCountLimit(value),
+            SecurityLevel::SOFTWARE,
+        ));
+    }
+
+    for sid in user_secure_ids.iter() {
+        params.push(KeyParameter::new(
+            KeyParameterValue::UserSecureID(*sid),
+            SecurityLevel::STRONGBOX,
+        ));
+    }
+    params
+}
+
+pub fn make_test_key_entry(
+    db: &mut KeystoreDB,
+    domain: Domain,
+    namespace: i64,
+    alias: &str,
+    max_usage_count: Option<i32>,
+) -> Result<KeyIdGuard> {
+    make_test_key_entry_with_sids(db, domain, namespace, alias, max_usage_count, &[42])
+}
+
+pub fn make_test_key_entry_with_sids(
+    db: &mut KeystoreDB,
+    domain: Domain,
+    namespace: i64,
+    alias: &str,
+    max_usage_count: Option<i32>,
+    sids: &[i64],
+) -> Result<KeyIdGuard> {
+    let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+    let mut blob_metadata = BlobMetaData::new();
+    blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+    blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
+    blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
+    blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
+    blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+    db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+    db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+    db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+
+    let params = make_test_params_with_sids(max_usage_count, sids);
+    db.insert_keyparameter(&key_id, &params)?;
+
+    let mut metadata = KeyMetaData::new();
+    metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+    db.insert_key_metadata(&key_id, &metadata)?;
+    rebind_alias(db, &key_id, alias, domain, namespace)?;
+    Ok(key_id)
+}
+
+fn make_test_key_entry_test_vector(key_id: i64, max_usage_count: Option<i32>) -> KeyEntry {
+    let params = make_test_params(max_usage_count);
+
+    let mut blob_metadata = BlobMetaData::new();
+    blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+    blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
+    blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
+    blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
+    blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+    let mut metadata = KeyMetaData::new();
+    metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+
+    KeyEntry {
+        id: key_id,
+        key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
+        cert: Some(TEST_CERT_BLOB.to_vec()),
+        cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+        km_uuid: KEYSTORE_UUID,
+        parameters: params,
+        metadata,
+        pure_cert: false,
+    }
+}
+
+pub fn make_bootlevel_key_entry(
+    db: &mut KeystoreDB,
+    domain: Domain,
+    namespace: i64,
+    alias: &str,
+    logical_only: bool,
+) -> Result<KeyIdGuard> {
+    let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+    let mut blob_metadata = BlobMetaData::new();
+    if !logical_only {
+        blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
+    }
+    blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+    db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+    db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+    db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+
+    let mut params = make_test_params(None);
+    params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
+
+    db.insert_keyparameter(&key_id, &params)?;
+
+    let mut metadata = KeyMetaData::new();
+    metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+    db.insert_key_metadata(&key_id, &metadata)?;
+    rebind_alias(db, &key_id, alias, domain, namespace)?;
+    Ok(key_id)
+}
+
+// Creates an app key that is marked as being superencrypted by the given
+// super key ID and that has the given authentication and unlocked device
+// parameters. This does not actually superencrypt the key blob.
+fn make_superencrypted_key_entry(
+    db: &mut KeystoreDB,
+    namespace: i64,
+    alias: &str,
+    requires_authentication: bool,
+    requires_unlocked_device: bool,
+    super_key_id: i64,
+) -> Result<KeyIdGuard> {
+    let domain = Domain::APP;
+    let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+
+    let mut blob_metadata = BlobMetaData::new();
+    blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+    blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
+    db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+
+    let mut params = vec![];
+    if requires_unlocked_device {
+        params.push(KeyParameter::new(
+            KeyParameterValue::UnlockedDeviceRequired,
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ));
+    }
+    if requires_authentication {
+        params.push(KeyParameter::new(
+            KeyParameterValue::UserSecureID(42),
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+        ));
+    }
+    db.insert_keyparameter(&key_id, &params)?;
+
+    let mut metadata = KeyMetaData::new();
+    metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+    db.insert_key_metadata(&key_id, &metadata)?;
+
+    rebind_alias(db, &key_id, alias, domain, namespace)?;
+    Ok(key_id)
+}
+
+fn make_bootlevel_test_key_entry_test_vector(key_id: i64, logical_only: bool) -> KeyEntry {
+    let mut params = make_test_params(None);
+    params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
+
+    let mut blob_metadata = BlobMetaData::new();
+    if !logical_only {
+        blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
+    }
+    blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+    let mut metadata = KeyMetaData::new();
+    metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+
+    KeyEntry {
+        id: key_id,
+        key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
+        cert: Some(TEST_CERT_BLOB.to_vec()),
+        cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+        km_uuid: KEYSTORE_UUID,
+        parameters: params,
+        metadata,
+        pure_cert: false,
+    }
+}
+
+fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
+    let mut stmt = db.conn.prepare(
+        "SELECT id, key_type, domain, namespace, alias, state, km_uuid FROM persistent.keyentry;",
+    )?;
+    let rows =
+        stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle, Uuid), _, _>([], |row| {
+            Ok((
+                row.get(0)?,
+                row.get(1)?,
+                row.get(2)?,
+                row.get(3)?,
+                row.get(4)?,
+                row.get(5)?,
+                row.get(6)?,
+            ))
+        })?;
+
+    println!("Key entry table rows:");
+    for r in rows {
+        let (id, key_type, domain, namespace, alias, state, km_uuid) = r.unwrap();
+        println!(
+            "    id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?} KmUuid: {:?}",
+            id, key_type, domain, namespace, alias, state, km_uuid
+        );
+    }
+    Ok(())
+}
+
+fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> {
+    let mut stmt =
+        db.conn.prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
+    let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>([], |row| {
+        Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
+    })?;
+
+    println!("Grant table rows:");
+    for r in rows {
+        let (id, gt, ki, av) = r.unwrap();
+        println!("    id: {} grantee: {} key_id: {} access_vector: {}", id, gt, ki, av);
+    }
+    Ok(())
+}
+
+// Use a custom random number generator that repeats each number once.
+// This allows us to test repeated elements.
+
+thread_local! {
+    static RANDOM_COUNTER: RefCell<i64> = const { RefCell::new(0) };
+}
+
+fn reset_random() {
+    RANDOM_COUNTER.with(|counter| {
+        *counter.borrow_mut() = 0;
+    })
+}
+
+pub fn random() -> i64 {
+    RANDOM_COUNTER.with(|counter| {
+        let result = *counter.borrow() / 2;
+        *counter.borrow_mut() += 1;
+        result
+    })
+}
+
+#[test]
+fn test_unbind_keys_for_user() -> Result<()> {
+    let mut db = new_test_db()?;
+    db.unbind_keys_for_user(1)?;
+
+    make_test_key_entry(&mut db, Domain::APP, 210000, TEST_ALIAS, None)?;
+    make_test_key_entry(&mut db, Domain::APP, 110000, TEST_ALIAS, None)?;
+    db.unbind_keys_for_user(2)?;
+
+    assert_eq!(1, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
+    assert_eq!(0, db.list_past_alias(Domain::APP, 210000, KeyType::Client, None)?.len());
+
+    db.unbind_keys_for_user(1)?;
+    assert_eq!(0, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
+
+    Ok(())
+}
+
+#[test]
+fn test_unbind_keys_for_user_removes_superkeys() -> Result<()> {
+    let mut db = new_test_db()?;
+    let super_key = keystore2_crypto::generate_aes256_key()?;
+    let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+    let (encrypted_super_key, metadata) = SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+
+    let key_name_enc = SuperKeyType {
+        alias: "test_super_key_1",
+        algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+        name: "test_super_key_1",
+    };
+
+    let key_name_nonenc = SuperKeyType {
+        alias: "test_super_key_2",
+        algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+        name: "test_super_key_2",
+    };
+
+    // Install two super keys.
+    db.store_super_key(1, &key_name_nonenc, &super_key, &BlobMetaData::new(), &KeyMetaData::new())?;
+    db.store_super_key(1, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
+
+    // Check that both can be found in the database.
+    assert!(db.load_super_key(&key_name_enc, 1)?.is_some());
+    assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
+
+    // Install the same keys for a different user.
+    db.store_super_key(2, &key_name_nonenc, &super_key, &BlobMetaData::new(), &KeyMetaData::new())?;
+    db.store_super_key(2, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
+
+    // Check that the second pair of keys can be found in the database.
+    assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
+    assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
+
+    // Delete all keys for user 1.
+    db.unbind_keys_for_user(1)?;
+
+    // All of user 1's keys should be gone.
+    assert!(db.load_super_key(&key_name_enc, 1)?.is_none());
+    assert!(db.load_super_key(&key_name_nonenc, 1)?.is_none());
+
+    // User 2's keys should not have been touched.
+    assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
+    assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
+
+    Ok(())
+}
+
+fn app_key_exists(db: &mut KeystoreDB, nspace: i64, alias: &str) -> Result<bool> {
+    db.key_exists(Domain::APP, nspace, alias, KeyType::Client)
+}
+
+// Tests the unbind_auth_bound_keys_for_user() function.
+#[test]
+fn test_unbind_auth_bound_keys_for_user() -> Result<()> {
+    let mut db = new_test_db()?;
+    let user_id = 1;
+    let nspace: i64 = (user_id * AID_USER_OFFSET).into();
+    let other_user_id = 2;
+    let other_user_nspace: i64 = (other_user_id * AID_USER_OFFSET).into();
+    let super_key_type = &USER_AFTER_FIRST_UNLOCK_SUPER_KEY;
+
+    // Create a superencryption key.
+    let super_key = keystore2_crypto::generate_aes256_key()?;
+    let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+    let (encrypted_super_key, blob_metadata) =
+        SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+    db.store_super_key(
+        user_id,
+        super_key_type,
+        &encrypted_super_key,
+        &blob_metadata,
+        &KeyMetaData::new(),
+    )?;
+    let super_key_id = db.load_super_key(super_key_type, user_id)?.unwrap().0 .0;
+
+    // Store 4 superencrypted app keys, one for each possible combination of
+    // (authentication required, unlocked device required).
+    make_superencrypted_key_entry(&mut db, nspace, "noauth_noud", false, false, super_key_id)?;
+    make_superencrypted_key_entry(&mut db, nspace, "noauth_ud", false, true, super_key_id)?;
+    make_superencrypted_key_entry(&mut db, nspace, "auth_noud", true, false, super_key_id)?;
+    make_superencrypted_key_entry(&mut db, nspace, "auth_ud", true, true, super_key_id)?;
+    assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
+    assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
+    assert!(app_key_exists(&mut db, nspace, "auth_noud")?);
+    assert!(app_key_exists(&mut db, nspace, "auth_ud")?);
+
+    // Also store a key for a different user that requires authentication.
+    make_superencrypted_key_entry(&mut db, other_user_nspace, "auth_ud", true, true, super_key_id)?;
+
+    db.unbind_auth_bound_keys_for_user(user_id)?;
+
+    // Verify that only the user's app keys that require authentication were
+    // deleted. Keys that require an unlocked device but not authentication
+    // should *not* have been deleted, nor should the super key have been
+    // deleted, nor should other users' keys have been deleted.
+    assert!(db.load_super_key(super_key_type, user_id)?.is_some());
+    assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
+    assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
+    assert!(!app_key_exists(&mut db, nspace, "auth_noud")?);
+    assert!(!app_key_exists(&mut db, nspace, "auth_ud")?);
+    assert!(app_key_exists(&mut db, other_user_nspace, "auth_ud")?);
+
+    Ok(())
+}
+
+#[test]
+fn test_store_super_key() -> Result<()> {
+    let mut db = new_test_db()?;
+    let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+    let super_key = keystore2_crypto::generate_aes256_key()?;
+    let secret_bytes = b"keystore2 is great.";
+    let (encrypted_secret, iv, tag) = keystore2_crypto::aes_gcm_encrypt(secret_bytes, &super_key)?;
+
+    let (encrypted_super_key, metadata) = SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+    db.store_super_key(
+        1,
+        &USER_AFTER_FIRST_UNLOCK_SUPER_KEY,
+        &encrypted_super_key,
+        &metadata,
+        &KeyMetaData::new(),
+    )?;
+
+    // Check if super key exists.
+    assert!(db.key_exists(
+        Domain::APP,
+        1,
+        USER_AFTER_FIRST_UNLOCK_SUPER_KEY.alias,
+        KeyType::Super
+    )?);
+
+    let (_, key_entry) = db.load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, 1)?.unwrap();
+    let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
+        USER_AFTER_FIRST_UNLOCK_SUPER_KEY.algorithm,
+        key_entry,
+        &pw,
+        None,
+    )?;
+
+    let decrypted_secret_bytes = loaded_super_key.decrypt(&encrypted_secret, &iv, &tag)?;
+    assert_eq!(secret_bytes, &*decrypted_secret_bytes);
+
+    Ok(())
+}
+
+fn get_valid_statsd_storage_types() -> Vec<MetricsStorage> {
+    vec![
+        MetricsStorage::KEY_ENTRY,
+        MetricsStorage::KEY_ENTRY_ID_INDEX,
+        MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
+        MetricsStorage::BLOB_ENTRY,
+        MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
+        MetricsStorage::KEY_PARAMETER,
+        MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX,
+        MetricsStorage::KEY_METADATA,
+        MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX,
+        MetricsStorage::GRANT,
+        MetricsStorage::AUTH_TOKEN,
+        MetricsStorage::BLOB_METADATA,
+        MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
+    ]
+}
+
+/// Perform a simple check to ensure that we can query all the storage types
+/// that are supported by the DB. Check for reasonable values.
+#[test]
+fn test_query_all_valid_table_sizes() -> Result<()> {
+    const PAGE_SIZE: i32 = 4096;
+
+    let mut db = new_test_db()?;
+
+    for t in get_valid_statsd_storage_types() {
+        let stat = db.get_storage_stat(t)?;
+        // AuthToken can be less than a page since it's in a btree, not sqlite
+        // TODO(b/187474736) stop using if-let here
+        if let MetricsStorage::AUTH_TOKEN = t {
+        } else {
+            assert!(stat.size >= PAGE_SIZE);
+        }
+        assert!(stat.size >= stat.unused_size);
+    }
+
+    Ok(())
+}
+
+fn get_storage_stats_map(db: &mut KeystoreDB) -> BTreeMap<i32, StorageStats> {
+    get_valid_statsd_storage_types()
+        .into_iter()
+        .map(|t| (t.0, db.get_storage_stat(t).unwrap()))
+        .collect()
+}
+
+fn assert_storage_increased(
+    db: &mut KeystoreDB,
+    increased_storage_types: Vec<MetricsStorage>,
+    baseline: &mut BTreeMap<i32, StorageStats>,
+) {
+    for storage in increased_storage_types {
+        // Verify the expected storage increased.
+        let new = db.get_storage_stat(storage).unwrap();
+        let old = &baseline[&storage.0];
+        assert!(new.size >= old.size, "{}: {} >= {}", storage.0, new.size, old.size);
+        assert!(
+            new.unused_size <= old.unused_size,
+            "{}: {} <= {}",
+            storage.0,
+            new.unused_size,
+            old.unused_size
+        );
+
+        // Update the baseline with the new value so that it succeeds in the
+        // later comparison.
+        baseline.insert(storage.0, new);
+    }
+
+    // Get an updated map of the storage and verify there were no unexpected changes.
+    let updated_stats = get_storage_stats_map(db);
+    assert_eq!(updated_stats.len(), baseline.len());
+
+    for &k in baseline.keys() {
+        let stringify = |map: &BTreeMap<i32, StorageStats>| -> String {
+            let mut s = String::new();
+            for &k in map.keys() {
+                writeln!(&mut s, "  {}: {}, {}", &k, map[&k].size, map[&k].unused_size)
+                    .expect("string concat failed");
+            }
+            s
+        };
+
+        assert!(
+            updated_stats[&k].size == baseline[&k].size
+                && updated_stats[&k].unused_size == baseline[&k].unused_size,
+            "updated_stats:\n{}\nbaseline:\n{}",
+            stringify(&updated_stats),
+            stringify(baseline)
+        );
+    }
+}
+
+#[test]
+fn test_verify_key_table_size_reporting() -> Result<()> {
+    let mut db = new_test_db()?;
+    let mut working_stats = get_storage_stats_map(&mut db);
+
+    let key_id = create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+    assert_storage_increased(
+        &mut db,
+        vec![
+            MetricsStorage::KEY_ENTRY,
+            MetricsStorage::KEY_ENTRY_ID_INDEX,
+            MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
+        ],
+        &mut working_stats,
+    );
+
+    let mut blob_metadata = BlobMetaData::new();
+    blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+    db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), None)?;
+    assert_storage_increased(
+        &mut db,
+        vec![
+            MetricsStorage::BLOB_ENTRY,
+            MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
+            MetricsStorage::BLOB_METADATA,
+            MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
+        ],
+        &mut working_stats,
+    );
+
+    let params = make_test_params(None);
+    db.insert_keyparameter(&key_id, &params)?;
+    assert_storage_increased(
+        &mut db,
+        vec![MetricsStorage::KEY_PARAMETER, MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX],
+        &mut working_stats,
+    );
+
+    let mut metadata = KeyMetaData::new();
+    metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+    db.insert_key_metadata(&key_id, &metadata)?;
+    assert_storage_increased(
+        &mut db,
+        vec![MetricsStorage::KEY_METADATA, MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX],
+        &mut working_stats,
+    );
+
+    let mut sum = 0;
+    for stat in working_stats.values() {
+        sum += stat.size;
+    }
+    let total = db.get_storage_stat(MetricsStorage::DATABASE)?.size;
+    assert!(sum <= total, "Expected sum <= total. sum: {}, total: {}", sum, total);
+
+    Ok(())
+}
+
+#[test]
+fn test_verify_auth_table_size_reporting() -> Result<()> {
+    let mut db = new_test_db()?;
+    let mut working_stats = get_storage_stats_map(&mut db);
+    db.insert_auth_token(&HardwareAuthToken {
+        challenge: 123,
+        userId: 456,
+        authenticatorId: 789,
+        authenticatorType: kmhw_authenticator_type::ANY,
+        timestamp: Timestamp { milliSeconds: 10 },
+        mac: b"mac".to_vec(),
+    });
+    assert_storage_increased(&mut db, vec![MetricsStorage::AUTH_TOKEN], &mut working_stats);
+    Ok(())
+}
+
+#[test]
+fn test_verify_grant_table_size_reporting() -> Result<()> {
+    const OWNER: i64 = 1;
+    let mut db = new_test_db()?;
+    make_test_key_entry(&mut db, Domain::APP, OWNER, TEST_ALIAS, None)?;
+
+    let mut working_stats = get_storage_stats_map(&mut db);
+    db.grant(
+        &KeyDescriptor {
+            domain: Domain::APP,
+            nspace: 0,
+            alias: Some(TEST_ALIAS.to_string()),
+            blob: None,
+        },
+        OWNER as u32,
+        123,
+        key_perm_set![KeyPerm::Use],
+        |_, _| Ok(()),
+    )?;
+
+    assert_storage_increased(&mut db, vec![MetricsStorage::GRANT], &mut working_stats);
+
+    Ok(())
+}
+
+#[test]
+fn find_auth_token_entry_returns_latest() -> Result<()> {
+    let mut db = new_test_db()?;
+    db.insert_auth_token(&HardwareAuthToken {
+        challenge: 123,
+        userId: 456,
+        authenticatorId: 789,
+        authenticatorType: kmhw_authenticator_type::ANY,
+        timestamp: Timestamp { milliSeconds: 10 },
+        mac: b"mac0".to_vec(),
+    });
+    std::thread::sleep(std::time::Duration::from_millis(1));
+    db.insert_auth_token(&HardwareAuthToken {
+        challenge: 123,
+        userId: 457,
+        authenticatorId: 789,
+        authenticatorType: kmhw_authenticator_type::ANY,
+        timestamp: Timestamp { milliSeconds: 12 },
+        mac: b"mac1".to_vec(),
+    });
+    std::thread::sleep(std::time::Duration::from_millis(1));
+    db.insert_auth_token(&HardwareAuthToken {
+        challenge: 123,
+        userId: 458,
+        authenticatorId: 789,
+        authenticatorType: kmhw_authenticator_type::ANY,
+        timestamp: Timestamp { milliSeconds: 3 },
+        mac: b"mac2".to_vec(),
+    });
+    // All three entries are in the database
+    assert_eq!(db.perboot.auth_tokens_len(), 3);
+    // It selected the most recent timestamp
+    assert_eq!(db.find_auth_token_entry(|_| true).unwrap().auth_token.mac, b"mac2".to_vec());
+    Ok(())
+}
+
+#[test]
+fn test_load_key_descriptor() -> Result<()> {
+    let mut db = new_test_db()?;
+    let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)?.0;
+
+    let key = db.load_key_descriptor(key_id)?.unwrap();
+
+    assert_eq!(key.domain, Domain::APP);
+    assert_eq!(key.nspace, 1);
+    assert_eq!(key.alias, Some(TEST_ALIAS.to_string()));
+
+    // No such id
+    assert_eq!(db.load_key_descriptor(key_id + 1)?, None);
+    Ok(())
+}
+
+#[test]
+fn test_get_list_app_uids_for_sid() -> Result<()> {
+    let uid: i32 = 1;
+    let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
+    let first_sid = 667;
+    let second_sid = 669;
+    let first_app_id: i64 = 123 + uid_offset;
+    let second_app_id: i64 = 456 + uid_offset;
+    let third_app_id: i64 = 789 + uid_offset;
+    let unrelated_app_id: i64 = 1011 + uid_offset;
+    let mut db = new_test_db()?;
+    make_test_key_entry_with_sids(
+        &mut db,
+        Domain::APP,
+        first_app_id,
+        TEST_ALIAS,
+        None,
+        &[first_sid],
+    )
+    .context("test_get_list_app_uids_for_sid")?;
+    make_test_key_entry_with_sids(
+        &mut db,
+        Domain::APP,
+        second_app_id,
+        "alias2",
+        None,
+        &[first_sid],
+    )
+    .context("test_get_list_app_uids_for_sid")?;
+    make_test_key_entry_with_sids(
+        &mut db,
+        Domain::APP,
+        second_app_id,
+        TEST_ALIAS,
+        None,
+        &[second_sid],
+    )
+    .context("test_get_list_app_uids_for_sid")?;
+    make_test_key_entry_with_sids(
+        &mut db,
+        Domain::APP,
+        third_app_id,
+        "alias3",
+        None,
+        &[second_sid],
+    )
+    .context("test_get_list_app_uids_for_sid")?;
+    make_test_key_entry_with_sids(&mut db, Domain::APP, unrelated_app_id, TEST_ALIAS, None, &[])
+        .context("test_get_list_app_uids_for_sid")?;
+
+    let mut first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
+    first_sid_apps.sort();
+    assert_eq!(first_sid_apps, vec![first_app_id, second_app_id]);
+    let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
+    second_sid_apps.sort();
+    assert_eq!(second_sid_apps, vec![second_app_id, third_app_id]);
+    Ok(())
+}
+
+#[test]
+fn test_get_list_app_uids_with_multiple_sids() -> Result<()> {
+    let uid: i32 = 1;
+    let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
+    let first_sid = 667;
+    let second_sid = 669;
+    let third_sid = 772;
+    let first_app_id: i64 = 123 + uid_offset;
+    let second_app_id: i64 = 456 + uid_offset;
+    let mut db = new_test_db()?;
+    make_test_key_entry_with_sids(
+        &mut db,
+        Domain::APP,
+        first_app_id,
+        TEST_ALIAS,
+        None,
+        &[first_sid, second_sid],
+    )
+    .context("test_get_list_app_uids_for_sid")?;
+    make_test_key_entry_with_sids(
+        &mut db,
+        Domain::APP,
+        second_app_id,
+        "alias2",
+        None,
+        &[second_sid, third_sid],
+    )
+    .context("test_get_list_app_uids_for_sid")?;
+
+    let first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
+    assert_eq!(first_sid_apps, vec![first_app_id]);
+
+    let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
+    second_sid_apps.sort();
+    assert_eq!(second_sid_apps, vec![first_app_id, second_app_id]);
+
+    let third_sid_apps = db.get_app_uids_affected_by_sid(uid, third_sid)?;
+    assert_eq!(third_sid_apps, vec![second_app_id]);
+    Ok(())
+}