Merge "keystore2: Statically link Rust libs to save RAM"
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 6386201..0ba49ed 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -62,6 +62,9 @@
     shared_libs: [
         "libcutils",
     ],
+    features: [
+        "watchdog",
+    ],
 }
 
 rust_library {
@@ -91,6 +94,10 @@
         "libkeystore2_test_utils",
         "libnix",
     ],
+    // The test should always include watchdog.
+    features: [
+        "watchdog",
+    ],
 }
 
 rust_binary {
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
index 848b770..0096686 100644
--- a/keystore2/src/apc.rs
+++ b/keystore2/src/apc.rs
@@ -21,7 +21,7 @@
     sync::{mpsc::Sender, Arc, Mutex},
 };
 
-use crate::utils::{compat_2_response_code, ui_opts_2_compat};
+use crate::utils::{compat_2_response_code, ui_opts_2_compat, watchdog as wd};
 use android_security_apc::aidl::android::security::apc::{
     IConfirmationCallback::IConfirmationCallback,
     IProtectedConfirmation::{BnProtectedConfirmation, IProtectedConfirmation},
@@ -363,6 +363,8 @@
         locale: &str,
         ui_option_flags: i32,
     ) -> BinderResult<()> {
+        // presentPrompt can take more time than other operations.
+        let _wp = wd::watch_millis("IProtectedConfirmation::presentPrompt", 3000);
         map_or_log_err(
             self.present_prompt(listener, prompt_text, extra_data, locale, ui_option_flags),
             Ok,
@@ -372,9 +374,11 @@
         &self,
         listener: &binder::Strong<dyn IConfirmationCallback>,
     ) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IProtectedConfirmation::cancelPrompt", 500);
         map_or_log_err(self.cancel_prompt(listener), Ok)
     }
     fn isSupported(&self) -> BinderResult<bool> {
+        let _wp = wd::watch_millis("IProtectedConfirmation::isSupported", 500);
         map_or_log_err(Self::is_supported(), Ok)
     }
 }
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index cac75c0..d07dab5 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -18,7 +18,7 @@
 use crate::globals::{ENFORCEMENTS, SUPER_KEY, DB, LEGACY_MIGRATOR};
 use crate::permission::KeystorePerm;
 use crate::super_key::UserState;
-use crate::utils::check_keystore_permission;
+use crate::utils::{check_keystore_permission, watchdog as wd};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     HardwareAuthToken::HardwareAuthToken,
 };
@@ -234,6 +234,7 @@
 
 impl IKeystoreAuthorization for AuthorizationManager {
     fn addAuthToken(&self, auth_token: &HardwareAuthToken) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreAuthorization::addAuthToken", 500);
         map_or_log_err(self.add_auth_token(auth_token), Ok)
     }
 
@@ -244,6 +245,10 @@
         password: Option<&[u8]>,
         unlocking_sids: Option<&[i64]>,
     ) -> BinderResult<()> {
+        let _wp =
+            wd::watch_millis_with("IKeystoreAuthorization::onLockScreenEvent", 500, move || {
+                format!("lock event: {}", lock_screen_event.0)
+            });
         map_or_log_err(
             self.on_lock_screen_event(
                 lock_screen_event,
@@ -261,6 +266,7 @@
         secure_user_id: i64,
         auth_token_max_age_millis: i64,
     ) -> binder::public_api::Result<AuthorizationTokens> {
+        let _wp = wd::watch_millis("IKeystoreAuthorization::getAuthTokensForCredStore", 500);
         map_or_log_err(
             self.get_auth_tokens_for_credstore(
                 challenge,
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 32e2c98..e62ec4e 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -46,7 +46,7 @@
 use crate::impl_metadata; // This is in db_utils.rs
 use crate::key_parameter::{KeyParameter, Tag};
 use crate::permission::KeyPermSet;
-use crate::utils::{get_current_time_in_seconds, AID_USER_OFFSET};
+use crate::utils::{get_current_time_in_seconds, watchdog as wd, AID_USER_OFFSET};
 use crate::{
     db_utils::{self, SqlField},
     gc::Gc,
@@ -851,6 +851,8 @@
     /// KeystoreDB cannot be used by multiple threads.
     /// Each thread should open their own connection using `thread_local!`.
     pub fn new(db_root: &Path, gc: Option<Gc>) -> Result<Self> {
+        let _wp = wd::watch_millis("KeystoreDB::new", 500);
+
         // Build the path to the sqlite file.
         let mut persistent_path = db_root.to_path_buf();
         persistent_path.push(Self::PERSISTENT_DB_FILENAME);
@@ -1096,6 +1098,8 @@
         &mut self,
         storage_type: StatsdStorageType,
     ) -> Result<Keystore2StorageStats> {
+        let _wp = wd::watch_millis("KeystoreDB::get_storage_stat", 500);
+
         match storage_type {
             StatsdStorageType::Database => self.get_total_size(),
             StatsdStorageType::KeyEntry => {
@@ -1151,6 +1155,7 @@
         &mut self,
         blob_id_to_delete: Option<i64>,
     ) -> Result<Option<(i64, Vec<u8>, BlobMetaData)>> {
+        let _wp = wd::watch_millis("KeystoreDB::handle_next_superseded_blob", 500);
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             // Delete the given blob if one was given.
             if let Some(blob_id_to_delete) = blob_id_to_delete {
@@ -1220,6 +1225,8 @@
     /// Unlike with `mark_unreferenced`, we don't need to purge grants, because only keys that made
     /// it to `KeyLifeCycle::Live` may have grants.
     pub fn cleanup_leftovers(&mut self) -> Result<usize> {
+        let _wp = wd::watch_millis("KeystoreDB::cleanup_leftovers", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             tx.execute(
                 "UPDATE persistent.keyentry SET state = ? WHERE state = ?;",
@@ -1239,6 +1246,8 @@
         alias: &str,
         key_type: KeyType,
     ) -> Result<bool> {
+        let _wp = wd::watch_millis("KeystoreDB::key_exists", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let key_descriptor =
                 KeyDescriptor { domain, nspace, alias: Some(alias.to_string()), blob: None };
@@ -1264,6 +1273,8 @@
         blob_metadata: &BlobMetaData,
         key_metadata: &KeyMetaData,
     ) -> Result<KeyEntry> {
+        let _wp = wd::watch_millis("KeystoreDB::store_super_key", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let key_id = Self::insert_with_retry(|id| {
                 tx.execute(
@@ -1307,6 +1318,8 @@
         key_type: &SuperKeyType,
         user_id: u32,
     ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+        let _wp = wd::watch_millis("KeystoreDB::load_super_key", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let key_descriptor = KeyDescriptor {
                 domain: Domain::APP,
@@ -1346,6 +1359,8 @@
     where
         F: Fn() -> Result<(Vec<u8>, BlobMetaData)>,
     {
+        let _wp = wd::watch_millis("KeystoreDB::get_or_create_key_with", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let id = {
                 let mut stmt = tx
@@ -1494,6 +1509,8 @@
         namespace: &i64,
         km_uuid: &Uuid,
     ) -> Result<KeyIdGuard> {
+        let _wp = wd::watch_millis("KeystoreDB::create_key_entry", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             Self::create_key_entry_internal(tx, domain, namespace, km_uuid).no_gc()
         })
@@ -1545,6 +1562,8 @@
         private_key: &[u8],
         km_uuid: &Uuid,
     ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::create_attestation_key_entry", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let key_id = KEY_ID_LOCK.get(
                 Self::insert_with_retry(|id| {
@@ -1587,6 +1606,8 @@
         blob: Option<&[u8]>,
         blob_metadata: Option<&BlobMetaData>,
     ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::set_blob", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             Self::set_blob_internal(&tx, key_id.0, sc_type, blob, blob_metadata).need_gc()
         })
@@ -1598,6 +1619,8 @@
     /// We use this to insert key blobs into the database which can then be garbage collected
     /// lazily by the key garbage collector.
     pub fn set_deleted_blob(&mut self, blob: &[u8], blob_metadata: &BlobMetaData) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::set_deleted_blob", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             Self::set_blob_internal(
                 &tx,
@@ -1708,6 +1731,8 @@
         expiration_date: i64,
         km_uuid: &Uuid,
     ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::store_signed_attestation_certificate_chain", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let mut stmt = tx
                 .prepare(
@@ -1777,6 +1802,8 @@
         namespace: i64,
         km_uuid: &Uuid,
     ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::assign_attestation_key", 500);
+
         match domain {
             Domain::APP | Domain::SELINUX => {}
             _ => {
@@ -1839,6 +1866,8 @@
         num_keys: i32,
         km_uuid: &Uuid,
     ) -> Result<Vec<Vec<u8>>> {
+        let _wp = wd::watch_millis("KeystoreDB::fetch_unsigned_attestation_keys", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let mut stmt = tx
                 .prepare(
@@ -1876,6 +1905,8 @@
     /// Removes any keys that have expired as of the current time. Returns the number of keys
     /// marked unreferenced that are bound to be garbage collected.
     pub fn delete_expired_attestation_keys(&mut self) -> Result<i32> {
+        let _wp = wd::watch_millis("KeystoreDB::delete_expired_attestation_keys", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let mut stmt = tx
                 .prepare(
@@ -1911,6 +1942,8 @@
     /// Deletes all remotely provisioned attestation keys in the system, regardless of the state
     /// they are in. This is useful primarily as a testing mechanism.
     pub fn delete_all_attestation_keys(&mut self) -> Result<i64> {
+        let _wp = wd::watch_millis("KeystoreDB::delete_all_attestation_keys", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let mut stmt = tx
                 .prepare(
@@ -1942,6 +1975,8 @@
         date: i64,
         km_uuid: &Uuid,
     ) -> Result<AttestationPoolStatus> {
+        let _wp = wd::watch_millis("KeystoreDB::get_attestation_pool_status", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let mut stmt = tx.prepare(
                 "SELECT data
@@ -2009,6 +2044,8 @@
         namespace: i64,
         km_uuid: &Uuid,
     ) -> Result<Option<CertificateChain>> {
+        let _wp = wd::watch_millis("KeystoreDB::retrieve_attestation_key_and_cert_chain", 500);
+
         match domain {
             Domain::APP | Domain::SELINUX => {}
             _ => {
@@ -2143,6 +2180,8 @@
         caller_uid: u32,
         check_permission: impl Fn(&KeyDescriptor) -> Result<()>,
     ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::migrate_key_namespace", 500);
+
         let destination = match destination.domain {
             Domain::APP => KeyDescriptor { nspace: caller_uid as i64, ..(*destination).clone() },
             Domain::SELINUX => (*destination).clone(),
@@ -2211,6 +2250,8 @@
         metadata: &KeyMetaData,
         km_uuid: &Uuid,
     ) -> Result<KeyIdGuard> {
+        let _wp = wd::watch_millis("KeystoreDB::store_new_key", 500);
+
         let (alias, domain, namespace) = match key {
             KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None }
             | KeyDescriptor { alias: Some(alias), domain: Domain::SELINUX, nspace, blob: None } => {
@@ -2266,6 +2307,8 @@
         cert: &[u8],
         km_uuid: &Uuid,
     ) -> Result<KeyIdGuard> {
+        let _wp = wd::watch_millis("KeystoreDB::store_new_certificate", 500);
+
         let (alias, domain, namespace) = match key {
             KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None }
             | KeyDescriptor { alias: Some(alias), domain: Domain::SELINUX, nspace, blob: None } => {
@@ -2545,6 +2588,8 @@
     /// zero, the key also gets marked unreferenced and scheduled for deletion.
     /// Returns Ok(true) if the key was marked unreferenced as a hint to the garbage collector.
     pub fn check_and_update_key_usage_count(&mut self, key_id: i64) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::check_and_update_key_usage_count", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let limit: Option<i32> = tx
                 .query_row(
@@ -2591,6 +2636,8 @@
         caller_uid: u32,
         check_permission: impl Fn(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
     ) -> Result<(KeyIdGuard, KeyEntry)> {
+        let _wp = wd::watch_millis("KeystoreDB::load_key_entry", 500);
+
         loop {
             match self.load_key_entry_internal(
                 key,
@@ -2718,6 +2765,8 @@
         caller_uid: u32,
         check_permission: impl Fn(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
     ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::unbind_key", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let (key_id, access_key_descriptor, access_vector) =
                 Self::load_access_tuple(tx, key, key_type, caller_uid)
@@ -2747,6 +2796,8 @@
     /// Delete all artifacts belonging to the namespace given by the domain-namespace tuple.
     /// This leaves all of the blob entries orphaned for subsequent garbage collection.
     pub fn unbind_keys_for_namespace(&mut self, domain: Domain, namespace: i64) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::unbind_keys_for_namespace", 500);
+
         if !(domain == Domain::APP || domain == Domain::SELINUX) {
             return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
                 .context("In unbind_keys_for_namespace.");
@@ -2798,6 +2849,8 @@
         user_id: u32,
         keep_non_super_encrypted_keys: bool,
     ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::unbind_keys_for_user", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let mut stmt = tx
                 .prepare(&format!(
@@ -2898,6 +2951,8 @@
     /// The key descriptors will have the domain, nspace, and alias field set.
     /// Domain must be APP or SELINUX, the caller must make sure of that.
     pub fn list(&mut self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+        let _wp = wd::watch_millis("KeystoreDB::list", 500);
+
         self.with_transaction(TransactionBehavior::Deferred, |tx| {
             let mut stmt = tx
                 .prepare(
@@ -2939,6 +2994,8 @@
         access_vector: KeyPermSet,
         check_permission: impl Fn(&KeyDescriptor, &KeyPermSet) -> Result<()>,
     ) -> Result<KeyDescriptor> {
+        let _wp = wd::watch_millis("KeystoreDB::grant", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             // Load the key_id and complete the access control tuple.
             // We ignore the access vector here because grants cannot be granted.
@@ -3004,6 +3061,8 @@
         grantee_uid: u32,
         check_permission: impl Fn(&KeyDescriptor) -> Result<()>,
     ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::ungrant", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             // Load the key_id and complete the access control tuple.
             // We ignore the access vector here because grants cannot be granted.
@@ -3055,6 +3114,8 @@
 
     /// Insert or replace the auth token based on the UNIQUE constraint of the auth token table
     pub fn insert_auth_token(&mut self, auth_token: &HardwareAuthToken) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::insert_auth_token", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             tx.execute(
                 "INSERT OR REPLACE INTO perboot.authtoken (challenge, user_id, auth_id,
@@ -3082,6 +3143,8 @@
     where
         F: Fn(&AuthTokenEntry) -> bool,
     {
+        let _wp = wd::watch_millis("KeystoreDB::find_auth_token_entry", 500);
+
         self.with_transaction(TransactionBehavior::Deferred, |tx| {
             let mut stmt = tx
                 .prepare("SELECT * from perboot.authtoken ORDER BY time_received DESC;")
@@ -3117,6 +3180,8 @@
 
     /// Insert last_off_body into the metadata table at the initialization of auth token table
     pub fn insert_last_off_body(&mut self, last_off_body: MonotonicRawTime) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::insert_last_off_body", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             tx.execute(
                 "INSERT OR REPLACE INTO perboot.metadata (key, value) VALUES (?, ?);",
@@ -3129,6 +3194,8 @@
 
     /// Update last_off_body when on_device_off_body is called
     pub fn update_last_off_body(&mut self, last_off_body: MonotonicRawTime) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::update_last_off_body", 500);
+
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             tx.execute(
                 "UPDATE perboot.metadata SET value = ? WHERE key = ?;",
@@ -3141,6 +3208,8 @@
 
     /// Get last_off_body time when finding auth tokens
     fn get_last_off_body(tx: &Transaction) -> Result<MonotonicRawTime> {
+        let _wp = wd::watch_millis("KeystoreDB::get_last_off_body", 500);
+
         tx.query_row(
             "SELECT value from perboot.metadata WHERE key = ?;",
             params!["last_off_body"],
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 378b72f..04d1f77 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -682,9 +682,10 @@
             // So the HAT cannot be presented on create. So on update/finish we present both
             // an per-op-bound auth token and a timestamp token.
             (Some(_), true, true) => (None, DeferredAuthState::TimeStampedOpAuthRequired),
-            (Some(hat), true, false) => {
-                (None, DeferredAuthState::TimeStampRequired(hat.take_auth_token()))
-            }
+            (Some(hat), true, false) => (
+                Some(hat.auth_token().clone()),
+                DeferredAuthState::TimeStampRequired(hat.take_auth_token()),
+            ),
             (Some(hat), false, true) => {
                 (Some(hat.take_auth_token()), DeferredAuthState::OpAuthRequired)
             }
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index d1b2ffb..f969cb6 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -30,16 +30,13 @@
 //! Keystore functions should use `anyhow::Result` to return error conditions, and
 //! context should be added every time an error is forwarded.
 
-use std::cmp::PartialEq;
-
 pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::ErrorCode::ErrorCode;
 pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
-
-use keystore2_selinux as selinux;
-
 use android_system_keystore2::binder::{
     ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode,
 };
+use keystore2_selinux as selinux;
+use std::cmp::PartialEq;
 
 /// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
 /// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index bd28ca6..54fceab 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -20,6 +20,7 @@
 use crate::legacy_blob::LegacyBlobLoader;
 use crate::legacy_migrator::LegacyMigrator;
 use crate::super_key::SuperKeyManager;
+use crate::utils::watchdog as wd;
 use crate::utils::Asp;
 use crate::{async_task::AsyncTask, database::MonotonicRawTime};
 use crate::{
@@ -58,6 +59,7 @@
             Box::new(|uuid, blob| {
                 let km_dev: Strong<dyn IKeyMintDevice> =
                     get_keymint_dev_by_uuid(uuid).map(|(dev, _)| dev)?.get_interface()?;
+                let _wp = wd::watch_millis("In create_thread_local_db: calling deleteKey", 500);
                 map_km_error(km_dev.deleteKey(&*blob))
                     .context("In invalidate key closure: Trying to invalidate key blob.")
             }),
@@ -227,8 +229,10 @@
             .context("In connect_keymint: Trying to get Legacy wrapper.")
     }?;
 
+    let wp = wd::watch_millis("In connect_keymint: calling getHardwareInfo()", 500);
     let hw_info = map_km_error(keymint.getHardwareInfo())
         .context("In connect_keymint: Failed to get hardware info.")?;
+    drop(wp);
 
     Ok((Asp::new(keymint.as_binder()), hw_info))
 }
diff --git a/keystore2/src/legacy_migrator.rs b/keystore2/src/legacy_migrator.rs
index 5e0d573..d5647cd 100644
--- a/keystore2/src/legacy_migrator.rs
+++ b/keystore2/src/legacy_migrator.rs
@@ -17,7 +17,7 @@
 use crate::error::Error;
 use crate::key_parameter::KeyParameterValue;
 use crate::legacy_blob::BlobValue;
-use crate::utils::uid_to_android_user;
+use crate::utils::{uid_to_android_user, watchdog as wd};
 use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
 use crate::{
     database::{
@@ -196,6 +196,8 @@
 
     /// List all aliases for uid in the legacy database.
     pub fn list_uid(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+        let _wp = wd::watch_millis("LegacyMigrator::list_uid", 500);
+
         let uid = match (domain, namespace) {
             (Domain::APP, namespace) => namespace as u32,
             (Domain::SELINUX, Self::WIFI_NAMESPACE) => Self::AID_WIFI,
@@ -290,6 +292,8 @@
     where
         F: Fn() -> Result<T>,
     {
+        let _wp = wd::watch_millis("LegacyMigrator::with_try_migrate", 500);
+
         // Access the key and return on success.
         match key_accessor() {
             Ok(result) => return Ok(result),
@@ -342,6 +346,8 @@
     where
         F: FnMut() -> Result<Option<T>>,
     {
+        let _wp = wd::watch_millis("LegacyMigrator::with_try_migrate_super_key", 500);
+
         match key_accessor() {
             Ok(Some(result)) => return Ok(Some(result)),
             Ok(None) => {}
@@ -364,6 +370,8 @@
     /// Deletes all keys belonging to the given namespace, migrating them into the database
     /// for subsequent garbage collection if necessary.
     pub fn bulk_delete_uid(&self, domain: Domain, nspace: i64) -> Result<()> {
+        let _wp = wd::watch_millis("LegacyMigrator::bulk_delete_uid", 500);
+
         let uid = match (domain, nspace) {
             (Domain::APP, nspace) => nspace as u32,
             (Domain::SELINUX, Self::WIFI_NAMESPACE) => Self::AID_WIFI,
@@ -385,6 +393,8 @@
         user_id: u32,
         keep_non_super_encrypted_keys: bool,
     ) -> Result<()> {
+        let _wp = wd::watch_millis("LegacyMigrator::bulk_delete_user", 500);
+
         let result = self.do_serialized(move |migrator_state| {
             migrator_state
                 .bulk_delete(BulkDeleteRequest::User(user_id), keep_non_super_encrypted_keys)
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 3332b83..c04c4b0 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -47,3 +47,6 @@
 mod db_utils;
 mod gc;
 mod super_key;
+
+#[cfg(feature = "watchdog")]
+mod watchdog;
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 9e7576e..a099d18 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -22,7 +22,7 @@
 use crate::globals::{DB, LEGACY_MIGRATOR, SUPER_KEY};
 use crate::permission::{KeyPerm, KeystorePerm};
 use crate::super_key::UserState;
-use crate::utils::{check_key_permission, check_keystore_permission};
+use crate::utils::{check_key_permission, check_keystore_permission, watchdog as wd};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::IKeyMintDevice::IKeyMintDevice;
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
 use android_security_maintenance::aidl::android::security::maintenance::{
@@ -133,11 +133,17 @@
         }
     }
 
-    fn early_boot_ended_help(sec_level: &SecurityLevel) -> Result<()> {
-        let (dev, _, _) =
-            get_keymint_device(sec_level).context("In early_boot_ended: getting keymint device")?;
+    fn early_boot_ended_help(sec_level: SecurityLevel) -> Result<()> {
+        let (dev, _, _) = get_keymint_device(&sec_level)
+            .context("In early_boot_ended: getting keymint device")?;
         let km_dev: Strong<dyn IKeyMintDevice> =
             dev.get_interface().context("In early_boot_ended: getting keymint device interface")?;
+
+        let _wp = wd::watch_millis_with(
+            "In early_boot_ended_help: calling earlyBootEnded()",
+            500,
+            move || format!("Seclevel: {:?}", sec_level),
+        );
         map_km_error(km_dev.earlyBootEnded())
             .context("In keymint device: calling earlyBootEnded")?;
         Ok(())
@@ -157,7 +163,7 @@
             (SecurityLevel::STRONGBOX, "STRONGBOX"),
         ];
         sec_levels.iter().fold(Ok(()), |result, (sec_level, sec_level_string)| {
-            let curr_result = Maintenance::early_boot_ended_help(sec_level);
+            let curr_result = Maintenance::early_boot_ended_help(*sec_level);
             if curr_result.is_err() {
                 log::error!(
                     "Call to earlyBootEnded failed for security level {}.",
@@ -219,30 +225,37 @@
 
 impl IKeystoreMaintenance for Maintenance {
     fn onUserPasswordChanged(&self, user_id: i32, password: Option<&[u8]>) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::onUserPasswordChanged", 500);
         map_or_log_err(Self::on_user_password_changed(user_id, password.map(|pw| pw.into())), Ok)
     }
 
     fn onUserAdded(&self, user_id: i32) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::onUserAdded", 500);
         map_or_log_err(Self::add_or_remove_user(user_id), Ok)
     }
 
     fn onUserRemoved(&self, user_id: i32) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::onUserRemoved", 500);
         map_or_log_err(Self::add_or_remove_user(user_id), Ok)
     }
 
     fn clearNamespace(&self, domain: Domain, nspace: i64) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::clearNamespace", 500);
         map_or_log_err(Self::clear_namespace(domain, nspace), Ok)
     }
 
     fn getState(&self, user_id: i32) -> BinderResult<AidlUserState> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::getState", 500);
         map_or_log_err(Self::get_state(user_id), Ok)
     }
 
     fn earlyBootEnded(&self) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::earlyBootEnded", 500);
         map_or_log_err(Self::early_boot_ended(), Ok)
     }
 
     fn onDeviceOffBody(&self) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::onDeviceOffBody", 500);
         map_or_log_err(Self::on_device_off_body(), Ok)
     }
 
@@ -251,6 +264,7 @@
         source: &KeyDescriptor,
         destination: &KeyDescriptor,
     ) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::migrateKeyNamespace", 500);
         map_or_log_err(Self::migrate_key_namespace(source, destination), Ok)
     }
 }
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index 0b5c77a..8d7ad0a 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -128,7 +128,7 @@
 use crate::enforcements::AuthInfo;
 use crate::error::{map_err_with, map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
 use crate::metrics::log_key_operation_event_stats;
-use crate::utils::Asp;
+use crate::utils::{watchdog as wd, Asp};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
     SecurityLevel::SecurityLevel,
@@ -291,6 +291,8 @@
                 }
             };
 
+        let _wp = wd::watch_millis("In Operation::prune: calling abort()", 500);
+
         // We abort the operation. If there was an error we log it but ignore it.
         if let Err(e) = map_km_error(km_op.abort()) {
             log::error!("In prune: KeyMint::abort failed with {:?}.", e);
@@ -370,10 +372,10 @@
             .before_update()
             .context("In update_aad: Trying to get auth tokens.")?;
 
-        self.update_outcome(
-            &mut *outcome,
-            map_km_error(km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref())),
-        )
+        self.update_outcome(&mut *outcome, {
+            let _wp = wd::watch_millis("Operation::update_aad: calling updateAad", 500);
+            map_km_error(km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref()))
+        })
         .context("In update_aad: KeyMint::update failed.")?;
 
         Ok(())
@@ -397,10 +399,10 @@
             .context("In update: Trying to get auth tokens.")?;
 
         let output = self
-            .update_outcome(
-                &mut *outcome,
-                map_km_error(km_op.update(input, hat.as_ref(), tst.as_ref())),
-            )
+            .update_outcome(&mut *outcome, {
+                let _wp = wd::watch_millis("Operation::update: calling update", 500);
+                map_km_error(km_op.update(input, hat.as_ref(), tst.as_ref()))
+            })
             .context("In update: KeyMint::update failed.")?;
 
         if output.is_empty() {
@@ -430,16 +432,16 @@
             .context("In finish: Trying to get auth tokens.")?;
 
         let output = self
-            .update_outcome(
-                &mut *outcome,
+            .update_outcome(&mut *outcome, {
+                let _wp = wd::watch_millis("Operation::finish: calling finish", 500);
                 map_km_error(km_op.finish(
                     input,
                     signature,
                     hat.as_ref(),
                     tst.as_ref(),
                     confirmation_token.as_deref(),
-                )),
-            )
+                ))
+            })
             .context("In finish: KeyMint::finish failed.")?;
 
         self.auth_info.lock().unwrap().after_finish().context("In finish.")?;
@@ -463,7 +465,10 @@
         let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
             self.km_op.get_interface().context("In abort: Failed to get KeyMintOperation.")?;
 
-        map_km_error(km_op.abort()).context("In abort: KeyMint::abort failed.")
+        {
+            let _wp = wd::watch_millis("Operation::abort: calling abort", 500);
+            map_km_error(km_op.abort()).context("In abort: KeyMint::abort failed.")
+        }
     }
 }
 
@@ -837,6 +842,7 @@
 
 impl IKeystoreOperation for KeystoreOperation {
     fn updateAad(&self, aad_input: &[u8]) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IKeystoreOperation::updateAad", 500);
         map_or_log_err(
             self.with_locked_operation(
                 |op| op.update_aad(aad_input).context("In KeystoreOperation::updateAad"),
@@ -847,6 +853,7 @@
     }
 
     fn update(&self, input: &[u8]) -> binder::public_api::Result<Option<Vec<u8>>> {
+        let _wp = wd::watch_millis("IKeystoreOperation::update", 500);
         map_or_log_err(
             self.with_locked_operation(
                 |op| op.update(input).context("In KeystoreOperation::update"),
@@ -860,6 +867,7 @@
         input: Option<&[u8]>,
         signature: Option<&[u8]>,
     ) -> binder::public_api::Result<Option<Vec<u8>>> {
+        let _wp = wd::watch_millis("IKeystoreOperation::finish", 500);
         map_or_log_err(
             self.with_locked_operation(
                 |op| op.finish(input, signature).context("In KeystoreOperation::finish"),
@@ -870,6 +878,7 @@
     }
 
     fn abort(&self) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IKeystoreOperation::abort", 500);
         map_err_with(
             self.with_locked_operation(
                 |op| op.abort(Outcome::Abort).context("In KeystoreOperation::abort"),
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
index 06432fe..9e6ef41 100644
--- a/keystore2/src/raw_device.rs
+++ b/keystore2/src/raw_device.rs
@@ -22,7 +22,7 @@
     error::{map_km_error, Error},
     globals::get_keymint_device,
     super_key::KeyBlob,
-    utils::{key_characteristics_to_internal, Asp, AID_KEYSTORE},
+    utils::{key_characteristics_to_internal, watchdog as wd, Asp, AID_KEYSTORE},
 };
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     BeginResult::BeginResult, ErrorCode::ErrorCode, HardwareAuthToken::HardwareAuthToken,
@@ -151,7 +151,7 @@
             self.create_and_store_key(db, &key_desc, |km_dev| km_dev.generateKey(&params, None))
                 .context("In lookup_or_generate_key: generate_and_store_key failed")?;
             Self::lookup_from_desc(db, key_desc)
-                .context("In lookup_or_generate_key: secpnd lookup failed")
+                .context("In lookup_or_generate_key: second lookup failed")
         }
     }
 
@@ -170,8 +170,14 @@
     {
         match f(key_blob) {
             Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
-                let upgraded_blob = map_km_error(km_dev.upgradeKey(key_blob, &[]))
-                    .context("In upgrade_keyblob_if_required_with: Upgrade failed")?;
+                let upgraded_blob = map_km_error({
+                    let _wp = wd::watch_millis(
+                        "In KeyMintDevice::upgrade_keyblob_if_required_with: calling upgradeKey.",
+                        500,
+                    );
+                    km_dev.upgradeKey(key_blob, &[])
+                })
+                .context("In upgrade_keyblob_if_required_with: Upgrade failed")?;
 
                 let mut new_blob_metadata = BlobMetaData::new();
                 new_blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
@@ -223,14 +229,20 @@
 
         let begin_result: BeginResult = self
             .upgrade_keyblob_if_required_with(db, &km_dev, key_id_guard, &key_blob, |blob| {
-                map_km_error(km_dev.begin(purpose, blob, operation_parameters, auth_token))
+                map_km_error({
+                    let _wp = wd::watch_millis("In use_key_in_one_step: calling: begin", 500);
+                    km_dev.begin(purpose, blob, operation_parameters, auth_token)
+                })
             })
             .context("In use_key_in_one_step: Failed to begin operation.")?;
         let operation: Strong<dyn IKeyMintOperation> = begin_result
             .operation
             .ok_or_else(Error::sys)
             .context("In use_key_in_one_step: Operation missing")?;
-        map_km_error(operation.finish(Some(input), None, None, None, None))
-            .context("In use_key_in_one_step: Failed to finish operation.")
+        map_km_error({
+            let _wp = wd::watch_millis("In use_key_in_one_step: calling: finish", 500);
+            operation.finish(Some(input), None, None, None, None)
+        })
+        .context("In use_key_in_one_step: Failed to finish operation.")
     }
 }
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index f8ee369..fc1a6ad 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -45,7 +45,7 @@
 use crate::database::{CertificateChain, KeystoreDB, Uuid};
 use crate::error::{self, map_or_log_err, map_rem_prov_error, Error};
 use crate::globals::{get_keymint_device, get_remotely_provisioned_component, DB};
-use crate::utils::Asp;
+use crate::utils::{watchdog as wd, Asp};
 
 /// Contains helper functions to check if remote provisioning is enabled on the system and, if so,
 /// to assign and retrieve attestation keys and certificate chains.
@@ -392,6 +392,7 @@
         expired_by: i64,
         sec_level: SecurityLevel,
     ) -> binder::public_api::Result<AttestationPoolStatus> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::getPoolStatus", 500);
         map_or_log_err(self.get_pool_status(expired_by, sec_level), Ok)
     }
 
@@ -405,6 +406,7 @@
         protected_data: &mut ProtectedData,
         device_info: &mut DeviceInfo,
     ) -> binder::public_api::Result<Vec<u8>> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::generateCsr", 500);
         map_or_log_err(
             self.generate_csr(
                 test_mode,
@@ -427,6 +429,7 @@
         expiration_date: i64,
         sec_level: SecurityLevel,
     ) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::provisionCertChain", 500);
         map_or_log_err(
             self.provision_cert_chain(public_key, batch_cert, certs, expiration_date, sec_level),
             Ok,
@@ -438,14 +441,17 @@
         is_test_mode: bool,
         sec_level: SecurityLevel,
     ) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::generateKeyPair", 500);
         map_or_log_err(self.generate_key_pair(is_test_mode, sec_level), Ok)
     }
 
     fn getSecurityLevels(&self) -> binder::public_api::Result<Vec<SecurityLevel>> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::getSecurityLevels", 500);
         map_or_log_err(self.get_security_levels(), Ok)
     }
 
     fn deleteAllKeys(&self) -> binder::public_api::Result<i64> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::deleteAllKeys", 500);
         map_or_log_err(self.delete_all_keys(), Ok)
     }
 }
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 53880a1..d10aba0 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -14,6 +14,30 @@
 
 //! This crate implements the IKeystoreSecurityLevel interface.
 
+use crate::attestation_key_utils::{get_attest_key_info, AttestationKeyInfo};
+use crate::audit_log::{log_key_deleted, log_key_generated, log_key_imported};
+use crate::database::{CertificateInfo, KeyIdGuard};
+use crate::error::{self, map_km_error, map_or_log_err, Error, ErrorCode};
+use crate::globals::{DB, ENFORCEMENTS, LEGACY_MIGRATOR, SUPER_KEY};
+use crate::key_parameter::KeyParameter as KsKeyParam;
+use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
+use crate::metrics::log_key_creation_event_stats;
+use crate::remote_provisioning::RemProvState;
+use crate::super_key::{KeyBlob, SuperKeyManager};
+use crate::utils::{
+    check_device_attestation_permissions, check_key_permission, is_device_id_attestation_tag,
+    key_characteristics_to_internal, uid_to_android_user, watchdog as wd, Asp,
+};
+use crate::{
+    database::{
+        BlobMetaData, BlobMetaEntry, DateTime, KeyEntry, KeyEntryLoadBits, KeyMetaData,
+        KeyMetaEntry, KeyType, SubComponentType, Uuid,
+    },
+    operation::KeystoreOperation,
+    operation::LoggingInfo,
+    operation::OperationDb,
+    permission::KeyPerm,
+};
 use crate::{globals::get_keymint_device, id_rotation::IdRotationState};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, AttestationKey::AttestationKey,
@@ -30,34 +54,6 @@
     IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
     KeyMetadata::KeyMetadata, KeyParameters::KeyParameters,
 };
-
-use crate::attestation_key_utils::{get_attest_key_info, AttestationKeyInfo};
-use crate::audit_log::{log_key_deleted, log_key_generated, log_key_imported};
-use crate::database::{CertificateInfo, KeyIdGuard};
-use crate::globals::{DB, ENFORCEMENTS, LEGACY_MIGRATOR, SUPER_KEY};
-use crate::key_parameter::KeyParameter as KsKeyParam;
-use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
-use crate::metrics::log_key_creation_event_stats;
-use crate::remote_provisioning::RemProvState;
-use crate::super_key::{KeyBlob, SuperKeyManager};
-use crate::utils::{
-    check_device_attestation_permissions, check_key_permission, is_device_id_attestation_tag,
-    uid_to_android_user, Asp,
-};
-use crate::{
-    database::{
-        BlobMetaData, BlobMetaEntry, DateTime, KeyEntry, KeyEntryLoadBits, KeyMetaData,
-        KeyMetaEntry, KeyType, SubComponentType, Uuid,
-    },
-    operation::KeystoreOperation,
-    operation::LoggingInfo,
-    operation::OperationDb,
-    permission::KeyPerm,
-};
-use crate::{
-    error::{self, map_km_error, map_or_log_err, Error, ErrorCode},
-    utils::key_characteristics_to_internal,
-};
 use anyhow::{anyhow, Context, Result};
 
 /// Implementation of the IKeystoreSecurityLevel Interface.
@@ -104,6 +100,11 @@
         Ok((result, km_uuid))
     }
 
+    fn watch_millis(&self, id: &'static str, millis: u64) -> Option<wd::WatchPoint> {
+        let sec_level = self.security_level;
+        wd::watch_millis_with(id, millis, move || format!("SecurityLevel {:?}", sec_level))
+    }
+
     fn store_new_key(
         &self,
         key: KeyDescriptor,
@@ -313,12 +314,13 @@
                 &blob_metadata,
                 &operation_parameters,
                 |blob| loop {
-                    match map_km_error(km_dev.begin(
-                        purpose,
-                        blob,
-                        &operation_parameters,
-                        immediate_hat.as_ref(),
-                    )) {
+                    match map_km_error({
+                        let _wp = self.watch_millis(
+                            "In KeystoreSecurityLevel::create_operation: calling begin",
+                            500,
+                        );
+                        km_dev.begin(purpose, blob, &operation_parameters, immediate_hat.as_ref())
+                    }) {
                         Err(Error::Km(ErrorCode::TOO_MANY_OPERATIONS)) => {
                             self.operation_db.prune(caller_uid, forced)?;
                             continue;
@@ -334,12 +336,19 @@
         let op_params: Vec<KeyParameter> = operation_parameters.to_vec();
 
         let operation = match begin_result.operation {
-            Some(km_op) => {
-                self.operation_db.create_operation(km_op, caller_uid, auth_info, forced,
-                    LoggingInfo::new(self.security_level, purpose, op_params,
-                         upgraded_blob.is_some()))
-            },
-            None => return Err(Error::sys()).context("In create_operation: Begin operation returned successfully, but did not return a valid operation."),
+            Some(km_op) => self.operation_db.create_operation(
+                km_op,
+                caller_uid,
+                auth_info,
+                forced,
+                LoggingInfo::new(self.security_level, purpose, op_params, upgraded_blob.is_some()),
+            ),
+            None => {
+                return Err(Error::sys()).context(concat!(
+                    "In create_operation: Begin operation returned successfully, ",
+                    "but did not return a valid operation."
+                ))
+            }
         };
 
         let op_binder: binder::public_api::Strong<dyn IKeystoreOperation> =
@@ -371,9 +380,19 @@
         let mut result = params.to_vec();
         // If there is an attestation challenge we need to get an application id.
         if params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE) {
-            let aaid = keystore2_aaid::get_aaid(uid).map_err(|e| {
-                anyhow!(format!("In add_certificate_parameters: get_aaid returned status {}.", e))
-            })?;
+            let aaid = {
+                let _wp = self.watch_millis(
+                    "In KeystoreSecurityLevel::add_certificate_parameters calling: get_aaid",
+                    500,
+                );
+                keystore2_aaid::get_aaid(uid).map_err(|e| {
+                    anyhow!(format!(
+                        "In add_certificate_parameters: get_aaid returned status {}.",
+                        e
+                    ))
+                })
+            }?;
+
             result.push(KeyParameter {
                 tag: Tag::ATTESTATION_APPLICATION_ID,
                 value: KeyParameterValue::Blob(aaid),
@@ -495,21 +514,48 @@
                             attestKeyParams: vec![],
                             issuerSubjectName: issuer_subject.clone(),
                         });
-                        map_km_error(km_dev.generateKey(&params, attest_key.as_ref()))
+                        map_km_error({
+                            let _wp = self.watch_millis(
+                                concat!(
+                                    "In KeystoreSecurityLevel::generate_key (UserGenerated): ",
+                                    "calling generate_key."
+                                ),
+                                5000, // Generate can take a little longer.
+                            );
+                            km_dev.generateKey(&params, attest_key.as_ref())
+                        })
                     },
                 )
                 .context("In generate_key: Using user generated attestation key.")
                 .map(|(result, _)| result),
             Some(AttestationKeyInfo::RemoteProvisioned { attestation_key, attestation_certs }) => {
-                map_km_error(km_dev.generateKey(&params, Some(&attestation_key)))
-                    .context("While generating Key with remote provisioned attestation key.")
-                    .map(|mut creation_result| {
-                        creation_result.certificateChain.push(attestation_certs);
-                        creation_result
-                    })
+                map_km_error({
+                    let _wp = self.watch_millis(
+                        concat!(
+                            "In KeystoreSecurityLevel::generate_key (RemoteProvisioned): ",
+                            "calling generate_key.",
+                        ),
+                        5000, // Generate can take a little longer.
+                    );
+                    km_dev.generateKey(&params, Some(&attestation_key))
+                })
+                .context("While generating Key with remote provisioned attestation key.")
+                .map(|mut creation_result| {
+                    creation_result.certificateChain.push(attestation_certs);
+                    creation_result
+                })
             }
-            None => map_km_error(km_dev.generateKey(&params, None))
-                .context("While generating Key without explicit attestation key."),
+            None => map_km_error({
+                let _wp = self.watch_millis(
+                    concat!(
+                        "In KeystoreSecurityLevel::generate_key (No attestation): ",
+                        "calling generate_key.",
+                    ),
+                    5000, // Generate can take a little longer.
+                );
+                km_dev.generateKey(&params, None)
+            })
+            .context("While generating Key without explicit attestation key."),
         }
         .context("In generate_key.")?;
 
@@ -566,9 +612,12 @@
 
         let km_dev: Strong<dyn IKeyMintDevice> =
             self.keymint.get_interface().context("In import_key: Trying to get the KM device")?;
-        let creation_result =
-            map_km_error(km_dev.importKey(&params, format, key_data, None /* attestKey */))
-                .context("In import_key: Trying to call importKey")?;
+        let creation_result = map_km_error({
+            let _wp =
+                self.watch_millis("In KeystoreSecurityLevel::import_key: calling importKey.", 500);
+            km_dev.importKey(&params, format, key_data, None /* attestKey */)
+        })
+        .context("In import_key: Trying to call importKey")?;
 
         let user_id = uid_to_android_user(caller_uid);
         self.store_new_key(key, creation_result, user_id, Some(flags)).context("In import_key.")
@@ -681,6 +730,10 @@
                 &wrapping_blob_metadata,
                 &[],
                 |wrapping_blob| {
+                    let _wp = self.watch_millis(
+                        "In KeystoreSecurityLevel::import_wrapped_key: calling importWrappedKey.",
+                        500,
+                    );
                     let creation_result = map_km_error(km_dev.importWrappedKey(
                         wrapped_data,
                         wrapping_blob,
@@ -739,8 +792,17 @@
     {
         match f(key_blob) {
             Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
-                let upgraded_blob = map_km_error(km_dev.upgradeKey(key_blob, params))
-                    .context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
+                let upgraded_blob = {
+                    let _wp = self.watch_millis(
+                        concat!(
+                            "In KeystoreSecurityLevel::upgrade_keyblob_if_required_with: ",
+                            "calling upgradeKey."
+                        ),
+                        500,
+                    );
+                    map_km_error(km_dev.upgradeKey(key_blob, params))
+                }
+                .context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
 
                 if let Some(kid) = key_id_guard {
                     Self::store_upgraded_keyblob(
@@ -810,14 +872,35 @@
             "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: ",
             "Getting keymint device interface"
         ))?;
-        match map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob)) {
+        match {
+            let _wp = self.watch_millis(
+                concat!(
+                    "In IKeystoreSecurityLevel::convert_storage_key_to_ephemeral: ",
+                    "calling convertStorageKeyToEphemeral (1)"
+                ),
+                500,
+            );
+            map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
+        } {
             Ok(result) => {
                 Ok(EphemeralStorageKeyResponse { ephemeralKey: result, upgradedBlob: None })
             }
             Err(error::Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
-                let upgraded_blob = map_km_error(km_dev.upgradeKey(key_blob, &[]))
-                    .context("In convert_storage_key_to_ephemeral: Failed to upgrade key blob.")?;
-                let ephemeral_key = map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
+                let upgraded_blob = {
+                    let _wp = self.watch_millis(
+                        "In convert_storage_key_to_ephemeral: calling upgradeKey",
+                        500,
+                    );
+                    map_km_error(km_dev.upgradeKey(key_blob, &[]))
+                }
+                .context("In convert_storage_key_to_ephemeral: Failed to upgrade key blob.")?;
+                let ephemeral_key = {
+                    let _wp = self.watch_millis(
+                        "In convert_storage_key_to_ephemeral: calling convertStorageKeyToEphemeral (2)",
+                        500,
+                    );
+                    map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
+                }
                     .context(concat!(
                         "In convert_storage_key_to_ephemeral: ",
                         "Failed to retrieve ephemeral key (after upgrade)."
@@ -851,7 +934,11 @@
             .keymint
             .get_interface()
             .context("In IKeystoreSecurityLevel delete_key: Getting keymint device interface")?;
-        map_km_error(km_dev.deleteKey(&key_blob)).context("In keymint device deleteKey")
+        {
+            let _wp =
+                self.watch_millis("In KeystoreSecuritylevel::delete_key: calling deleteKey", 500);
+            map_km_error(km_dev.deleteKey(&key_blob)).context("In keymint device deleteKey")
+        }
     }
 }
 
@@ -864,6 +951,7 @@
         operation_parameters: &[KeyParameter],
         forced: bool,
     ) -> binder::public_api::Result<CreateOperationResponse> {
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::createOperation", 500);
         map_or_log_err(self.create_operation(key, operation_parameters, forced), Ok)
     }
     fn generateKey(
@@ -874,6 +962,9 @@
         flags: i32,
         entropy: &[u8],
     ) -> binder::public_api::Result<KeyMetadata> {
+        // Duration is set to 5 seconds, because generateKey - especially for RSA keys, takes more
+        // time than other operations
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::generateKey", 5000);
         let result = self.generate_key(key, attestation_key, params, flags, entropy);
         log_key_creation_event_stats(self.security_level, params, &result);
         log_key_generated(key, ThreadState::get_calling_uid(), result.is_ok());
@@ -887,6 +978,7 @@
         flags: i32,
         key_data: &[u8],
     ) -> binder::public_api::Result<KeyMetadata> {
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::importKey", 500);
         let result = self.import_key(key, attestation_key, params, flags, key_data);
         log_key_creation_event_stats(self.security_level, params, &result);
         log_key_imported(key, ThreadState::get_calling_uid(), result.is_ok());
@@ -900,6 +992,7 @@
         params: &[KeyParameter],
         authenticators: &[AuthenticatorSpec],
     ) -> binder::public_api::Result<KeyMetadata> {
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::importWrappedKey", 500);
         let result =
             self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators);
         log_key_creation_event_stats(self.security_level, params, &result);
@@ -910,9 +1003,11 @@
         &self,
         storage_key: &KeyDescriptor,
     ) -> binder::public_api::Result<EphemeralStorageKeyResponse> {
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::convertStorageKeyToEphemeral", 500);
         map_or_log_err(self.convert_storage_key_to_ephemeral(storage_key), Ok)
     }
     fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::deleteKey", 500);
         let result = self.delete_key(key);
         log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
         map_or_log_err(result, Ok)
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index b8ea244..3ce0550 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -22,7 +22,7 @@
 use crate::security_level::KeystoreSecurityLevel;
 use crate::utils::{
     check_grant_permission, check_key_permission, check_keystore_permission,
-    key_parameters_to_authorizations, Asp,
+    key_parameters_to_authorizations, watchdog as wd, Asp,
 };
 use crate::{
     database::Uuid,
@@ -354,9 +354,13 @@
         &self,
         security_level: SecurityLevel,
     ) -> binder::public_api::Result<Strong<dyn IKeystoreSecurityLevel>> {
+        let _wp = wd::watch_millis_with("IKeystoreService::getSecurityLevel", 500, move || {
+            format!("security_level: {}", security_level.0)
+        });
         map_or_log_err(self.get_security_level(security_level), Ok)
     }
     fn getKeyEntry(&self, key: &KeyDescriptor) -> binder::public_api::Result<KeyEntryResponse> {
+        let _wp = wd::watch_millis("IKeystoreService::get_key_entry", 500);
         map_or_log_err(self.get_key_entry(key), Ok)
     }
     fn updateSubcomponent(
@@ -365,6 +369,7 @@
         public_cert: Option<&[u8]>,
         certificate_chain: Option<&[u8]>,
     ) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IKeystoreService::updateSubcomponent", 500);
         map_or_log_err(self.update_subcomponent(key, public_cert, certificate_chain), Ok)
     }
     fn listEntries(
@@ -372,9 +377,11 @@
         domain: Domain,
         namespace: i64,
     ) -> binder::public_api::Result<Vec<KeyDescriptor>> {
+        let _wp = wd::watch_millis("IKeystoreService::listEntries", 500);
         map_or_log_err(self.list_entries(domain, namespace), Ok)
     }
     fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IKeystoreService::deleteKey", 500);
         let result = self.delete_key(key);
         log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
         map_or_log_err(result, Ok)
@@ -385,9 +392,11 @@
         grantee_uid: i32,
         access_vector: i32,
     ) -> binder::public_api::Result<KeyDescriptor> {
+        let _wp = wd::watch_millis("IKeystoreService::grant", 500);
         map_or_log_err(self.grant(key, grantee_uid, access_vector.into()), Ok)
     }
     fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IKeystoreService::ungrant", 500);
         map_or_log_err(self.ungrant(key, grantee_uid), Ok)
     }
 }
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 50a5f31..848707c 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -29,6 +29,7 @@
     legacy_migrator::LegacyMigrator,
     raw_device::KeyMintDevice,
     try_insert::TryInsert,
+    utils::watchdog as wd,
 };
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, BlockMode::BlockMode, HardwareAuthToken::HardwareAuthToken,
@@ -949,6 +950,10 @@
                     let key_params: Vec<KmKeyParameter> =
                         key_params.into_iter().map(|x| x.into()).collect();
                     km_dev.create_and_store_key(db, &key_desc, |dev| {
+                        let _wp = wd::watch_millis(
+                            "In lock_screen_lock_bound_key: calling importKey.",
+                            500,
+                        );
                         dev.importKey(key_params.as_slice(), KeyFormat::RAW, &encrypting_key, None)
                     })?;
                     entry.biometric_unlock = Some(BiometricUnlock {
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index bca27d1..9852aad 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -107,11 +107,17 @@
     let permission_controller: binder::Strong<dyn IPermissionController::IPermissionController> =
         binder::get_interface("permission")?;
 
-    let binder_result = permission_controller.checkPermission(
-        "android.permission.READ_PRIVILEGED_PHONE_STATE",
-        ThreadState::get_calling_pid(),
-        ThreadState::get_calling_uid() as i32,
-    );
+    let binder_result = {
+        let _wp = watchdog::watch_millis(
+            "In check_device_attestation_permissions: calling checkPermission.",
+            500,
+        );
+        permission_controller.checkPermission(
+            "android.permission.READ_PRIVILEGED_PHONE_STATE",
+            ThreadState::get_calling_pid(),
+            ThreadState::get_calling_uid() as i32,
+        )
+    };
     let has_permissions = map_binder_status(binder_result)
         .context("In check_device_attestation_permissions: checkPermission failed")?;
     match has_permissions {
@@ -233,6 +239,55 @@
     unsafe { cutils_bindgen::multiuser_get_user_id(uid) }
 }
 
+/// This module provides helpers for simplified use of the watchdog module.
+#[cfg(feature = "watchdog")]
+pub mod watchdog {
+    pub use crate::watchdog::WatchPoint;
+    use crate::watchdog::Watchdog;
+    use lazy_static::lazy_static;
+    use std::sync::Arc;
+    use std::time::Duration;
+
+    lazy_static! {
+        /// A Watchdog thread, that can be used to create watch points.
+        static ref WD: Arc<Watchdog> = Watchdog::new(Duration::from_secs(10));
+    }
+
+    /// Sets a watch point with `id` and a timeout of `millis` milliseconds.
+    pub fn watch_millis(id: &'static str, millis: u64) -> Option<WatchPoint> {
+        Watchdog::watch(&WD, id, Duration::from_millis(millis))
+    }
+
+    /// Like `watch_millis` but with a callback that is called every time a report
+    /// is printed about this watch point.
+    pub fn watch_millis_with(
+        id: &'static str,
+        millis: u64,
+        callback: impl Fn() -> String + Send + 'static,
+    ) -> Option<WatchPoint> {
+        Watchdog::watch_with(&WD, id, Duration::from_millis(millis), callback)
+    }
+}
+
+/// This module provides empty/noop implementations of the watch dog utility functions.
+#[cfg(not(feature = "watchdog"))]
+pub mod watchdog {
+    /// Noop watch point.
+    pub struct WatchPoint();
+    /// Sets a Noop watch point.
+    fn watch_millis(_: &'static str, _: u64) -> Option<WatchPoint> {
+        None
+    }
+
+    pub fn watch_millis_with(
+        _: &'static str,
+        _: u64,
+        _: impl Fn() -> String + Send + 'static,
+    ) -> Option<WatchPoint> {
+        None
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/keystore2/src/watchdog.rs b/keystore2/src/watchdog.rs
new file mode 100644
index 0000000..9cca171
--- /dev/null
+++ b/keystore2/src/watchdog.rs
@@ -0,0 +1,326 @@
+// Copyright 2021, 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.
+
+// Can be removed when instrumentations are added to keystore.
+#![allow(dead_code)]
+
+//! This module implements a watchdog thread.
+
+use std::{
+    cmp::min,
+    collections::HashMap,
+    sync::Arc,
+    sync::{Condvar, Mutex, MutexGuard},
+    thread,
+};
+use std::{
+    marker::PhantomData,
+    time::{Duration, Instant},
+};
+
+/// Represents a Watchdog record. It can be created with `Watchdog::watch` or
+/// `Watchdog::watch_with`. It disarms the record when dropped.
+pub struct WatchPoint {
+    id: &'static str,
+    wd: Arc<Watchdog>,
+    not_send: PhantomData<*mut ()>, // WatchPoint must not be Send.
+}
+
+impl Drop for WatchPoint {
+    fn drop(&mut self) {
+        self.wd.disarm(self.id)
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+enum State {
+    NotRunning,
+    Running,
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+struct Index {
+    tid: thread::ThreadId,
+    id: &'static str,
+}
+
+struct Record {
+    started: Instant,
+    deadline: Instant,
+    callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+}
+
+struct WatchdogState {
+    state: State,
+    thread: Option<thread::JoinHandle<()>>,
+    timeout: Duration,
+    records: HashMap<Index, Record>,
+    last_report: Instant,
+    has_overdue: bool,
+}
+
+impl WatchdogState {
+    fn update_overdue_and_find_next_timeout(&mut self) -> (bool, Option<Duration>) {
+        let now = Instant::now();
+        let mut next_timeout: Option<Duration> = None;
+        let mut has_overdue = false;
+        for (_, r) in self.records.iter() {
+            let timeout = r.deadline.saturating_duration_since(now);
+            if timeout == Duration::new(0, 0) {
+                has_overdue = true;
+                continue;
+            }
+            next_timeout = match next_timeout {
+                Some(nt) => {
+                    if timeout < nt {
+                        Some(timeout)
+                    } else {
+                        Some(nt)
+                    }
+                }
+                None => Some(timeout),
+            };
+        }
+        (has_overdue, next_timeout)
+    }
+
+    fn log_report(&mut self, has_overdue: bool) -> bool {
+        match (self.has_overdue, has_overdue) {
+            (true, true) => {
+                if self.last_report.elapsed() < Watchdog::NOISY_REPORT_TIMEOUT {
+                    self.has_overdue = false;
+                    return false;
+                }
+            }
+            (_, false) => {
+                self.has_overdue = false;
+                return false;
+            }
+            (false, true) => {}
+        }
+        self.last_report = Instant::now();
+        self.has_overdue = has_overdue;
+        log::warn!("Keystore Watchdog report:");
+        log::warn!("Overdue records:");
+        let now = Instant::now();
+        for (i, r) in self.records.iter() {
+            if r.deadline.saturating_duration_since(now) == Duration::new(0, 0) {
+                match &r.callback {
+                    Some(cb) => {
+                        log::warn!(
+                            "{:?} {} Pending: {:?} Overdue {:?}: {}",
+                            i.tid,
+                            i.id,
+                            r.started.elapsed(),
+                            r.deadline.elapsed(),
+                            (cb)()
+                        );
+                    }
+                    None => {
+                        log::warn!(
+                            "{:?} {} Pending: {:?} Overdue {:?}",
+                            i.tid,
+                            i.id,
+                            r.started.elapsed(),
+                            r.deadline.elapsed()
+                        );
+                    }
+                }
+            }
+        }
+        true
+    }
+
+    fn disarm(&mut self, index: Index) {
+        self.records.remove(&index);
+    }
+
+    fn arm(&mut self, index: Index, record: Record) {
+        if self.records.insert(index.clone(), record).is_some() {
+            log::warn!("Recursive watchdog record at \"{:?}\" replaces previous record.", index);
+        }
+    }
+}
+
+/// Watchdog spawns a thread that logs records of all overdue watch points when a deadline
+/// is missed and at least every second as long as overdue watch points exist.
+/// The thread terminates when idle for a given period of time.
+pub struct Watchdog {
+    state: Arc<(Condvar, Mutex<WatchdogState>)>,
+}
+
+impl Watchdog {
+    /// If we have overdue records, we want to be noisy about it and log a report
+    /// at least every `NOISY_REPORT_TIMEOUT` interval.
+    const NOISY_REPORT_TIMEOUT: Duration = Duration::from_secs(1);
+
+    /// Construct a [`Watchdog`]. When `timeout` has elapsed since the watchdog thread became
+    /// idle, i.e., there are no more active or overdue watch points, the watchdog thread
+    /// terminates.
+    pub fn new(timeout: Duration) -> Arc<Self> {
+        Arc::new(Self {
+            state: Arc::new((
+                Condvar::new(),
+                Mutex::new(WatchdogState {
+                    state: State::NotRunning,
+                    thread: None,
+                    timeout,
+                    records: HashMap::new(),
+                    last_report: Instant::now(),
+                    has_overdue: false,
+                }),
+            )),
+        })
+    }
+
+    fn watch_with_optional(
+        wd: &Arc<Self>,
+        callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+        id: &'static str,
+        timeout: Duration,
+    ) -> Option<WatchPoint> {
+        let deadline = Instant::now().checked_add(timeout);
+        if deadline.is_none() {
+            log::warn!("Deadline computation failed for WatchPoint \"{}\"", id);
+            log::warn!("WatchPoint not armed.");
+            return None;
+        }
+        wd.arm(callback, id, deadline.unwrap());
+        Some(WatchPoint { id, wd: wd.clone(), not_send: Default::default() })
+    }
+
+    /// Create a new watch point. If the WatchPoint is not dropped before the timeout
+    /// expires, a report is logged at least every second, which includes the id string
+    /// and whatever string the callback returns.
+    pub fn watch_with(
+        wd: &Arc<Self>,
+        id: &'static str,
+        timeout: Duration,
+        callback: impl Fn() -> String + Send + 'static,
+    ) -> Option<WatchPoint> {
+        Self::watch_with_optional(wd, Some(Box::new(callback)), id, timeout)
+    }
+
+    /// Like `watch_with`, but without a callback.
+    pub fn watch(wd: &Arc<Self>, id: &'static str, timeout: Duration) -> Option<WatchPoint> {
+        Self::watch_with_optional(wd, None, id, timeout)
+    }
+
+    fn arm(
+        &self,
+        callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+        id: &'static str,
+        deadline: Instant,
+    ) {
+        let tid = thread::current().id();
+        let index = Index { tid, id };
+        let record = Record { started: Instant::now(), deadline, callback };
+
+        let (ref condvar, ref state) = *self.state;
+
+        let mut state = state.lock().unwrap();
+        state.arm(index, record);
+
+        if state.state != State::Running {
+            self.spawn_thread(&mut state);
+        }
+        drop(state);
+        condvar.notify_all();
+    }
+
+    fn disarm(&self, id: &'static str) {
+        let tid = thread::current().id();
+        let index = Index { tid, id };
+        let (_, ref state) = *self.state;
+
+        let mut state = state.lock().unwrap();
+        state.disarm(index);
+        // There is no need to notify condvar. There is no action required for the
+        // watchdog thread before the next deadline.
+    }
+
+    fn spawn_thread(&self, state: &mut MutexGuard<WatchdogState>) {
+        if let Some(t) = state.thread.take() {
+            t.join().expect("Watchdog thread panicked.");
+        }
+
+        let cloned_state = self.state.clone();
+
+        state.thread = Some(thread::spawn(move || {
+            let (ref condvar, ref state) = *cloned_state;
+
+            let mut state = state.lock().unwrap();
+
+            loop {
+                let (has_overdue, next_timeout) = state.update_overdue_and_find_next_timeout();
+                state.log_report(has_overdue);
+                let (next_timeout, idle) = match (has_overdue, next_timeout) {
+                    (true, Some(next_timeout)) => {
+                        (min(next_timeout, Self::NOISY_REPORT_TIMEOUT), false)
+                    }
+                    (false, Some(next_timeout)) => (next_timeout, false),
+                    (true, None) => (Self::NOISY_REPORT_TIMEOUT, false),
+                    (false, None) => (state.timeout, true),
+                };
+
+                let (s, timeout) = condvar.wait_timeout(state, next_timeout).unwrap();
+                state = s;
+
+                if idle && timeout.timed_out() && state.records.is_empty() {
+                    state.state = State::NotRunning;
+                    break;
+                }
+            }
+            log::info!("Watchdog thread idle -> terminating. Have a great day.");
+        }));
+        state.state = State::Running;
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use std::sync::atomic;
+    use std::thread;
+    use std::time::Duration;
+
+    #[test]
+    fn test_watchdog() {
+        android_logger::init_once(
+            android_logger::Config::default()
+                .with_tag("keystore2_watchdog_tests")
+                .with_min_level(log::Level::Debug),
+        );
+
+        let wd = Watchdog::new(Watchdog::NOISY_REPORT_TIMEOUT.checked_mul(3).unwrap());
+        let hit_count = Arc::new(atomic::AtomicU8::new(0));
+        let hit_count_clone = hit_count.clone();
+        let wp =
+            Watchdog::watch_with(&wd, "test_watchdog", Duration::from_millis(100), move || {
+                format!("hit_count: {}", hit_count_clone.fetch_add(1, atomic::Ordering::Relaxed))
+            });
+        assert_eq!(0, hit_count.load(atomic::Ordering::Relaxed));
+        thread::sleep(Duration::from_millis(500));
+        assert_eq!(1, hit_count.load(atomic::Ordering::Relaxed));
+        thread::sleep(Watchdog::NOISY_REPORT_TIMEOUT);
+        assert_eq!(2, hit_count.load(atomic::Ordering::Relaxed));
+        drop(wp);
+        thread::sleep(Watchdog::NOISY_REPORT_TIMEOUT.checked_mul(4).unwrap());
+        assert_eq!(2, hit_count.load(atomic::Ordering::Relaxed));
+        let (_, ref state) = *wd.state;
+        let state = state.lock().unwrap();
+        assert_eq!(state.state, State::NotRunning);
+    }
+}
diff --git a/keystore2/vpnprofilestore/lib.rs b/keystore2/vpnprofilestore/lib.rs
index d92e045..8b3bc2b 100644
--- a/keystore2/vpnprofilestore/lib.rs
+++ b/keystore2/vpnprofilestore/lib.rs
@@ -23,7 +23,7 @@
     ThreadState,
 };
 use anyhow::{Context, Result};
-use keystore2::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
+use keystore2::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader, utils::watchdog as wd};
 use rusqlite::{
     params, Connection, OptionalExtension, Transaction, TransactionBehavior, NO_PARAMS,
 };
@@ -366,15 +366,19 @@
 
 impl IVpnProfileStore for VpnProfileStore {
     fn get(&self, alias: &str) -> BinderResult<Vec<u8>> {
+        let _wp = wd::watch_millis("IVpnProfileStore::get", 500);
         map_or_log_err(self.get(alias), Ok)
     }
     fn put(&self, alias: &str, profile: &[u8]) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IVpnProfileStore::put", 500);
         map_or_log_err(self.put(alias, profile), Ok)
     }
     fn remove(&self, alias: &str) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IVpnProfileStore::remove", 500);
         map_or_log_err(self.remove(alias), Ok)
     }
     fn list(&self, prefix: &str) -> BinderResult<Vec<String>> {
+        let _wp = wd::watch_millis("IVpnProfileStore::list", 500);
         map_or_log_err(self.list(prefix), Ok)
     }
 }