diff --git a/system/audio/Android.bp b/system/audio/Android.bp
index c0e3119d9d1bf6179fb6115da6ae44c2b0f373d3..2fc5464fd4fac866228655a3af20b3049258c71d 100644
--- a/system/audio/Android.bp
+++ b/system/audio/Android.bp
@@ -14,9 +14,6 @@ cc_library_static {
         "asrc/asrc_resampler.cc",
         "asrc/asrc_tables.cc",
     ],
-    include_dirs: [
-        "packages/modules/Bluetooth/system/gd",
-    ],
     shared_libs: [
         "libchrome",
     ],
@@ -44,7 +41,6 @@ cc_library_host_shared {
     stl: "libc++_static",
     include_dirs: [
         "packages/modules/Bluetooth/system",
-        "packages/modules/Bluetooth/system/gd",
     ],
     generated_headers: [
         "BluetoothGeneratedDumpsysDataSchema_h",
diff --git a/system/audio/asrc/asrc_resampler.cc b/system/audio/asrc/asrc_resampler.cc
index 0590b849d9f54a0a9f70dfc1cc0c7b5b7feba990..09e59584c7e8b487960f869d81eeb5e05f8474ef 100644
--- a/system/audio/asrc/asrc_resampler.cc
+++ b/system/audio/asrc/asrc_resampler.cc
@@ -19,15 +19,15 @@
 #include <base/logging.h>
 #include <base/strings/stringprintf.h>
 
+#include <algorithm>
 #include <cmath>
 #include <utility>
 
 #include "asrc_tables.h"
-#include "hal/nocp_iso_clocker.h"
 
 namespace bluetooth::audio::asrc {
 
-class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler {
+class SourceAudioHalAsrc::ClockRecovery : public ClockHandler {
   const int interval_;
 
   std::mutex mutex_;
@@ -113,8 +113,7 @@ class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler {
     }
 
     int dt_current = int(timestamp_us - link.local_time);
-    if (std::abs(dt_current) < std::abs(link.decim_dt[1]))
-      link.decim_dt[1] = dt_current;
+    link.decim_dt[1] = std::min(link.decim_dt[1], dt_current);
 
     if (link.local_time - link.decim_t0 < 1000 * 1000) return;
 
@@ -237,11 +236,9 @@ class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler {
         state_{
             .link = {{.state = LinkState::RESET}, {.state = LinkState::RESET}},
             .active_link_id = -1},
-        reference_timing_{0, 0, 0} {
-    ::bluetooth::hal::NocpIsoClocker::Register(this);
-  }
+        reference_timing_{0, 0, 0} {}
 
-  ~ClockRecovery() override { ::bluetooth::hal::NocpIsoClocker::Unregister(); }
+  ~ClockRecovery() override {}
 
   __attribute__((no_sanitize("integer"))) uint32_t Convert(
       uint32_t stream_time) {
@@ -466,16 +463,16 @@ inline int32_t SourceAudioHalAsrc::Resampler::Filter(const int32_t* in,
 
 #endif
 
-SourceAudioHalAsrc::SourceAudioHalAsrc(int channels, int sample_rate,
-                                       int bit_depth, int interval_us,
-                                       int num_burst_buffers,
-                                       int burst_delay_ms)
+SourceAudioHalAsrc::SourceAudioHalAsrc(
+    std::shared_ptr<ClockSource> clock_source, int channels, int sample_rate,
+    int bit_depth, int interval_us, int num_burst_buffers, int burst_delay_ms)
     : sample_rate_(sample_rate),
       bit_depth_(bit_depth),
       interval_us_(interval_us),
       stream_us_(0),
       drift_us_(0),
       out_counter_(0),
+      clock_source_(std::move(clock_source)),
       resampler_pos_{0, 0} {
   buffers_size_ = 0;
 
@@ -509,6 +506,7 @@ SourceAudioHalAsrc::SourceAudioHalAsrc(int channels, int sample_rate,
   // when the PCM bit_depth is higher than 16 bits.
 
   clock_recovery_ = std::make_unique<ClockRecovery>(interval_us_);
+  clock_source_->Bind(clock_recovery_.get());
   resamplers_ = std::make_unique<std::vector<Resampler>>(channels, bit_depth_);
 
   // Deduct from the PCM stream characteristics, the size of the pool buffers
diff --git a/system/audio/asrc/asrc_resampler.h b/system/audio/asrc/asrc_resampler.h
index ae9ea312cb2b27861359c1bace4771bb74f4e516..400c51c7be8fe3888da2a215bff98f580abaebb0 100644
--- a/system/audio/asrc/asrc_resampler.h
+++ b/system/audio/asrc/asrc_resampler.h
@@ -23,6 +23,19 @@
 
 namespace bluetooth::audio::asrc {
 
+class ClockHandler {
+ public:
+  virtual ~ClockHandler() = default;
+  virtual void OnEvent(uint32_t timestamp, int link_id,
+                       int num_of_completed_packets) = 0;
+};
+
+class ClockSource {
+ public:
+  virtual ~ClockSource() = default;
+  virtual void Bind(ClockHandler*) = 0;
+};
+
 class SourceAudioHalAsrc {
  public:
   // The Asynchronous Sample Rate Conversion (ASRC) is set up from the PCM
@@ -36,9 +49,9 @@ class SourceAudioHalAsrc {
   // `burst_delay_ms` helps to ensure that the synchronization with the
   // transmission intervals is done.
 
-  SourceAudioHalAsrc(int channels, int sample_rate, int bit_depth,
-                     int interval_us, int num_burst_buffers = 2,
-                     int burst_delay_ms = 500);
+  SourceAudioHalAsrc(std::shared_ptr<ClockSource> clock_source, int channels,
+                     int sample_rate, int bit_depth, int interval_us,
+                     int num_burst_buffers = 2, int burst_delay_ms = 500);
 
   ~SourceAudioHalAsrc();
 
@@ -74,6 +87,7 @@ class SourceAudioHalAsrc {
 
   class ClockRecovery;
   std::unique_ptr<ClockRecovery> clock_recovery_;
+  std::shared_ptr<ClockSource> clock_source_;
 
   class Resampler;
   std::unique_ptr<std::vector<Resampler>> resamplers_;
diff --git a/system/audio/asrc/asrc_resampler_test.cc b/system/audio/asrc/asrc_resampler_test.cc
index 04bf07ede89f39ceadeb473405a984100313a692..1b90e0a803180877cdd56769f4bd19c788ccbab5 100644
--- a/system/audio/asrc/asrc_resampler_test.cc
+++ b/system/audio/asrc/asrc_resampler_test.cc
@@ -19,17 +19,17 @@
 #include <cstdio>
 #include <iostream>
 
-namespace bluetooth::hal {
-void NocpIsoClocker::Register(NocpIsoHandler*) {}
-void NocpIsoClocker::Unregister() {}
-}  // namespace bluetooth::hal
-
 namespace bluetooth::audio::asrc {
 
+class MockClockSource : public ClockSource {
+  void Bind(ClockHandler*) override {}
+};
+
 class SourceAudioHalAsrcTest : public SourceAudioHalAsrc {
  public:
   SourceAudioHalAsrcTest(int channels, int bitdepth)
-      : SourceAudioHalAsrc(channels, 48000, bitdepth, 10000) {}
+      : SourceAudioHalAsrc(std::make_unique<MockClockSource>(), channels, 48000,
+                           bitdepth, 10000) {}
 
   template <typename T>
   void Resample(double ratio, const T* in, size_t in_length, size_t* in_count,
diff --git a/system/bta/hearing_aid/hearing_aid.cc b/system/bta/hearing_aid/hearing_aid.cc
index a32d7b23bfa1af3c698be6b0e09dfa98de068ca2..3b28bdfc57c31d298e18e8129ba120849458bd1f 100644
--- a/system/bta/hearing_aid/hearing_aid.cc
+++ b/system/bta/hearing_aid/hearing_aid.cc
@@ -32,12 +32,14 @@
 #include <mutex>
 #include <vector>
 
+#include "audio/asrc/asrc_resampler.h"
 #include "bta/include/bta_gatt_api.h"
 #include "bta/include/bta_gatt_queue.h"
 #include "bta/include/bta_hearing_aid_api.h"
 #include "btm_iso_api.h"
 #include "device/include/controller.h"
 #include "embdrv/g722/g722_enc_dec.h"
+#include "hal/link_clocker.h"
 #include "hardware/bt_gatt_types.h"
 #include "include/check.h"
 #include "internal_include/bt_trace.h"
@@ -275,6 +277,13 @@ class HearingAidImpl : public HearingAid {
   const int DROP_FREQUENCY_THRESHOLD =
       bluetooth::common::init_flags::get_asha_packet_drop_frequency_threshold();
 
+  // Resampler context for audio stream.
+  // Clock recovery uses L2CAP Flow Control Credit Ind acknowledgments
+  // from either the left or right connection, whichever is first
+  // connected.
+  std::shared_ptr<bluetooth::hal::L2capCreditIndEvents> asrc_clock_source;
+  std::unique_ptr<bluetooth::audio::asrc::SourceAudioHalAsrc> asrc;
+
  public:
   ~HearingAidImpl() override = default;
 
@@ -354,6 +363,51 @@ class HearingAidImpl : public HearingAid {
     }
   }
 
+  // Reset and configure the ASHA resampling context using the input device
+  // devices as reference for the BT clock estimation.
+  void ConfigureAsrc() {
+    if (!IS_FLAG_ENABLED(asha_asrc)) {
+      log::info("Asha resampling disabled: feature flag off");
+      return;
+    }
+
+    // Create a new ASRC context if required.
+    if (asrc == nullptr) {
+      asrc_clock_source =
+          std::make_shared<bluetooth::hal::L2capCreditIndEvents>();
+      asrc = std::make_unique<bluetooth::audio::asrc::SourceAudioHalAsrc>(
+          asrc_clock_source, /*channels*/ 2,
+          /*sample_rate*/ codec_in_use == CODEC_G722_24KHZ ? 24000 : 16000,
+          /*bit_depth*/ 16,
+          /*interval_us*/ default_data_interval_ms * 1000,
+          /*num_burst_buffers*/ 0,
+          /*burst_delay*/ 0);
+    }
+
+    for (auto& device : hearingDevices.devices) {
+      if (!device.accepting_audio) {
+        continue;
+      }
+
+      uint16_t lcid = GAP_ConnGetL2CAPCid(device.gap_handle);
+      uint16_t rcid = 0;
+      L2CA_GetRemoteCid(lcid, &rcid);
+
+      auto conn = btm_acl_for_bda(device.address, BT_TRANSPORT_LE);
+      log::info("Updating ASRC context for handle=0x{:x}, cid=0x{:x}",
+                conn->Handle(), rcid);
+
+      asrc_clock_source->Update(device.isLeft(), conn->Handle(), rcid);
+    }
+  }
+
+  // Reset the ASHA resampling context.
+  void ResetAsrc() {
+    log::info("Resetting the Asha resampling context");
+    asrc_clock_source = nullptr;
+    asrc = nullptr;
+  }
+
   uint16_t UpdateBleConnParams(const RawAddress& address) {
     /* List of parameters that depends on the chosen Connection Interval */
     uint16_t min_ce_len = MIN_CE_LEN_20MS_CI;
@@ -1171,6 +1225,10 @@ class HearingAidImpl : public HearingAid {
     } else {
       log::info("audio_running={}", audio_running);
     }
+
+    // Close the ASRC context.
+    ResetAsrc();
+
     audio_running = false;
     stop_audio_ticks();
 
@@ -1213,6 +1271,9 @@ class HearingAidImpl : public HearingAid {
       return;
     }
 
+    // Open the ASRC context.
+    ConfigureAsrc();
+
     // TODO: shall we also reset the encoder ?
     encoder_state_release();
     encoder_state_init();
@@ -1352,6 +1413,16 @@ class HearingAidImpl : public HearingAid {
     return diff_credit < (init_credit / 2 - 1);
   }
 
+  void OnAudioDataReadyResample(const std::vector<uint8_t>& data) {
+    if (asrc == nullptr) {
+      return OnAudioDataReady(data);
+    }
+
+    for (auto const resampled_data : asrc->Run(data)) {
+      OnAudioDataReady(*resampled_data);
+    }
+  }
+
   void OnAudioDataReady(const std::vector<uint8_t>& data) {
     /* For now we assume data comes in as 16bit per sample 16kHz PCM stereo */
     bool need_drop = false;
@@ -1414,6 +1485,18 @@ class HearingAidImpl : public HearingAid {
       l2cap_flush_threshold = 1;
     }
 
+    // Skipping packets completely messes up the resampler context.
+    // The condition for skipping packets seems to be easily triggered,
+    // causing dropouts that could have been avoided.
+    //
+    // When the resampler is enabled, the flush threshold is set
+    // to the number of credits specified for the ASHA l2cap streaming
+    // channel. This will ensure it is only triggered in case of
+    // critical failure.
+    if (IS_FLAG_ENABLED(asha_asrc)) {
+      l2cap_flush_threshold = 8;
+    }
+
     // TODO: monural, binarual check
 
     // divide encoded data into packets, add header, send.
@@ -2069,7 +2152,7 @@ void encryption_callback(const RawAddress* address, tBT_TRANSPORT, void*,
 class HearingAidAudioReceiverImpl : public HearingAidAudioReceiver {
  public:
   void OnAudioDataReady(const std::vector<uint8_t>& data) override {
-    if (instance) instance->OnAudioDataReady(data);
+    if (instance) instance->OnAudioDataReadyResample(data);
   }
   void OnAudioSuspend(const std::function<void()>& stop_audio_ticks) override {
     if (instance) instance->OnAudioSuspend(stop_audio_ticks);
diff --git a/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc b/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
index f8c87c43834efd73fea8754e200c94db0535b8ae..f2e8b12d165bb01e10ba1b08ce221234ad0ef9c4 100644
--- a/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
+++ b/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
@@ -27,6 +27,7 @@
 #include "bta/le_audio/codec_manager.h"
 #include "common/repeating_timer.h"
 #include "common/time_util.h"
+#include "gd/hal/link_clocker.h"
 #include "os/log.h"
 #include "osi/include/wakelock.h"
 #include "stack/include/main_thread.h"
@@ -248,6 +249,7 @@ void SourceImpl::StartAudioTicks() {
   wakelock_acquire();
   if (IS_FLAG_ENABLED(leaudio_hal_client_asrc)) {
     asrc_ = std::make_unique<bluetooth::audio::asrc::SourceAudioHalAsrc>(
+        std::make_shared<bluetooth::hal::NocpIsoEvents>(),
         source_codec_config_.num_channels, source_codec_config_.sample_rate,
         source_codec_config_.bits_per_sample,
         source_codec_config_.data_interval_us);
diff --git a/system/gd/hal/Android.bp b/system/gd/hal/Android.bp
index 27e6b6f204821a7d4c23f54112cc063cdb7f3e4a..c583cab7e44ef31fd8c6b69498d266bcf1d6b1c1 100644
--- a/system/gd/hal/Android.bp
+++ b/system/gd/hal/Android.bp
@@ -10,7 +10,7 @@ package {
 filegroup {
     name: "BluetoothHalSources",
     srcs: [
-        "nocp_iso_clocker.cc",
+        "link_clocker.cc",
         "snoop_logger.cc",
         "snoop_logger_socket.cc",
         "snoop_logger_socket_thread.cc",
diff --git a/system/gd/hal/BUILD.gn b/system/gd/hal/BUILD.gn
index 67034e1cc069544a3cb30d2942ea3b1dfb051149..affd15172330391f1757d305ccada27f61bb8ac9 100644
--- a/system/gd/hal/BUILD.gn
+++ b/system/gd/hal/BUILD.gn
@@ -16,7 +16,7 @@
 
 source_set("BluetoothHalSources") {
   sources = [
-    "nocp_iso_clocker.cc",
+    "link_clocker.cc",
     "snoop_logger.cc",
     "snoop_logger_socket.cc",
     "snoop_logger_socket_thread.cc",
diff --git a/system/gd/hal/hci_hal_android_hidl.cc b/system/gd/hal/hci_hal_android_hidl.cc
index 686b9510cf6d14fb1f47caf9bdf8770e608ab417..daef43372e140169e097e2f3e5efde017272dda0 100644
--- a/system/gd/hal/hci_hal_android_hidl.cc
+++ b/system/gd/hal/hci_hal_android_hidl.cc
@@ -38,7 +38,7 @@
 #include "common/stop_watch.h"
 #include "common/strings.h"
 #include "hal/hci_hal.h"
-#include "hal/nocp_iso_clocker.h"
+#include "hal/link_clocker.h"
 #include "hal/snoop_logger.h"
 #include "os/alarm.h"
 #include "os/log.h"
@@ -83,8 +83,8 @@ std::string GetTimerText(const char* func_name, VecType vec) {
 
 class InternalHciCallbacks : public IBluetoothHciCallbacks_1_1 {
  public:
-  InternalHciCallbacks(SnoopLogger* btsnoop_logger, NocpIsoClocker* nocp_iso_clocker)
-      : btsnoop_logger_(btsnoop_logger), nocp_iso_clocker_(nocp_iso_clocker) {
+  InternalHciCallbacks(SnoopLogger* btsnoop_logger, LinkClocker* link_clocker)
+      : btsnoop_logger_(btsnoop_logger), link_clocker_(link_clocker) {
     init_promise_ = new std::promise<void>();
   }
 
@@ -115,7 +115,7 @@ class InternalHciCallbacks : public IBluetoothHciCallbacks_1_1 {
   Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) override {
     common::StopWatch stop_watch(GetTimerText(__func__, event));
     std::vector<uint8_t> received_hci_packet(event.begin(), event.end());
-    nocp_iso_clocker_->OnHciEvent(received_hci_packet);
+    link_clocker_->OnHciEvent(received_hci_packet);
     btsnoop_logger_->Capture(
         received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
     {
@@ -130,6 +130,7 @@ class InternalHciCallbacks : public IBluetoothHciCallbacks_1_1 {
   Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) override {
     common::StopWatch stop_watch(GetTimerText(__func__, data));
     std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
+    link_clocker_->OnAclDataReceived(received_hci_packet);
     btsnoop_logger_->Capture(
         received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
     {
@@ -174,7 +175,7 @@ class InternalHciCallbacks : public IBluetoothHciCallbacks_1_1 {
   std::promise<void>* init_promise_ = nullptr;
   HciHalCallbacks* callback_ = nullptr;
   SnoopLogger* btsnoop_logger_ = nullptr;
-  NocpIsoClocker* nocp_iso_clocker_ = nullptr;
+  LinkClocker* link_clocker_ = nullptr;
 };
 
 static constexpr char kBluetoothAidlHalServiceName[] =
@@ -182,8 +183,8 @@ static constexpr char kBluetoothAidlHalServiceName[] =
 
 class AidlHciCallbacks : public ::aidl::android::hardware::bluetooth::BnBluetoothHciCallbacks {
  public:
-  AidlHciCallbacks(SnoopLogger* btsnoop_logger, NocpIsoClocker* nocp_iso_clocker)
-      : btsnoop_logger_(btsnoop_logger), nocp_iso_clocker_(nocp_iso_clocker) {
+  AidlHciCallbacks(SnoopLogger* btsnoop_logger, LinkClocker* link_clocker)
+      : btsnoop_logger_(btsnoop_logger), link_clocker_(link_clocker) {
     init_promise_ = new std::promise<void>();
   }
 
@@ -212,7 +213,7 @@ class AidlHciCallbacks : public ::aidl::android::hardware::bluetooth::BnBluetoot
   ::ndk::ScopedAStatus hciEventReceived(const std::vector<uint8_t>& event) override {
     common::StopWatch stop_watch(GetTimerText(__func__, event));
     std::vector<uint8_t> received_hci_packet(event.begin(), event.end());
-    nocp_iso_clocker_->OnHciEvent(received_hci_packet);
+    link_clocker_->OnHciEvent(received_hci_packet);
     btsnoop_logger_->Capture(
         received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
     bool sent = false;
@@ -232,6 +233,7 @@ class AidlHciCallbacks : public ::aidl::android::hardware::bluetooth::BnBluetoot
   ::ndk::ScopedAStatus aclDataReceived(const std::vector<uint8_t>& data) override {
     common::StopWatch stop_watch(GetTimerText(__func__, data));
     std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
+    link_clocker_->OnAclDataReceived(received_hci_packet);
     btsnoop_logger_->Capture(
         received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
     bool sent = false;
@@ -291,7 +293,7 @@ class AidlHciCallbacks : public ::aidl::android::hardware::bluetooth::BnBluetoot
   std::promise<void>* init_promise_ = nullptr;
   HciHalCallbacks* callback_ = nullptr;
   SnoopLogger* btsnoop_logger_ = nullptr;
-  NocpIsoClocker* nocp_iso_clocker_ = nullptr;
+  LinkClocker* link_clocker_ = nullptr;
 };
 
 }  // namespace
@@ -362,7 +364,7 @@ class HciHalHidl : public HciHal {
 
  protected:
   void ListDependencies(ModuleList* list) const {
-    list->add<NocpIsoClocker>();
+    list->add<LinkClocker>();
     list->add<SnoopLogger>();
   }
 
@@ -374,7 +376,7 @@ class HciHalHidl : public HciHal {
     ASSERT(bt_hci_1_1_ == nullptr);
     ASSERT(aidl_hci_ == nullptr);
 
-    nocp_iso_clocker_ = GetDependency<NocpIsoClocker>();
+    link_clocker_ = GetDependency<LinkClocker>();
     btsnoop_logger_ = GetDependency<SnoopLogger>();
 
     if (AServiceManager_isDeclared(kBluetoothAidlHalServiceName)) {
@@ -408,7 +410,7 @@ class HciHalHidl : public HciHal {
           death_link == STATUS_OK, "Unable to set the death recipient for the Bluetooth HAL");
 
       aidl_callbacks_ =
-          ::ndk::SharedRefBase::make<AidlHciCallbacks>(btsnoop_logger_, nocp_iso_clocker_);
+          ::ndk::SharedRefBase::make<AidlHciCallbacks>(btsnoop_logger_, link_clocker_);
       aidl_hci_->initialize(aidl_callbacks_);
     }
   }
@@ -448,7 +450,7 @@ class HciHalHidl : public HciHal {
     ASSERT(bt_hci_ != nullptr);
     auto death_link = bt_hci_->linkToDeath(hci_death_recipient_, 0);
     ASSERT_LOG(death_link.isOk(), "Unable to set the death recipient for the Bluetooth HAL");
-    hidl_callbacks_ = new InternalHciCallbacks(btsnoop_logger_, nocp_iso_clocker_);
+    hidl_callbacks_ = new InternalHciCallbacks(btsnoop_logger_, link_clocker_);
 
     if (bt_hci_1_1_ != nullptr) {
       bt_hci_1_1_->initialize_1_1(hidl_callbacks_);
@@ -507,7 +509,7 @@ class HciHalHidl : public HciHal {
   std::shared_ptr<AidlHciCallbacks> aidl_callbacks_;
   ::ndk::ScopedAIBinder_DeathRecipient aidl_death_recipient_;
   SnoopLogger* btsnoop_logger_;
-  NocpIsoClocker* nocp_iso_clocker_;
+  LinkClocker* link_clocker_;
 };
 
 const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalHidl(); });
diff --git a/system/gd/hal/hci_hal_host.cc b/system/gd/hal/hci_hal_host.cc
index 4c24c6b55c7676915a091e431b8292da203f6ff8..e05fda06b404f84d5d5b0943df9c3c545a4f6e70 100644
--- a/system/gd/hal/hci_hal_host.cc
+++ b/system/gd/hal/hci_hal_host.cc
@@ -30,8 +30,8 @@
 
 #include "common/init_flags.h"
 #include "hal/hci_hal.h"
+#include "hal/link_clocker.h"
 #include "hal/mgmt.h"
-#include "hal/nocp_iso_clocker.h"
 #include "hal/snoop_logger.h"
 #include "metrics/counter_metrics.h"
 #include "os/log.h"
@@ -285,7 +285,7 @@ class HciHalHost : public HciHal {
 
  protected:
   void ListDependencies(ModuleList* list) const {
-    list->add<NocpIsoClocker>();
+    list->add<LinkClocker>();
     list->add<metrics::CounterMetrics>();
     list->add<SnoopLogger>();
   }
@@ -307,7 +307,7 @@ class HciHalHost : public HciHal {
         common::Bind(&HciHalHost::incoming_packet_received, common::Unretained(this)),
         common::Bind(&HciHalHost::send_packet_ready, common::Unretained(this)));
     hci_incoming_thread_.GetReactor()->ModifyRegistration(reactable_, os::Reactor::REACT_ON_READ_ONLY);
-    nocp_iso_clocker_ = GetDependency<NocpIsoClocker>();
+    link_clocker_ = GetDependency<LinkClocker>();
     btsnoop_logger_ = GetDependency<SnoopLogger>();
     LOG_INFO("HAL opened successfully");
   }
@@ -348,7 +348,7 @@ class HciHalHost : public HciHal {
   bluetooth::os::Reactor::Reactable* reactable_ = nullptr;
   std::queue<std::vector<uint8_t>> hci_outgoing_queue_;
   SnoopLogger* btsnoop_logger_ = nullptr;
-  NocpIsoClocker* nocp_iso_clocker_ = nullptr;
+  LinkClocker* link_clocker_ = nullptr;
 
   void write_to_fd(HciPacket packet) {
     // TODO: replace this with new queue when it's ready
@@ -414,7 +414,7 @@ class HciHalHost : public HciHal {
 
       HciPacket receivedHciPacket;
       receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciEvtHeaderSize + payload_size);
-      nocp_iso_clocker_->OnHciEvent(receivedHciPacket);
+      link_clocker_->OnHciEvent(receivedHciPacket);
       btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
       {
         std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
@@ -440,6 +440,7 @@ class HciHalHost : public HciHal {
 
       HciPacket receivedHciPacket;
       receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciAclHeaderSize + payload_size);
+      link_clocker_->OnAclDataReceived(receivedHciPacket);
       btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
       {
         std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
diff --git a/system/gd/hal/link_clocker.cc b/system/gd/hal/link_clocker.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d0d09d9b3b76e1afc55686efb84c9e8b690da846
--- /dev/null
+++ b/system/gd/hal/link_clocker.cc
@@ -0,0 +1,228 @@
+/*
+ * Copyright 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.
+ */
+
+#include "hal/link_clocker.h"
+
+#include <algorithm>
+
+namespace bluetooth::hal {
+
+static constexpr uint16_t kInvalidConnectionHandle = 0xFFFF;
+
+static class : public bluetooth::audio::asrc::ClockHandler {
+  void OnEvent(uint32_t, int, int) override {}
+} g_empty_handler;
+
+static std::atomic<bluetooth::audio::asrc::ClockHandler*> g_nocp_iso_handler = &g_empty_handler;
+
+static struct {
+  std::mutex mutex;
+  bluetooth::audio::asrc::ClockHandler* handler;
+  struct {
+    uint16_t connection_handle;
+    uint16_t stream_cid;
+  } links[2];
+} g_credit_ind_handler = {.handler = &g_empty_handler, .links = {{}, {}}};
+
+NocpIsoEvents::~NocpIsoEvents() {
+  g_nocp_iso_handler = &g_empty_handler;
+}
+
+void NocpIsoEvents::Bind(bluetooth::audio::asrc::ClockHandler* handler) {
+  g_nocp_iso_handler = handler;
+}
+
+L2capCreditIndEvents::~L2capCreditIndEvents() {
+  std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex);
+  g_credit_ind_handler.handler = &g_empty_handler;
+  g_credit_ind_handler.links[0].connection_handle = kInvalidConnectionHandle;
+  g_credit_ind_handler.links[1].connection_handle = kInvalidConnectionHandle;
+}
+
+void L2capCreditIndEvents::Bind(bluetooth::audio::asrc::ClockHandler* handler) {
+  std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex);
+  g_credit_ind_handler.handler = handler;
+  g_credit_ind_handler.links[0].connection_handle = kInvalidConnectionHandle;
+  g_credit_ind_handler.links[1].connection_handle = kInvalidConnectionHandle;
+}
+
+void L2capCreditIndEvents::Update(int link_id, uint16_t connection_handle, uint16_t stream_cid) {
+  std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex);
+  g_credit_ind_handler.links[link_id].connection_handle = connection_handle;
+  g_credit_ind_handler.links[link_id].stream_cid = stream_cid;
+}
+
+LinkClocker::LinkClocker() : cig_id_(-1), cis_handle_(-1) {}
+
+void LinkClocker::OnHciEvent(const HciPacket& packet) {
+  const int HCI_CMD_SET_CIG_PARAMETERS = 0x2062;
+  const int HCI_EVT_COMMAND_COMPLETE = 0x0e;
+  const int HCI_EVT_NUMBER_OF_COMPLETED_PACKETS = 0x13;
+
+  // HCI Event [Core 4.E.5.4.4]
+  // |  [0]  Event Code
+  // |  [1]  Parameter Total Length
+  // | [2+]  Parameters
+
+  if (packet.size() < 2) return;
+
+  const uint8_t* payload = packet.data() + 2;
+  size_t payload_length = std::min(size_t(packet[1]), packet.size() - 2);
+
+  switch (packet[0]) {
+      // HCI Command Complete Event [Core 4.E.7.7.14]
+      // |    [0]  Num_HCI_Command_Packets, Ignored
+      // | [1..2]  Command_Opcode, catch `HCI_LE_Set_CIG_Parameters`
+      // |   [3+]  Return Parameters
+
+    case HCI_EVT_COMMAND_COMPLETE: {
+      if (payload_length < 3) return;
+
+      int cmd_opcode = payload[1] | (payload[2] << 8);
+      if (cmd_opcode != HCI_CMD_SET_CIG_PARAMETERS) return;
+
+      const uint8_t* parameters = payload + 3;
+      size_t parameters_length = payload_length - 3;
+
+      // HCI LE Set CIG Parameters return parameters [4.E.7.8.97]
+      // |    [0]  Status, 0 when OK
+      // |    [1]  CIG_ID
+      // |    [2]  CIS_Count
+      // | [3..4]  Connection_Handle[0]
+
+      if (parameters_length < 3) return;
+
+      int status = parameters[0];
+      int cig_id = parameters[1];
+      int cis_count = parameters[2];
+
+      if (status != 0) return;
+
+      if (cig_id_ >= 0 && cis_handle_ >= 0 && cig_id_ != cig_id) {
+        LOG_WARN("Multiple groups not supported");
+        return;
+      }
+
+      cig_id_ = -1;
+      cis_handle_ = -1;
+
+      if (cis_count > 0 && parameters_length >= 5) {
+        cig_id_ = cig_id;
+        cis_handle_ = (parameters[3] | (parameters[4] << 8)) & 0xfff;
+      }
+
+      break;
+    }
+
+      // HCI Number Of Completed Packets event [Core 4.E.7.7.19]
+      // | [0]  Num_Handles
+      // | FOR each `Num_Handles` connection handles
+      // | | [0..1]  Connection_Handle, catch the CIS Handle
+      // | | [2..3]  Num_Completed_Packets
+
+    case HCI_EVT_NUMBER_OF_COMPLETED_PACKETS: {
+      if (payload_length < 1) return;
+
+      int i, num_handles = payload[0];
+      const uint8_t* item = payload + 1;
+      if (payload_length < size_t(1 + 4 * num_handles)) return;
+
+      for (i = 0; i < num_handles && ((item[0] | (item[1] << 8)) & 0xfff) != cis_handle_;
+           i++, item += 4)
+        ;
+      if (i >= num_handles) return;
+
+      auto timestamp = std::chrono::system_clock::now().time_since_epoch();
+      unsigned timestamp_us =
+          std::chrono::duration_cast<std::chrono::microseconds>(timestamp).count();
+      int num_of_completed_packets = item[2] | (item[3] << 8);
+      (*g_nocp_iso_handler).OnEvent(timestamp_us, 0, num_of_completed_packets);
+
+      break;
+    }
+  }
+}
+
+/// Filter received L2CAP PDUs for Credit acknowledgments for the registered
+/// L2CAP channels.
+void LinkClocker::OnAclDataReceived(const HciPacket& packet) {
+  const int L2CAP_LE_U_CID = 0x0005;
+  const int L2CAP_FLOW_CONTROL_CREDIT_IND = 0x16;
+
+  // HCI ACL Data Packets [4.E.5.4.2]
+  // | [0..1]  Handle | PBF | BC
+  // | [2..3]  Data Total Length
+  // | [4+]    Data
+
+  if (packet.size() < 4) return;
+
+  uint16_t handle = packet[0] | (packet[1] << 8);
+  int packet_boundary_flag = (handle >> 12) & 0x3;
+  handle &= 0xfff;
+  uint16_t data_total_length = std::min(size_t(packet[2] | (packet[3] << 8)), packet.size() - 4);
+  const uint8_t* data = packet.data() + 4;
+
+  if (data_total_length < 4 || packet_boundary_flag == 0b01 || packet_boundary_flag == 0b11) return;
+
+  // L2CAP Signalling PDU Format [3.A.4]
+  // | [0..1]  PDU Length
+  // | [2..3]  Channel ID
+  // | [4+]    PDU
+  uint16_t pdu_length = std::min(data[0] | (data[1] << 8), data_total_length - 4);
+  uint16_t channel_id = data[2] | (data[3] << 8);
+  data += 4;
+
+  if (channel_id != L2CAP_LE_U_CID) return;
+
+  while (pdu_length >= 4) {
+    // | FOR each command in the PDU
+    // | | [0]     Command Code
+    // | | [1]     Command Identifier
+    // | | [2..3]  Data Length
+    // | | [4+]    Data
+    uint8_t command_code = data[0];
+    uint16_t data_length = std::min(data[2] | (data[3] << 8), pdu_length - 4);
+
+    if (command_code == L2CAP_FLOW_CONTROL_CREDIT_IND && data_length == 4) {
+      // | L2CAP Flow Control Credit Ind [3.A.4.24]
+      // | | [4..5]  CID
+      // | | [6..7]  Credits
+      uint16_t channel_id = data[4] | (data[5] << 8);
+      uint16_t credits = data[6] | (data[7] << 8);
+
+      auto timestamp = std::chrono::system_clock::now().time_since_epoch();
+      unsigned timestamp_us =
+          std::chrono::duration_cast<std::chrono::microseconds>(timestamp).count();
+
+      {
+        std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex);
+        for (int link_id = 0; link_id < 2; link_id++) {
+          auto const& link = g_credit_ind_handler.links[link_id];
+          if (link.connection_handle == handle && link.stream_cid == channel_id) {
+            g_credit_ind_handler.handler->OnEvent(timestamp_us, link_id, credits);
+          }
+        }
+      }
+    }
+
+    data += data_length + 4;
+    pdu_length -= data_length + 4;
+  }
+}
+
+const ModuleFactory LinkClocker::Factory = ModuleFactory([]() { return new LinkClocker(); });
+
+}  // namespace bluetooth::hal
diff --git a/system/gd/hal/nocp_iso_clocker.h b/system/gd/hal/link_clocker.h
similarity index 61%
rename from system/gd/hal/nocp_iso_clocker.h
rename to system/gd/hal/link_clocker.h
index a04cbd13c0d98c5a7a01381849be466dafd62631..c0a7278cabdb95ac915616c7da9a8cd984461d11 100644
--- a/system/gd/hal/nocp_iso_clocker.h
+++ b/system/gd/hal/link_clocker.h
@@ -16,25 +16,35 @@
 
 #pragma once
 
+#include "audio/asrc/asrc_resampler.h"
 #include "hci_hal.h"
 #include "module.h"
 
 namespace bluetooth::hal {
 
-class NocpIsoHandler {
+class NocpIsoEvents : public bluetooth::audio::asrc::ClockSource {
  public:
-  virtual ~NocpIsoHandler() = default;
-  virtual void OnEvent(uint32_t timestamp_us, int link_id, int num_of_completed_packets) = 0;
+  NocpIsoEvents() = default;
+  ~NocpIsoEvents() override;
+
+  void Bind(bluetooth::audio::asrc::ClockHandler*) override;
+};
+
+class L2capCreditIndEvents : public bluetooth::audio::asrc::ClockSource {
+ public:
+  L2capCreditIndEvents() {}
+  ~L2capCreditIndEvents() override;
+
+  void Bind(bluetooth::audio::asrc::ClockHandler*) override;
+  void Update(int link_id, uint16_t connection_handle, uint16_t stream_cid);
 };
 
-class NocpIsoClocker : public ::bluetooth::Module {
+class LinkClocker : public ::bluetooth::Module {
  public:
   static const ModuleFactory Factory;
 
   void OnHciEvent(const HciPacket& packet);
-
-  static void Register(NocpIsoHandler* handler);
-  static void Unregister();
+  void OnAclDataReceived(const HciPacket& packet);
 
  protected:
   void ListDependencies(ModuleList*) const override{};
@@ -42,10 +52,10 @@ class NocpIsoClocker : public ::bluetooth::Module {
   void Stop() override{};
 
   std::string ToString() const override {
-    return std::string("NocpIsoClocker");
+    return std::string("LinkClocker");
   }
 
-  NocpIsoClocker();
+  LinkClocker();
 
  private:
   int cig_id_;
diff --git a/system/gd/hal/nocp_iso_clocker.cc b/system/gd/hal/nocp_iso_clocker.cc
deleted file mode 100644
index b97f515944792f47c45c945750db258db9422db9..0000000000000000000000000000000000000000
--- a/system/gd/hal/nocp_iso_clocker.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#include "hal/nocp_iso_clocker.h"
-
-namespace bluetooth::hal {
-
-static class : public NocpIsoHandler {
-  void OnEvent(uint32_t, int, int) override {}
-} g_empty_handler;
-
-static std::atomic<NocpIsoHandler*> g_handler = &g_empty_handler;
-
-NocpIsoClocker::NocpIsoClocker() : cig_id_(-1), cis_handle_(-1) {}
-
-void NocpIsoClocker::OnHciEvent(const HciPacket& packet) {
-  const int HCI_CMD_SET_CIG_PARAMETERS = 0x2062;
-  const int HCI_EVT_COMMAND_COMPLETE = 0x0e;
-  const int HCI_EVT_NUMBER_OF_COMPLETED_PACKETS = 0x13;
-
-  // HCI Event [Core 4.E.5.4.4]
-  // |  [0]  Event Code
-  // |  [1]  Parameter Total Length
-  // | [2+]  Parameters
-
-  if (packet.size() < 2) return;
-
-  const uint8_t* payload = packet.data() + 2;
-  size_t payload_length = std::min(size_t(packet[1]), packet.size() - 2);
-
-  switch (packet[0]) {
-      // HCI Command Complete Event [Core 4.E.7.7.14]
-      // |    [0]  Num_HCI_Command_Packets, Ignored
-      // | [1..2]  Command_Opcode, catch `HCI_LE_Set_CIG_Parameters`
-      // |   [3+]  Return Parameters
-
-    case HCI_EVT_COMMAND_COMPLETE: {
-      if (payload_length < 3) return;
-
-      int cmd_opcode = payload[1] | (payload[2] << 8);
-      if (cmd_opcode != HCI_CMD_SET_CIG_PARAMETERS) return;
-
-      const uint8_t* parameters = payload + 3;
-      size_t parameters_length = payload_length - 3;
-
-      // HCI LE Set CIG Parameters return parameters [4.E.7.8.97]
-      // |    [0]  Status, 0 when OK
-      // |    [1]  CIG_ID
-      // |    [2]  CIS_Count
-      // | [3..4]  Connection_Handle[0]
-
-      if (parameters_length < 3) return;
-
-      int status = parameters[0];
-      int cig_id = parameters[1];
-      int cis_count = parameters[2];
-
-      if (status != 0) return;
-
-      if (cig_id_ >= 0 && cis_handle_ >= 0 && cig_id_ != cig_id) {
-        LOG_WARN("Multiple groups not supported");
-        return;
-      }
-
-      cig_id_ = -1;
-      cis_handle_ = -1;
-
-      if (cis_count > 0 && parameters_length >= 5) {
-        cig_id_ = cig_id;
-        cis_handle_ = (parameters[3] | (parameters[4] << 8)) & 0xfff;
-      }
-
-      break;
-    }
-
-      // HCI Number Of Completed Packets event [Core 4.E.7.7.19]
-      // | [0]  Num_Handles
-      // | FOR each `Num_Handles` connection handles
-      // | | [0..1]  Connection_Handle, catch the CIS Handle
-      // | | [2..3]  Num_Completed_Packets
-
-    case HCI_EVT_NUMBER_OF_COMPLETED_PACKETS: {
-      if (payload_length < 1) return;
-
-      int i, num_handles = payload[0];
-      const uint8_t* item = payload + 1;
-      if (payload_length < size_t(1 + 4 * num_handles)) return;
-
-      for (i = 0; i < num_handles && ((item[0] | (item[1] << 8)) & 0xfff) != cis_handle_;
-           i++, item += 4)
-        ;
-      if (i >= num_handles) return;
-
-      auto timestamp = std::chrono::system_clock::now().time_since_epoch();
-      unsigned timestamp_us =
-          std::chrono::duration_cast<std::chrono::microseconds>(timestamp).count();
-      int num_of_completed_packets = item[2] | (item[3] << 8);
-      (*g_handler).OnEvent(timestamp_us, 0, num_of_completed_packets);
-
-      break;
-    }
-  }
-}
-
-void NocpIsoClocker::Register(NocpIsoHandler* handler) {
-  g_handler = handler;
-}
-void NocpIsoClocker::Unregister() {
-  g_handler = &g_empty_handler;
-}
-
-const ModuleFactory NocpIsoClocker::Factory = ModuleFactory([]() { return new NocpIsoClocker(); });
-
-}  // namespace bluetooth::hal