Hearing Aid: codec and connection interval switching

Added support to switch the LE Connection Interval between 10 msec and
20 msec. Uses the "persist.bluetooth.hearingaid.interval" to configure.

Bug: 116317072
Bug: 116044083
Test: Manually use hearing aid devices and switches between 10 msec and
20 msec connection interval
Bug: 79579786
Change-Id: Iec15a0c9f76afe265368fd90662ffe60a29fb27b
Merged-In: I982002ac9a41969cda5dec73332b27b3142d7426
diff --git a/bta/hearing_aid/hearing_aid.cc b/bta/hearing_aid/hearing_aid.cc
index 4fc416e..e3c6ad2 100644
--- a/bta/hearing_aid/hearing_aid.cc
+++ b/bta/hearing_aid/hearing_aid.cc
@@ -25,6 +25,7 @@
 #include "embdrv/g722/g722_enc_dec.h"
 #include "gap_api.h"
 #include "gatt_api.h"
+#include "osi/include/properties.h"
 
 #include <base/bind.h>
 #include <base/logging.h>
@@ -36,6 +37,13 @@
 using bluetooth::Uuid;
 using bluetooth::hearing_aid::ConnectionState;
 
+// The MIN_CE_LEN parameter for Connection Parameters based on the current
+// Connection Interval
+constexpr uint16_t MIN_CE_LEN_10MS_CI = 0x0006;
+constexpr uint16_t MIN_CE_LEN_20MS_CI = 0x000C;
+constexpr uint16_t CONNECTION_INTERVAL_10MS_PARAM = 0x0008;
+constexpr uint16_t CONNECTION_INTERVAL_20MS_PARAM = 0x0010;
+
 void btif_storage_add_hearing_aid(const RawAddress& address, uint16_t psm,
                                   uint8_t capabilities, uint16_t codecs,
                                   uint16_t audio_control_point_handle,
@@ -70,8 +78,6 @@
 Uuid LE_PSM_UUID               = Uuid::FromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
 // clang-format on
 
-constexpr uint16_t MIN_CE_LEN_1M = 0x0006;
-
 void hearingaid_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
 void encryption_callback(const RawAddress*, tGATT_TRANSPORT, void*,
                          tBTM_STATUS);
@@ -252,8 +258,20 @@
       : gatt_if(0),
         seq_counter(0),
         current_volume(VOLUME_UNKNOWN),
-        callbacks(callbacks) {
-    DVLOG(2) << __func__;
+        callbacks(callbacks),
+        codec_in_use(0) {
+    default_data_interval_ms = (uint16_t)osi_property_get_int32(
+        "persist.bluetooth.hearingaid.interval", (int32_t)HA_INTERVAL_20_MS);
+    if ((default_data_interval_ms != HA_INTERVAL_10_MS) &&
+        (default_data_interval_ms != HA_INTERVAL_20_MS)) {
+      LOG(ERROR) << __func__
+                 << ": invalid interval=" << default_data_interval_ms
+                 << "ms. Overwriting back to default";
+      default_data_interval_ms = HA_INTERVAL_20_MS;
+    }
+    VLOG(2) << __func__
+            << ", default_data_interval_ms=" << default_data_interval_ms;
+
     BTA_GATTC_AppRegister(
         hearingaid_gattc_callback,
         base::Bind(
@@ -269,6 +287,31 @@
             initCb));
   }
 
+  void UpdateBleConnParams(const RawAddress& address) {
+    /* List of parameters that depends on the chosen Connection Interval */
+    uint16_t min_ce_len;
+    uint16_t connection_interval;
+
+    switch (default_data_interval_ms) {
+      case HA_INTERVAL_10_MS:
+        min_ce_len = MIN_CE_LEN_10MS_CI;
+        connection_interval = CONNECTION_INTERVAL_10MS_PARAM;
+        break;
+      case HA_INTERVAL_20_MS:
+        min_ce_len = MIN_CE_LEN_20MS_CI;
+        connection_interval = CONNECTION_INTERVAL_20MS_PARAM;
+        break;
+      default:
+        LOG(ERROR) << __func__ << ":Error: invalid default_data_interval_ms="
+                   << default_data_interval_ms;
+        min_ce_len = MIN_CE_LEN_10MS_CI;
+        connection_interval = CONNECTION_INTERVAL_10MS_PARAM;
+    }
+
+    L2CA_UpdateBleConnParams(address, connection_interval, connection_interval,
+                             0x000A, 0x0064 /*1s*/, min_ce_len, min_ce_len);
+  }
+
   void Connect(const RawAddress& address) override {
     DVLOG(2) << __func__ << " " << address;
     hearingDevices.Add(HearingDevice(address, true));
@@ -339,13 +382,12 @@
     // update now, it'll be started once current device finishes.
     hearingDevice->connection_update_pending = true;
     if (!any_update_pending) {
-      L2CA_UpdateBleConnParams(address, 0x0008, 0x0008, 0x000A, 0x0064 /*1s*/,
-                               MIN_CE_LEN_1M, MIN_CE_LEN_1M);
+      UpdateBleConnParams(address);
     }
 
     // Set data length
     // TODO(jpawlowski: for 16khz only 87 is required, optimize
-    BTM_SetBleDataLength(address, 147);
+    BTM_SetBleDataLength(address, 168);
 
     tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(address);
     if (p_dev_rec) {
@@ -390,8 +432,7 @@
 
     for (auto& device : hearingDevices.devices) {
       if (device.conn_id && device.connection_update_pending) {
-        L2CA_UpdateBleConnParams(device.address, 0x0008, 0x0008, 0x000A,
-                                 0x0064 /*1s*/, MIN_CE_LEN_1M, MIN_CE_LEN_1M);
+        UpdateBleConnParams(device.address);
         return;
       }
     }
@@ -557,6 +598,49 @@
     }
   }
 
+  uint16_t CalcCompressedAudioPacketSize(uint16_t codec_type,
+                                         int connection_interval) {
+    int sample_rate;
+
+    const int sample_bit_rate = 16;  /* 16 bits per sample */
+    const int compression_ratio = 4; /* G.722 has a 4:1 compression ratio */
+    if (codec_type == CODEC_G722_24KHZ) {
+      sample_rate = 24000;
+    } else {
+      sample_rate = 16000;
+    }
+
+    // compressed_data_packet_size is the size in bytes of the compressed audio
+    // data buffer that is generated for each connection interval.
+    uint32_t compressed_data_packet_size =
+        (sample_rate * connection_interval * (sample_bit_rate / 8) /
+         compression_ratio) /
+        1000;
+    return ((uint16_t)compressed_data_packet_size);
+  }
+
+  void ChooseCodec(const HearingDevice& hearingDevice) {
+    if (codec_in_use) return;
+
+    // use the best codec available for this pair of devices.
+    uint16_t codecs = hearingDevice.codecs;
+    if (hearingDevice.hi_sync_id != 0) {
+      for (const auto& device : hearingDevices.devices) {
+        if (device.hi_sync_id != hearingDevice.hi_sync_id) continue;
+
+        codecs &= device.codecs;
+      }
+    }
+
+    if ((codecs & (1 << CODEC_G722_24KHZ)) &&
+        controller_get_interface()->supports_ble_2m_phy() &&
+        default_data_interval_ms == HA_INTERVAL_10_MS) {
+      codec_in_use = CODEC_G722_24KHZ;
+    } else if (codecs & (1 << CODEC_G722_16KHZ)) {
+      codec_in_use = CODEC_G722_16KHZ;
+    }
+  }
+
   void OnAudioStatus(uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
                      uint16_t len, uint8_t* value, void* data) {
     DVLOG(2) << __func__ << " " << base::HexEncode(value, len);
@@ -648,11 +732,14 @@
       hearingDevice->first_connection = false;
     }
 
+    ChooseCodec(*hearingDevice);
+
     SendStart(*hearingDevice);
 
     hearingDevice->accepting_audio = true;
     LOG(INFO) << __func__ << ": address=" << address
-              << ", hi_sync_id=" << loghex(hearingDevice->hi_sync_id);
+              << ", hi_sync_id=" << loghex(hearingDevice->hi_sync_id)
+              << ", codec_in_use=" << loghex(codec_in_use);
 
     StartSendingAudio(*hearingDevice);
 
@@ -679,17 +766,14 @@
         }
       }
 
-      if ((codecs & (1 << CODEC_G722_24KHZ)) &&
-          controller_get_interface()->supports_ble_2m_phy()) {
-        codec_in_use = CODEC_G722_24KHZ;
+      CodecConfiguration codec;
+      if (codec_in_use == CODEC_G722_24KHZ) {
         codec.sample_rate = 24000;
-      } else if (codecs & (1 << CODEC_G722_16KHZ)) {
-        codec_in_use = CODEC_G722_16KHZ;
+      } else {
         codec.sample_rate = 16000;
       }
-
       codec.bit_rate = 16;
-      codec.data_interval_ms = 10;
+      codec.data_interval_ms = default_data_interval_ms;
 
       HearingAidAudioSource::Start(codec, audioReceiver);
     }
@@ -800,7 +884,9 @@
     // TODO: this should basically fit the encoded data, tune the size later
     std::vector<uint8_t> encoded_data_left;
     if (left) {
-      encoded_data_left.resize(2000);
+      // TODO: instead of a magic number, we need to figure out the correct
+      // buffer size
+      encoded_data_left.resize(4000);
       int encoded_size =
           g722_encode(encoder_state_left, encoded_data_left.data(),
                       (const int16_t*)chan_left.data(), chan_left.size());
@@ -820,7 +906,9 @@
 
     std::vector<uint8_t> encoded_data_right;
     if (right) {
-      encoded_data_right.resize(2000);
+      // TODO: instead of a magic number, we need to figure out the correct
+      // buffer size
+      encoded_data_right.resize(4000);
       int encoded_size =
           g722_encode(encoder_state_right, encoded_data_right.data(),
                       (const int16_t*)chan_right.data(), chan_right.size());
@@ -841,15 +929,8 @@
     size_t encoded_data_size =
         std::max(encoded_data_left.size(), encoded_data_right.size());
 
-    // TODO: make it also dependent on the interval, when we support intervals
-    // different than 10ms
-    uint16_t packet_size;
-
-    if (codec_in_use == CODEC_G722_24KHZ) {
-      packet_size = 120;
-    } else /* if (codec_in_use == CODEC_G722_16KHZ) */ {
-      packet_size = 80;
-    }
+    uint16_t packet_size =
+        CalcCompressedAudioPacketSize(codec_in_use, default_data_interval_ms);
 
     for (size_t i = 0; i < encoded_data_size; i += packet_size) {
       if (left) {
@@ -1078,7 +1159,8 @@
 
   /* currently used codec */
   uint8_t codec_in_use;
-  CodecConfiguration codec;
+
+  uint16_t default_data_interval_ms;
 
   HearingDevices hearingDevices;
 };
diff --git a/bta/hearing_aid/hearing_aid_audio_source.cc b/bta/hearing_aid/hearing_aid_audio_source.cc
index 9fba78d..e03b040 100644
--- a/bta/hearing_aid/hearing_aid_audio_source.cc
+++ b/bta/hearing_aid/hearing_aid_audio_source.cc
@@ -94,7 +94,8 @@
       UIPC_Ioctl(*uipc_hearing_aid, UIPC_CH_ID_AV_AUDIO, UIPC_SET_READ_POLL_TMO,
                  reinterpret_cast<void*>(0));
 
-      if (data_interval_ms != 10) {
+      if (data_interval_ms != HA_INTERVAL_10_MS &&
+          data_interval_ms != HA_INTERVAL_20_MS) {
         LOG(FATAL) << " Unsupported data interval: " << data_interval_ms;
       }
 
diff --git a/bta/include/bta_hearing_aid_api.h b/bta/include/bta_hearing_aid_api.h
index eeb9bfb..62b5c33 100644
--- a/bta/include/bta_hearing_aid_api.h
+++ b/bta/include/bta_hearing_aid_api.h
@@ -21,6 +21,9 @@
 #include <base/callback_forward.h>
 #include <hardware/bt_hearing_aid.h>
 
+constexpr uint16_t HA_INTERVAL_10_MS = 10;
+constexpr uint16_t HA_INTERVAL_20_MS = 20;
+
 /** Implementations of HearingAid will also implement this interface */
 class HearingAidAudioReceiver {
  public: