Revise database initialization.

Initializing the Keystore 2.0 database will now open the files
persistent.sql and perboot.sql. Tables are created, if they don't
exist, once per service startup instead of on every connection
instantiation. In the test situation we make use of the module
private visibility to create in-memory test databases or attaching
to a set of temporary files.

Test: keystore2_test
Bug: 159370859
Change-Id: I88594dabc72483779d980e81dbc05f2b7a687437
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index d64a26e..2ff5cf0 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -21,67 +21,61 @@
 #[cfg(not(test))]
 use rand::prelude::random;
 use rusqlite::{params, Connection, TransactionBehavior, NO_PARAMS};
+use std::sync::Once;
 #[cfg(test)]
 use tests::random;
 
+static INIT_TABLES: Once = Once::new();
+
 pub struct KeystoreDB {
     conn: Connection,
 }
 
 impl KeystoreDB {
-    // TODO(b/160882985): Figure out the location for this file.
-    #[cfg(not(test))]
-    pub fn new() -> Result<KeystoreDB> {
-        KeystoreDB::new_with_filename("persistent.sql")
+    pub fn new() -> Result<Self> {
+        let conn = Self::make_connection("file:persistent.sqlite", "file:perboot.sqlite")?;
+
+        INIT_TABLES.call_once(|| Self::init_tables(&conn).expect("Failed to initialize tables."));
+        Ok(Self { conn })
     }
 
-    #[cfg(test)]
-    pub fn new() -> Result<KeystoreDB> {
-        KeystoreDB::new_with_filename("")
-    }
-
-    fn new_with_filename(persistent_file: &str) -> Result<KeystoreDB> {
-        let db = KeystoreDB {
-            conn: Connection::open_in_memory()
-                .context("Failed to initialize sqlite connection.")?,
-        };
-        db.attach_databases(persistent_file).context("Failed to create KeystoreDB.")?;
-        db.init_tables().context("Failed to create KeystoreDB.")?;
-        Ok(db)
-    }
-
-    fn attach_databases(&self, persistent_file: &str) -> Result<()> {
-        self.conn
-            .execute("ATTACH DATABASE ? as 'persistent';", params![persistent_file])
-            .context("Failed to attach databases.")?;
-        Ok(())
-    }
-
-    fn init_tables(&self) -> Result<()> {
-        self.conn
-            .execute(
-                "CREATE TABLE IF NOT EXISTS persistent.keyentry (
+    fn init_tables(conn: &Connection) -> Result<()> {
+        conn.execute(
+            "CREATE TABLE IF NOT EXISTS persistent.keyentry (
                      id INTEGER UNIQUE,
                      creation_date DATETIME,
                      domain INTEGER,
                      namespace INTEGER,
                      alias TEXT);",
-                NO_PARAMS,
-            )
-            .context("Failed to initialize \"keyentry\" table.")?;
-        self.conn
-            .execute(
-                "CREATE TABLE IF NOT EXISTS persistent.keyparameter (
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"keyentry\" table.")?;
+
+        conn.execute(
+            "CREATE TABLE IF NOT EXISTS persistent.keyparameter (
                      keyentryid INTEGER,
                      tag INTEGER,
                      data ANY,
                      security_level INTEGER);",
-                NO_PARAMS,
-            )
-            .context("Failed to initialize \"keyparameter\" table.")?;
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"keyparameter\" table.")?;
+
         Ok(())
     }
 
+    fn make_connection(persistent_file: &str, perboot_file: &str) -> Result<Connection> {
+        let conn =
+            Connection::open_in_memory().context("Failed to initialize SQLite connection.")?;
+
+        conn.execute("ATTACH DATABASE ? as persistent;", params![persistent_file])
+            .context("Failed to attach database persistent.")?;
+        conn.execute("ATTACH DATABASE ? as perboot;", params![perboot_file])
+            .context("Failed to attach database perboot.")?;
+
+        Ok(conn)
+    }
+
     pub fn create_key_entry(&self, domain: aidl::Domain, namespace: i64) -> Result<i64> {
         match domain {
             aidl::Domain::App | aidl::Domain::SELinux => {}
@@ -165,6 +159,23 @@
     use super::*;
     use std::cell::RefCell;
 
+    static PERSISTENT_TEST_SQL: &str = "/data/local/tmp/persistent.sqlite";
+    static PERBOOT_TEST_SQL: &str = "/data/local/tmp/perboot.sqlite";
+
+    fn new_test_db() -> Result<KeystoreDB> {
+        let conn = KeystoreDB::make_connection("file::memory:", "file::memory:")?;
+
+        KeystoreDB::init_tables(&conn).context("Failed to initialize tables.")?;
+        Ok(KeystoreDB { conn })
+    }
+
+    fn new_test_db_with_persistent_file() -> Result<KeystoreDB> {
+        let conn = KeystoreDB::make_connection(PERSISTENT_TEST_SQL, PERBOOT_TEST_SQL)?;
+
+        KeystoreDB::init_tables(&conn).context("Failed to initialize tables.")?;
+        Ok(KeystoreDB { conn })
+    }
+
     // Ensure that we're using the "injected" random function, not the real one.
     #[test]
     fn test_mocked_random() {
@@ -179,17 +190,10 @@
         }
     }
 
-    // Ensure we can initialize the database.
-    #[test]
-    fn test_new() -> Result<()> {
-        KeystoreDB::new()?;
-        Ok(())
-    }
-
     // Test that we have the correct tables.
     #[test]
     fn test_tables() -> Result<()> {
-        let db = KeystoreDB::new()?;
+        let db = new_test_db()?;
         let tables = db
             .conn
             .prepare("SELECT name from persistent.sqlite_master WHERE type='table' ORDER BY name;")?
@@ -203,12 +207,12 @@
 
     #[test]
     fn test_no_persistence_for_tests() -> Result<()> {
-        let db = KeystoreDB::new()?;
+        let db = new_test_db()?;
 
         db.create_key_entry(aidl::Domain::App, 100)?;
         let entries = get_keyentry(&db)?;
         assert_eq!(entries.len(), 1);
-        let db = KeystoreDB::new()?;
+        let db = new_test_db()?;
 
         let entries = get_keyentry(&db)?;
         assert_eq!(entries.len(), 0);
@@ -217,13 +221,14 @@
 
     #[test]
     fn test_persistence_for_files() -> Result<()> {
-        let persistent = TempFile { filename: "/data/local/tmp/persistent.sql" };
-        let db = KeystoreDB::new_with_filename(persistent.filename)?;
+        let _file_guard_persistent = TempFile { filename: PERSISTENT_TEST_SQL };
+        let _file_guard_perboot = TempFile { filename: PERBOOT_TEST_SQL };
+        let db = new_test_db_with_persistent_file()?;
 
         db.create_key_entry(aidl::Domain::App, 100)?;
         let entries = get_keyentry(&db)?;
         assert_eq!(entries.len(), 1);
-        let db = KeystoreDB::new_with_filename(persistent.filename)?;
+        let db = new_test_db_with_persistent_file()?;
 
         let entries_new = get_keyentry(&db)?;
         assert_eq!(entries, entries_new);
@@ -238,7 +243,7 @@
             (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref())
         }
 
-        let db = KeystoreDB::new()?;
+        let db = new_test_db()?;
 
         db.create_key_entry(Domain::App, 100)?;
         db.create_key_entry(Domain::SELinux, 101)?;
@@ -273,7 +278,7 @@
             (ke.domain, ke.namespace, ke.alias.as_deref())
         }
 
-        let mut db = KeystoreDB::new()?;
+        let mut db = new_test_db()?;
         db.create_key_entry(Domain::App, 42)?;
         db.create_key_entry(Domain::App, 42)?;
         let entries = get_keyentry(&db)?;