Vpnprofilestore: Set sqlite busy hander to None.

Set the busy handler to None, because it is unlikely that a transaction
lock can be successfully taken while busy wating in the sqlite librarly.

Also add a vpn database stress test.

Bug: 184006658
Test: atest vpnprofilestore_test
Change-Id: Ia18d5e86683cde908444f6257949497fdd2872e4
diff --git a/keystore2/vpnprofilestore/lib.rs b/keystore2/vpnprofilestore/lib.rs
index f92eacd..f5adc1b 100644
--- a/keystore2/vpnprofilestore/lib.rs
+++ b/keystore2/vpnprofilestore/lib.rs
@@ -39,6 +39,10 @@
         let mut db = Self {
             conn: Connection::open(db_file).context("Failed to initialize SQLite connection.")?,
         };
+
+        // On busy fail Immediately. It is unlikely to succeed given a bug in sqlite.
+        db.conn.busy_handler(None).context("Failed to set busy handler.")?;
+
         db.init_tables().context("Trying to initialize vpnstore db.")?;
         Ok(db)
     }
@@ -377,7 +381,12 @@
 mod db_test {
     use super::*;
     use keystore2_test_utils::TempDir;
+    use std::sync::Arc;
+    use std::thread;
+    use std::time::Duration;
+    use std::time::Instant;
 
+    static TEST_ALIAS: &str = &"test_alias";
     static TEST_BLOB1: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
     static TEST_BLOB2: &[u8] = &[2, 2, 3, 4, 5, 6, 7, 8, 9, 0];
     static TEST_BLOB3: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
@@ -440,4 +449,104 @@
             db.get(2, "test1").expect("Failed to get profile.").as_deref()
         );
     }
+
+    #[test]
+    fn concurrent_vpn_profile_test() -> Result<()> {
+        let temp_dir = Arc::new(
+            TempDir::new("concurrent_vpn_profile_test_").expect("Failed to create temp dir."),
+        );
+
+        let db_path = temp_dir.build().push("vpnprofile.sqlite").to_owned();
+
+        let test_begin = Instant::now();
+
+        let mut db = DB::new(&db_path).expect("Failed to open database.");
+        const PROFILE_COUNT: u32 = 5000u32;
+        const PROFILE_DB_COUNT: u32 = 5000u32;
+
+        let mut actual_profile_count = PROFILE_COUNT;
+        // First insert PROFILE_COUNT profiles.
+        for count in 0..PROFILE_COUNT {
+            if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
+                actual_profile_count = count;
+                break;
+            }
+            let alias = format!("test_alias_{}", count);
+            db.put(1, &alias, TEST_BLOB1).expect("Failed to add profile (1).");
+        }
+
+        // Insert more keys from a different thread and into a different namespace.
+        let db_path1 = db_path.clone();
+        let handle1 = thread::spawn(move || {
+            let mut db = DB::new(&db_path1).expect("Failed to open database.");
+
+            for count in 0..actual_profile_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let alias = format!("test_alias_{}", count);
+                db.put(2, &alias, TEST_BLOB2).expect("Failed to add profile (2).");
+            }
+
+            // Then delete them again.
+            for count in 0..actual_profile_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let alias = format!("test_alias_{}", count);
+                db.remove(2, &alias).expect("Remove Failed (2).");
+            }
+        });
+
+        // And start deleting the first set of profiles.
+        let db_path2 = db_path.clone();
+        let handle2 = thread::spawn(move || {
+            let mut db = DB::new(&db_path2).expect("Failed to open database.");
+
+            for count in 0..actual_profile_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let alias = format!("test_alias_{}", count);
+                db.remove(1, &alias).expect("Remove Failed (1)).");
+            }
+        });
+
+        // While a lot of inserting and deleting is going on we have to open database connections
+        // successfully and then insert and delete a specific profile.
+        let db_path3 = db_path.clone();
+        let handle3 = thread::spawn(move || {
+            for _count in 0..PROFILE_DB_COUNT {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let mut db = DB::new(&db_path3).expect("Failed to open database.");
+
+                db.put(3, &TEST_ALIAS, TEST_BLOB3).expect("Failed to add profile (3).");
+
+                db.remove(3, &TEST_ALIAS).expect("Remove failed (3).");
+            }
+        });
+
+        // While thread 3 is inserting and deleting TEST_ALIAS, we try to get the alias.
+        // This may yield an entry or none, but it must not fail.
+        let handle4 = thread::spawn(move || {
+            for _count in 0..PROFILE_DB_COUNT {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let mut db = DB::new(&db_path).expect("Failed to open database.");
+
+                // This may return Some or None but it must not fail.
+                db.get(3, &TEST_ALIAS).expect("Failed to get profile (4).");
+            }
+        });
+
+        handle1.join().expect("Thread 1 panicked.");
+        handle2.join().expect("Thread 2 panicked.");
+        handle3.join().expect("Thread 3 panicked.");
+        handle4.join().expect("Thread 4 panicked.");
+
+        Ok(())
+    }
 }