Merge "BTAudio HAL: Added feature flag for DSA Over LEA" into main
diff --git a/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/ChannelSoudingRawData.aidl b/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/ChannelSoudingRawData.aidl
index e7fad4d..8fc77ae 100644
--- a/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/ChannelSoudingRawData.aidl
+++ b/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/ChannelSoudingRawData.aidl
@@ -35,6 +35,7 @@
 @VintfStability
 parcelable ChannelSoudingRawData {
   int procedureCounter;
+  int[] frequencyCompensation;
   boolean aborted;
   android.hardware.bluetooth.ranging.ChannelSoundingSingleSideData initiatorData;
   android.hardware.bluetooth.ranging.ChannelSoundingSingleSideData reflectorData;
diff --git a/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/ChannelSoundingSingleSideData.aidl b/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/ChannelSoundingSingleSideData.aidl
index 9fe85da..ddaba72 100644
--- a/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/ChannelSoundingSingleSideData.aidl
+++ b/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/ChannelSoundingSingleSideData.aidl
@@ -38,6 +38,7 @@
   @nullable byte[] packetQuality;
   @nullable byte[] packetRssiDbm;
   @nullable android.hardware.bluetooth.ranging.Nadm[] packetNadm;
+  @nullable int[] measuredFreqOffset;
   @nullable List<android.hardware.bluetooth.ranging.ComplexNumber> packetPct1;
   @nullable List<android.hardware.bluetooth.ranging.ComplexNumber> packetPct2;
   byte referencePowerDbm;
diff --git a/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/StepTonePct.aidl b/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/StepTonePct.aidl
index a51ba37..4125748 100644
--- a/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/StepTonePct.aidl
+++ b/bluetooth/ranging/aidl/aidl_api/android.hardware.bluetooth.ranging/current/android/hardware/bluetooth/ranging/StepTonePct.aidl
@@ -36,6 +36,7 @@
 parcelable StepTonePct {
   List<android.hardware.bluetooth.ranging.ComplexNumber> tonePcts;
   byte[] toneQualityIndicator;
+  byte toneExtensionAntennaIndex;
   const int TONE_QUALITY_GOOD = 0;
   const int TONE_QUALITY_MEDIUM = 1;
   const int TONE_QUALITY_LOW = 2;
@@ -44,4 +45,9 @@
   const int EXTENSION_SLOT_TONE_NOT_EXPECTED_TO_BE_PRESENT = 1;
   const int EXTENSION_SLOT_TONE_EXPECTED_TO_BE_PRESENT = 2;
   const int EXTENSION_SLOT_SHIFT_AMOUNT = 4;
+  const byte TONE_EXTENSION_ANTENNA_1 = 0x0;
+  const byte TONE_EXTENSION_ANTENNA_2 = 0x1;
+  const byte TONE_EXTENSION_ANTENNA_3 = 0x2;
+  const byte TONE_EXTENSION_ANTENNA_4 = 0x3;
+  const byte TONE_EXTENSION_UNUSED = 0xFFu8;
 }
diff --git a/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/ChannelSoudingRawData.aidl b/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/ChannelSoudingRawData.aidl
index 3c8a62f..0106865 100644
--- a/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/ChannelSoudingRawData.aidl
+++ b/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/ChannelSoudingRawData.aidl
@@ -29,6 +29,11 @@
      */
     int procedureCounter;
     /**
+     * Frequency Compensation indicates fractional frequency
+     * offset (FFO) value of initiator, in 0.01ppm
+     */
+    int[] frequencyCompensation;
+    /**
      * Indicate if the procedure aborted.
      */
     boolean aborted;
diff --git a/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/ChannelSoundingSingleSideData.aidl b/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/ChannelSoundingSingleSideData.aidl
index 2c3f201..942fc0d 100644
--- a/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/ChannelSoundingSingleSideData.aidl
+++ b/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/ChannelSoundingSingleSideData.aidl
@@ -42,6 +42,10 @@
      */
     @nullable Nadm[] packetNadm;
     /**
+     * Measured Frequency Offset from mode 0, relative to the remote device, in 0.01ppm
+     */
+    @nullable int[] measuredFreqOffset;
+    /**
      * Packet_PCT1 or packet_PCT2 of mode-1 or mode-3, if sounding sequence is used and sounding
      * phase-based ranging is supported.
      */
diff --git a/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/StepTonePct.aidl b/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/StepTonePct.aidl
index 99c6d65..4650861 100644
--- a/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/StepTonePct.aidl
+++ b/bluetooth/ranging/aidl/android/hardware/bluetooth/ranging/StepTonePct.aidl
@@ -23,6 +23,10 @@
  */
 @VintfStability
 parcelable StepTonePct {
+    /**
+     * PCT measured from mode-2 or mode-3 steps
+     * (in ascending order of antenna position with tone extension data at the end).
+     */
     List<ComplexNumber> tonePcts;
     const int TONE_QUALITY_GOOD = 0;
     const int TONE_QUALITY_MEDIUM = 1;
@@ -52,4 +56,20 @@
      * See: https://bluetooth.com/specifications/specs/channel-sounding-cr-pr/
      */
     byte[] toneQualityIndicator;
+
+    const byte TONE_EXTENSION_ANTENNA_1 = 0x0;
+    const byte TONE_EXTENSION_ANTENNA_2 = 0x1;
+    const byte TONE_EXTENSION_ANTENNA_3 = 0x2;
+    const byte TONE_EXTENSION_ANTENNA_4 = 0x3;
+    const byte TONE_EXTENSION_UNUSED = 0xFFu8;
+    /**
+     * Tone Extension Antenna Index indicates the Antenna position used in tone extension slot
+     *
+     * 0x00 = A1
+     * 0x01 = A2
+     * 0x02 = A3
+     * 0x03 = A4
+     * 0xFF = Tone extension not used
+     */
+    byte toneExtensionAntennaIndex;
 }
diff --git a/security/secretkeeper/aidl/vts/Android.bp b/security/secretkeeper/aidl/vts/Android.bp
index c130a3a..7fc7a70 100644
--- a/security/secretkeeper/aidl/vts/Android.bp
+++ b/security/secretkeeper/aidl/vts/Android.bp
@@ -25,6 +25,7 @@
         "general-tests",
         "vts",
     ],
+    test_config: "AndroidTest.xml",
     rustlibs: [
         "libsecretkeeper_comm_nostd",
         "libsecretkeeper_core_nostd",
@@ -36,6 +37,7 @@
         "libbinder_rs",
         "libcoset",
         "liblog_rust",
+        "liblogger",
     ],
     require_root: true,
 }
diff --git a/security/secretkeeper/aidl/vts/AndroidTest.xml b/security/secretkeeper/aidl/vts/AndroidTest.xml
new file mode 100644
index 0000000..4fee78f
--- /dev/null
+++ b/security/secretkeeper/aidl/vts/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<configuration description="Config for Secretkeeper VTS tests.">
+  <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+    <option name="push-file" key="VtsSecretkeeperTargetTest" value="/data/local/tmp/VtsSecretkeeperTargetTest" />
+  </target_preparer>
+
+  <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+    <option name="test-device-path" value="/data/local/tmp" />
+    <option name="module-name" value="VtsSecretkeeperTargetTest" />
+    <!-- Rust tests are run in parallel by default. Run these ones
+         single-threaded, so that one test's secrets don't affect
+         the behaviour of a different test. -->
+    <option name="native-test-flag" value="--test-threads=1" />
+  </test>
+</configuration>
diff --git a/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs b/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs
index b5cca27..6a70d02 100644
--- a/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs
+++ b/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs
@@ -14,27 +14,28 @@
  * limitations under the License.
  */
 
-#[cfg(test)]
+#![cfg(test)]
+
+use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::ISecretkeeper;
+use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::SecretId::SecretId;
+use authgraph_vts_test as ag_vts;
+use authgraph_boringssl as boring;
+use authgraph_core::key;
 use binder::StatusCode;
 use coset::{CborSerializable, CoseEncrypt0};
-use log::warn;
+use log::{info, warn};
 use secretkeeper_core::cipher;
 use secretkeeper_comm::data_types::error::SecretkeeperError;
 use secretkeeper_comm::data_types::request::Request;
 use secretkeeper_comm::data_types::request_response_impl::{
     GetVersionRequest, GetVersionResponse, GetSecretRequest, GetSecretResponse, StoreSecretRequest,
     StoreSecretResponse };
-use secretkeeper_comm::data_types::{Id, ID_SIZE, Secret, SECRET_SIZE};
+use secretkeeper_comm::data_types::{Id, Secret};
 use secretkeeper_comm::data_types::response::Response;
 use secretkeeper_comm::data_types::packet::{ResponsePacket, ResponseType};
-use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::ISecretkeeper;
-use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::SecretId::SecretId;
-use authgraph_vts_test as ag_vts;
-use authgraph_boringssl as boring;
-use authgraph_core::key;
 
-const SECRETKEEPER_IDENTIFIER: &str =
-    "android.hardware.security.secretkeeper.ISecretkeeper/nonsecure";
+const SECRETKEEPER_SERVICE: &str = "android.hardware.security.secretkeeper.ISecretkeeper";
+const SECRETKEEPER_INSTANCES: [&'static str; 2] = ["nonsecure", "default"];
 const CURRENT_VERSION: u64 = 1;
 
 // TODO(b/291238565): This will change once libdice_policy switches to Explicit-key DiceCertChain
@@ -47,38 +48,62 @@
 ];
 
 // Random bytes (of ID_SIZE/SECRET_SIZE) generated for tests.
-const ID_EXAMPLE: [u8; ID_SIZE] = [
+const ID_EXAMPLE: Id = Id([
     0xF1, 0xB2, 0xED, 0x3B, 0xD1, 0xBD, 0xF0, 0x7D, 0xE1, 0xF0, 0x01, 0xFC, 0x61, 0x71, 0xD3, 0x42,
     0xE5, 0x8A, 0xAF, 0x33, 0x6C, 0x11, 0xDC, 0xC8, 0x6F, 0xAE, 0x12, 0x5C, 0x26, 0x44, 0x6B, 0x86,
     0xCC, 0x24, 0xFD, 0xBF, 0x91, 0x4A, 0x54, 0x84, 0xF9, 0x01, 0x59, 0x25, 0x70, 0x89, 0x38, 0x8D,
     0x5E, 0xE6, 0x91, 0xDF, 0x68, 0x60, 0x69, 0x26, 0xBE, 0xFE, 0x79, 0x58, 0xF7, 0xEA, 0x81, 0x7D,
-];
-const ID_EXAMPLE_2: [u8; ID_SIZE] = [
+]);
+const ID_EXAMPLE_2: Id = Id([
     0x6A, 0xCC, 0xB1, 0xEB, 0xBB, 0xAB, 0xE3, 0xEA, 0x44, 0xBD, 0xDC, 0x75, 0x75, 0x7D, 0xC0, 0xE5,
     0xC7, 0x86, 0x41, 0x56, 0x39, 0x66, 0x96, 0x10, 0xCB, 0x43, 0x10, 0x79, 0x03, 0xDC, 0xE6, 0x9F,
     0x12, 0x2B, 0xEF, 0x28, 0x9C, 0x1E, 0x32, 0x46, 0x5F, 0xA3, 0xE7, 0x8D, 0x53, 0x63, 0xE8, 0x30,
     0x5A, 0x17, 0x6F, 0xEF, 0x42, 0xD6, 0x58, 0x7A, 0xF0, 0xCB, 0xD4, 0x40, 0x58, 0x96, 0x32, 0xF4,
-];
-const ID_NOT_STORED: [u8; ID_SIZE] = [
+]);
+const ID_NOT_STORED: Id = Id([
     0x56, 0xD0, 0x4E, 0xAA, 0xC1, 0x7B, 0x55, 0x6B, 0xA0, 0x2C, 0x65, 0x43, 0x39, 0x0A, 0x6C, 0xE9,
     0x1F, 0xD0, 0x0E, 0x20, 0x3E, 0xFB, 0xF5, 0xF9, 0x3F, 0x5B, 0x11, 0x1B, 0x18, 0x73, 0xF6, 0xBB,
     0xAB, 0x9F, 0xF2, 0xD6, 0xBD, 0xBA, 0x25, 0x68, 0x22, 0x30, 0xF2, 0x1F, 0x90, 0x05, 0xF3, 0x64,
     0xE7, 0xEF, 0xC6, 0xB6, 0xA0, 0x85, 0xC9, 0x40, 0x40, 0xF0, 0xB4, 0xB9, 0xD8, 0x28, 0xEE, 0x9C,
-];
-const SECRET_EXAMPLE: [u8; SECRET_SIZE] = [
+]);
+const SECRET_EXAMPLE: Secret = Secret([
     0xA9, 0x89, 0x97, 0xFE, 0xAE, 0x97, 0x55, 0x4B, 0x32, 0x35, 0xF0, 0xE8, 0x93, 0xDA, 0xEA, 0x24,
     0x06, 0xAC, 0x36, 0x8B, 0x3C, 0x95, 0x50, 0x16, 0x67, 0x71, 0x65, 0x26, 0xEB, 0xD0, 0xC3, 0x98,
-];
+]);
 
-fn get_connection() -> Option<binder::Strong<dyn ISecretkeeper>> {
-    match binder::get_interface(SECRETKEEPER_IDENTIFIER) {
-        Ok(sk) => Some(sk),
-        Err(StatusCode::NAME_NOT_FOUND) => None,
-        Err(e) => {
-            panic!(
-                "unexpected error while fetching connection to Secretkeeper {:?}",
-                e
-            );
+fn get_connection() -> Option<(binder::Strong<dyn ISecretkeeper>, String)> {
+    // Initialize logging (which is OK to call multiple times).
+    logger::init(logger::Config::default().with_min_level(log::Level::Debug));
+
+    // TODO: replace this with a parameterized set of tests that run for each available instance of
+    // ISecretkeeper (rather than having a fixed set of instance names to look for).
+    for instance in &SECRETKEEPER_INSTANCES {
+        let name = format!("{SECRETKEEPER_SERVICE}/{instance}");
+        match binder::get_interface(&name) {
+            Ok(sk) => {
+                info!("Running test against /{instance}");
+                return Some((sk, name));
+            }
+            Err(StatusCode::NAME_NOT_FOUND) => {
+                info!("No /{instance} instance of ISecretkeeper present");
+            }
+            Err(e) => {
+                panic!("unexpected error while fetching connection to Secretkeeper {:?}", e);
+            }
+        }
+    }
+    None
+}
+
+/// Macro to perform test setup. Invokes `return` if no Secretkeeper instance available.
+macro_rules! setup_client {
+    {} => {
+        match SkClient::new() {
+            Some(sk) => sk,
+            None => {
+                warn!("Secretkeeper HAL is unavailable, skipping test");
+                return;
+            }
         }
     }
 }
@@ -86,42 +111,86 @@
 /// Secretkeeper client information.
 struct SkClient {
     sk: binder::Strong<dyn ISecretkeeper>,
+    name: String,
     aes_keys: [key::AesKey; 2],
     session_id: Vec<u8>,
 }
 
+impl Drop for SkClient {
+    fn drop(&mut self) {
+        // Delete any IDs that may be left over.
+        self.delete(&[&ID_EXAMPLE, &ID_EXAMPLE_2]);
+    }
+}
+
 impl SkClient {
     fn new() -> Option<Self> {
-        let sk = get_connection()?;
+        let (sk, name) = get_connection()?;
         let (aes_keys, session_id) = authgraph_key_exchange(sk.clone());
-        Some(Self {
-            sk,
-            aes_keys,
-            session_id,
-        })
+        Some(Self { sk, name, aes_keys, session_id })
     }
+
     /// Wrapper around `ISecretkeeper::processSecretManagementRequest` that handles
     /// encryption and decryption.
     fn secret_management_request(&self, req_data: &[u8]) -> Vec<u8> {
         let aes_gcm = boring::BoringAes;
         let rng = boring::BoringRng;
-        let request_bytes = cipher::encrypt_message(
-            &aes_gcm,
-            &rng,
-            &self.aes_keys[0],
-            &self.session_id,
-            &req_data,
-        )
-        .unwrap();
+        let request_bytes =
+            cipher::encrypt_message(&aes_gcm, &rng, &self.aes_keys[0], &self.session_id, &req_data)
+                .unwrap();
 
-        let response_bytes = self
-            .sk
-            .processSecretManagementRequest(&request_bytes)
-            .unwrap();
+        let response_bytes = self.sk.processSecretManagementRequest(&request_bytes).unwrap();
 
         let response_encrypt0 = CoseEncrypt0::from_slice(&response_bytes).unwrap();
         cipher::decrypt_message(&aes_gcm, &self.aes_keys[1], &response_encrypt0).unwrap()
     }
+
+    /// Helper method to store a secret.
+    fn store(&self, id: &Id, secret: &Secret) {
+        let store_request = StoreSecretRequest {
+            id: id.clone(),
+            secret: secret.clone(),
+            sealing_policy: HYPOTHETICAL_DICE_POLICY.to_vec(),
+        };
+        let store_request = store_request.serialize_to_packet().to_vec().unwrap();
+
+        let store_response = self.secret_management_request(&store_request);
+        let store_response = ResponsePacket::from_slice(&store_response).unwrap();
+
+        assert_eq!(store_response.response_type().unwrap(), ResponseType::Success);
+        // Really just checking that the response is indeed StoreSecretResponse
+        let _ = StoreSecretResponse::deserialize_from_packet(store_response).unwrap();
+    }
+
+    /// Helper method to get a secret.
+    fn get(&self, id: &Id) -> Option<Secret> {
+        let get_request = GetSecretRequest { id: id.clone(), updated_sealing_policy: None };
+        let get_request = get_request.serialize_to_packet().to_vec().unwrap();
+
+        let get_response = self.secret_management_request(&get_request);
+        let get_response = ResponsePacket::from_slice(&get_response).unwrap();
+
+        if get_response.response_type().unwrap() == ResponseType::Success {
+            let get_response = *GetSecretResponse::deserialize_from_packet(get_response).unwrap();
+            Some(Secret(get_response.secret.0))
+        } else {
+            // Only expect a not-found failure.
+            let err = *SecretkeeperError::deserialize_from_packet(get_response).unwrap();
+            assert_eq!(err, SecretkeeperError::EntryNotFound);
+            None
+        }
+    }
+
+    /// Helper method to delete secrets.
+    fn delete(&self, ids: &[&Id]) {
+        let ids: Vec<SecretId> = ids.iter().map(|id| SecretId { id: id.0.to_vec() }).collect();
+        self.sk.deleteIds(&ids).unwrap();
+    }
+
+    /// Helper method to delete everything.
+    fn delete_all(&self) {
+        self.sk.deleteAll().unwrap();
+    }
 }
 
 /// Perform AuthGraph key exchange, returning the session keys and session ID.
@@ -135,7 +204,7 @@
 /// mainline key exchange against a local source implementation.
 #[test]
 fn authgraph_mainline() {
-    let sk = match get_connection() {
+    let (sk, _) = match get_connection() {
         Some(sk) => sk,
         None => {
             warn!("Secretkeeper HAL is unavailable, skipping test");
@@ -149,7 +218,7 @@
 /// a corrupted session ID signature.
 #[test]
 fn authgraph_corrupt_sig() {
-    let sk = match get_connection() {
+    let (sk, _) = match get_connection() {
         Some(sk) => sk,
         None => {
             warn!("Secretkeeper HAL is unavailable, skipping test");
@@ -165,7 +234,7 @@
 /// when corrupted keys are returned to it.
 #[test]
 fn authgraph_corrupt_keys() {
-    let sk = match get_connection() {
+    let (sk, _) = match get_connection() {
         Some(sk) => sk,
         None => {
             warn!("Secretkeeper HAL is unavailable, skipping test");
@@ -182,13 +251,7 @@
 
 #[test]
 fn secret_management_get_version() {
-    let sk_client = match SkClient::new() {
-        Some(sk) => sk,
-        None => {
-            warn!("Secretkeeper HAL is unavailable, skipping test");
-            return;
-        }
-    };
+    let sk_client = setup_client!();
 
     let request = GetVersionRequest {};
     let request_packet = request.serialize_to_packet();
@@ -197,10 +260,7 @@
     let response_bytes = sk_client.secret_management_request(&request_bytes);
 
     let response_packet = ResponsePacket::from_slice(&response_bytes).unwrap();
-    assert_eq!(
-        response_packet.response_type().unwrap(),
-        ResponseType::Success
-    );
+    assert_eq!(response_packet.response_type().unwrap(), ResponseType::Success);
     let get_version_response =
         *GetVersionResponse::deserialize_from_packet(response_packet).unwrap();
     assert_eq!(get_version_response.version, CURRENT_VERSION);
@@ -208,13 +268,7 @@
 
 #[test]
 fn secret_management_malformed_request() {
-    let sk_client = match SkClient::new() {
-        Some(sk) => sk,
-        None => {
-            warn!("Secretkeeper HAL is unavailable, skipping test");
-            return;
-        }
-    };
+    let sk_client = setup_client!();
 
     let request = GetVersionRequest {};
     let request_packet = request.serialize_to_packet();
@@ -226,268 +280,112 @@
     let response_bytes = sk_client.secret_management_request(&request_bytes);
 
     let response_packet = ResponsePacket::from_slice(&response_bytes).unwrap();
-    assert_eq!(
-        response_packet.response_type().unwrap(),
-        ResponseType::Error
-    );
+    assert_eq!(response_packet.response_type().unwrap(), ResponseType::Error);
     let err = *SecretkeeperError::deserialize_from_packet(response_packet).unwrap();
     assert_eq!(err, SecretkeeperError::RequestMalformed);
 }
 
 #[test]
 fn secret_management_store_get_secret_found() {
-    let sk_client = match SkClient::new() {
-        Some(sk) => sk,
-        None => {
-            warn!("Secretkeeper HAL is unavailable, skipping test");
-            return;
-        }
-    };
+    let sk_client = setup_client!();
 
-    let store_request = StoreSecretRequest {
-        id: Id(ID_EXAMPLE),
-        secret: Secret(SECRET_EXAMPLE),
-        sealing_policy: HYPOTHETICAL_DICE_POLICY.to_vec(),
-    };
-
-    let store_request = store_request.serialize_to_packet().to_vec().unwrap();
-
-    let store_response = sk_client.secret_management_request(&store_request);
-    let store_response = ResponsePacket::from_slice(&store_response).unwrap();
-
-    assert_eq!(
-        store_response.response_type().unwrap(),
-        ResponseType::Success
-    );
-    // Really just checking that the response is indeed StoreSecretResponse
-    let _ = StoreSecretResponse::deserialize_from_packet(store_response).unwrap();
+    sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE);
 
     // Get the secret that was just stored
-    let get_request = GetSecretRequest {
-        id: Id(ID_EXAMPLE),
-        updated_sealing_policy: None,
-    };
-    let get_request = get_request.serialize_to_packet().to_vec().unwrap();
-
-    let get_response = sk_client.secret_management_request(&get_request);
-    let get_response = ResponsePacket::from_slice(&get_response).unwrap();
-    assert_eq!(get_response.response_type().unwrap(), ResponseType::Success);
-    let get_response = *GetSecretResponse::deserialize_from_packet(get_response).unwrap();
-    assert_eq!(get_response.secret.0, SECRET_EXAMPLE);
+    assert_eq!(sk_client.get(&ID_EXAMPLE), Some(SECRET_EXAMPLE));
 }
 
 #[test]
 fn secret_management_store_get_secret_not_found() {
-    let sk_client = match SkClient::new() {
-        Some(sk) => sk,
-        None => {
-            warn!("Secretkeeper HAL is unavailable, skipping test");
-            return;
-        }
-    };
+    let sk_client = setup_client!();
 
     // Store a secret (corresponding to an id).
-    let store_request = StoreSecretRequest {
-        id: Id(ID_EXAMPLE),
-        secret: Secret(SECRET_EXAMPLE),
-        sealing_policy: HYPOTHETICAL_DICE_POLICY.to_vec(),
-    };
-
-    let store_request = store_request.serialize_to_packet().to_vec().unwrap();
-    let store_response = sk_client.secret_management_request(&store_request);
-    let store_response = ResponsePacket::from_slice(&store_response).unwrap();
-
-    assert_eq!(
-        store_response.response_type().unwrap(),
-        ResponseType::Success
-    );
-    // Really just checking that the response is indeed StoreSecretResponse
-    let _ = StoreSecretResponse::deserialize_from_packet(store_response).unwrap();
+    sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE);
 
     // Get the secret that was never stored
-    let get_request = GetSecretRequest {
-        id: Id(ID_NOT_STORED),
-        updated_sealing_policy: None,
-    };
-    let get_request = get_request.serialize_to_packet().to_vec().unwrap();
-
-    let get_response = sk_client.secret_management_request(&get_request);
-
-    // Expect the entry not to be found.
-    let get_response = ResponsePacket::from_slice(&get_response).unwrap();
-    assert_eq!(get_response.response_type().unwrap(), ResponseType::Error);
-    let err = *SecretkeeperError::deserialize_from_packet(get_response).unwrap();
-    assert_eq!(err, SecretkeeperError::EntryNotFound);
+    assert_eq!(sk_client.get(&ID_NOT_STORED), None);
 }
 
 #[test]
 fn secretkeeper_store_delete_ids() {
-    let sk_client = match SkClient::new() {
-        Some(sk) => sk,
-        None => {
-            warn!("Secretkeeper HAL is unavailable, skipping test");
-            return;
-        }
-    };
+    let sk_client = setup_client!();
 
-    let store_request = StoreSecretRequest {
-        id: Id(ID_EXAMPLE),
-        secret: Secret(SECRET_EXAMPLE),
-        sealing_policy: HYPOTHETICAL_DICE_POLICY.to_vec(),
-    };
+    sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE);
+    sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE);
+    sk_client.delete(&[&ID_EXAMPLE]);
 
-    let store_request = store_request.serialize_to_packet().to_vec().unwrap();
-    let store_response = sk_client.secret_management_request(&store_request);
-    let store_response = ResponsePacket::from_slice(&store_response).unwrap();
+    assert_eq!(sk_client.get(&ID_EXAMPLE), None);
+    assert_eq!(sk_client.get(&ID_EXAMPLE_2), Some(SECRET_EXAMPLE));
 
-    assert_eq!(
-        store_response.response_type().unwrap(),
-        ResponseType::Success
-    );
-    // Really just checking that the response is indeed StoreSecretResponse
-    let _ = StoreSecretResponse::deserialize_from_packet(store_response).unwrap();
+    sk_client.delete(&[&ID_EXAMPLE_2]);
 
-    sk_client
-        .sk
-        .deleteIds(&[SecretId {
-            id: ID_EXAMPLE.to_vec(),
-        }])
-        .unwrap();
+    assert_eq!(sk_client.get(&ID_EXAMPLE), None);
+    assert_eq!(sk_client.get(&ID_EXAMPLE_2), None);
+}
 
-    // Try to get the secret that was just stored & deleted
-    let get_request = GetSecretRequest {
-        id: Id(ID_EXAMPLE),
-        updated_sealing_policy: None,
-    };
-    let get_request = get_request.serialize_to_packet().to_vec().unwrap();
+#[test]
+fn secretkeeper_store_delete_multiple_ids() {
+    let sk_client = setup_client!();
 
-    let get_response = sk_client.secret_management_request(&get_request);
+    sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE);
+    sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE);
+    sk_client.delete(&[&ID_EXAMPLE, &ID_EXAMPLE_2]);
 
-    // Expect the entry not to be found.
-    let get_response = ResponsePacket::from_slice(&get_response).unwrap();
-    assert_eq!(get_response.response_type().unwrap(), ResponseType::Error);
-    let err = *SecretkeeperError::deserialize_from_packet(get_response).unwrap();
-    assert_eq!(err, SecretkeeperError::EntryNotFound);
+    assert_eq!(sk_client.get(&ID_EXAMPLE), None);
+    assert_eq!(sk_client.get(&ID_EXAMPLE_2), None);
+}
+
+#[test]
+fn secretkeeper_store_delete_duplicate_ids() {
+    let sk_client = setup_client!();
+
+    sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE);
+    sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE);
+    // Delete the same secret twice.
+    sk_client.delete(&[&ID_EXAMPLE, &ID_EXAMPLE]);
+
+    assert_eq!(sk_client.get(&ID_EXAMPLE), None);
+    assert_eq!(sk_client.get(&ID_EXAMPLE_2), Some(SECRET_EXAMPLE));
+}
+
+#[test]
+fn secretkeeper_store_delete_nonexistent() {
+    let sk_client = setup_client!();
+
+    sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE);
+    sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE);
+    sk_client.delete(&[&ID_NOT_STORED]);
+
+    assert_eq!(sk_client.get(&ID_EXAMPLE), Some(SECRET_EXAMPLE));
+    assert_eq!(sk_client.get(&ID_EXAMPLE_2), Some(SECRET_EXAMPLE));
+    assert_eq!(sk_client.get(&ID_NOT_STORED), None);
 }
 
 #[test]
 fn secretkeeper_store_delete_all() {
-    let sk_client = match SkClient::new() {
-        Some(sk) => sk,
-        None => {
-            warn!("Secretkeeper HAL is unavailable, skipping test");
-            return;
-        }
-    };
+    let sk_client = setup_client!();
 
-    let store_request = StoreSecretRequest {
-        id: Id(ID_EXAMPLE),
-        secret: Secret(SECRET_EXAMPLE),
-        sealing_policy: HYPOTHETICAL_DICE_POLICY.to_vec(),
-    };
+    if sk_client.name != "nonsecure" {
+        // Don't run deleteAll() on a secure device, as it might affect
+        // real secrets.
+        warn!("skipping deleteAll test due to real impl");
+        return;
+    }
 
-    let store_request = store_request.serialize_to_packet().to_vec().unwrap();
-    let store_response = sk_client.secret_management_request(&store_request);
-    let store_response = ResponsePacket::from_slice(&store_response).unwrap();
+    sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE);
+    sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE);
 
-    assert_eq!(
-        store_response.response_type().unwrap(),
-        ResponseType::Success
-    );
-    // Really just checking that the response is indeed StoreSecretResponse
-    let _ = StoreSecretResponse::deserialize_from_packet(store_response).unwrap();
+    sk_client.delete_all();
 
-    let store_request = StoreSecretRequest {
-        id: Id(ID_EXAMPLE_2),
-        secret: Secret(SECRET_EXAMPLE),
-        sealing_policy: HYPOTHETICAL_DICE_POLICY.to_vec(),
-    };
-
-    let store_request = store_request.serialize_to_packet().to_vec().unwrap();
-    let store_response = sk_client.secret_management_request(&store_request);
-    let store_response = ResponsePacket::from_slice(&store_response).unwrap();
-
-    assert_eq!(
-        store_response.response_type().unwrap(),
-        ResponseType::Success
-    );
-    // Really just checking that the response is indeed StoreSecretResponse
-    let _ = StoreSecretResponse::deserialize_from_packet(store_response).unwrap();
-
-    sk_client.sk.deleteAll().unwrap();
-
-    // Get the first secret that was just stored before deleteAll
-    let get_request = GetSecretRequest {
-        id: Id(ID_EXAMPLE),
-        updated_sealing_policy: None,
-    };
-    let get_request = get_request.serialize_to_packet().to_vec().unwrap();
-
-    let get_response = sk_client.secret_management_request(&get_request);
-
-    // Expect the entry not to be found.
-    let get_response = ResponsePacket::from_slice(&get_response).unwrap();
-    assert_eq!(get_response.response_type().unwrap(), ResponseType::Error);
-    let err = *SecretkeeperError::deserialize_from_packet(get_response).unwrap();
-    assert_eq!(err, SecretkeeperError::EntryNotFound);
-
-    // Get the second secret that was just stored before deleteAll
-    let get_request = GetSecretRequest {
-        id: Id(ID_EXAMPLE_2),
-        updated_sealing_policy: None,
-    };
-    let get_request = get_request.serialize_to_packet().to_vec().unwrap();
-
-    let get_response = sk_client.secret_management_request(&get_request);
-
-    // Expect the entry not to be found.
-    let get_response = ResponsePacket::from_slice(&get_response).unwrap();
-    assert_eq!(get_response.response_type().unwrap(), ResponseType::Error);
-    let err = *SecretkeeperError::deserialize_from_packet(get_response).unwrap();
-    assert_eq!(err, SecretkeeperError::EntryNotFound);
+    assert_eq!(sk_client.get(&ID_EXAMPLE), None);
+    assert_eq!(sk_client.get(&ID_EXAMPLE_2), None);
 
     // Store a new secret (corresponding to an id).
-    let store_request = StoreSecretRequest {
-        id: Id(ID_EXAMPLE),
-        secret: Secret(SECRET_EXAMPLE),
-        sealing_policy: HYPOTHETICAL_DICE_POLICY.to_vec(),
-    };
-
-    let store_request = store_request.serialize_to_packet().to_vec().unwrap();
-    let store_response = sk_client.secret_management_request(&store_request);
-    let store_response = ResponsePacket::from_slice(&store_response).unwrap();
-
-    assert_eq!(
-        store_response.response_type().unwrap(),
-        ResponseType::Success
-    );
+    sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE);
 
     // Get the restored secret.
-    let get_request = GetSecretRequest {
-        id: Id(ID_EXAMPLE),
-        updated_sealing_policy: None,
-    };
-    let get_request = get_request.serialize_to_packet().to_vec().unwrap();
-
-    let get_response = sk_client.secret_management_request(&get_request);
-    let get_response = ResponsePacket::from_slice(&get_response).unwrap();
-
-    // Get the secret that was just re-stored.
-    assert_eq!(get_response.response_type().unwrap(), ResponseType::Success);
-    let get_response = *GetSecretResponse::deserialize_from_packet(get_response).unwrap();
-    assert_eq!(get_response.secret.0, SECRET_EXAMPLE);
+    assert_eq!(sk_client.get(&ID_EXAMPLE), Some(SECRET_EXAMPLE));
 
     // (Try to) Get the secret that was never stored
-    let get_request = GetSecretRequest {
-        id: Id(ID_NOT_STORED),
-        updated_sealing_policy: None,
-    };
-    let get_request = get_request.serialize_to_packet().to_vec().unwrap();
-    let get_response = sk_client.secret_management_request(&get_request);
-
-    // Check that response is `SecretkeeperError::EntryNotFound`
-    let get_response = ResponsePacket::from_slice(&get_response).unwrap();
-    assert_eq!(get_response.response_type().unwrap(), ResponseType::Error);
-    let err = *SecretkeeperError::deserialize_from_packet(get_response).unwrap();
-    assert_eq!(err, SecretkeeperError::EntryNotFound);
+    assert_eq!(sk_client.get(&ID_NOT_STORED), None);
 }