Add audio configuration API and the callback function for bluetooth.audo@2.2

For LE audio hardware offload
1. Add the function to update audio configuration without session
restart
2. Add a callback to notify the audio HAL the audio configuration
changed

Bug: 197296692
Bug: 150670922
Test: HAL 2.1/2.2 work for A2DP software/hardware media, and LE audio
software media/voip call

Change-Id: I8b01ec8dfc9d9cd552770c8c734319a685ee626b
diff --git a/bluetooth/audio/2.2/IBluetoothAudioProvider.hal b/bluetooth/audio/2.2/IBluetoothAudioProvider.hal
index bc16b01..f577537 100644
--- a/bluetooth/audio/2.2/IBluetoothAudioProvider.hal
+++ b/bluetooth/audio/2.2/IBluetoothAudioProvider.hal
@@ -59,4 +59,14 @@
      */
     startSession_2_2(IBluetoothAudioPort hostIf, AudioConfiguration audioConfig)
                 generates (Status status, fmq_sync<uint8_t> dataMQ);
+
+    /**
+     * Called when the audio configuration of the stream has been changed.
+     *
+     * @param audioConfig The audio configuration negotiated with the remote
+     *    device. The PCM parameters are set if software based encoding,
+     *    otherwise the correct codec configuration is used for hardware
+     *    encoding.
+     */
+    updateAudioConfiguration(AudioConfiguration audioConfig);
 };
diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp
index 18ac292..202cfb9 100644
--- a/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp
+++ b/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp
@@ -186,6 +186,29 @@
   return Void();
 }
 
+Return<void> BluetoothAudioProvider::updateAudioConfiguration(
+    const AudioConfiguration& audioConfig) {
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
+
+  if (stack_iface_ == nullptr) {
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+              << " has NO session";
+    return Void();
+  }
+
+  if (audioConfig.getDiscriminator() != audio_config_.getDiscriminator()) {
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+              << " audio config type is not match";
+    return Void();
+  }
+
+  audio_config_ = audioConfig;
+  BluetoothAudioSessionReport_2_2::ReportAudioConfigChanged(session_type_,
+                                                            audio_config_);
+
+  return Void();
+}
+
 }  // namespace implementation
 }  // namespace V2_2
 }  // namespace audio
diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvider.h b/bluetooth/audio/2.2/default/BluetoothAudioProvider.h
index 0f1f3c6..425ea3b 100644
--- a/bluetooth/audio/2.2/default/BluetoothAudioProvider.h
+++ b/bluetooth/audio/2.2/default/BluetoothAudioProvider.h
@@ -53,6 +53,8 @@
   Return<void> streamStarted(BluetoothAudioStatus status) override;
   Return<void> streamSuspended(BluetoothAudioStatus status) override;
   Return<void> endSession() override;
+  Return<void> updateAudioConfiguration(
+      const AudioConfiguration& audioConfig) override;
 
  protected:
   sp<BluetoothAudioDeathRecipient> death_recipient_;
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h
index 71ab464..368939e 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h
+++ b/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h
@@ -48,20 +48,38 @@
     std::shared_ptr<BluetoothAudioSession_2_2> session_ptr =
         BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type);
     if (session_ptr != nullptr) {
-      return session_ptr->GetAudioSession()->RegisterStatusCback(cbacks);
+      PortStatusCallbacks_2_2 cb = {
+          .control_result_cb_ = cbacks.control_result_cb_,
+          .session_changed_cb_ = cbacks.session_changed_cb_,
+          .audio_configuration_changed_cb_ = nullptr};
+      return session_ptr->RegisterStatusCback(cb);
+    }
+    return kObserversCookieUndefined;
+  }
+
+  // The control API helps the bluetooth_audio module to register
+  // PortStatusCallbacks_2_2
+  // @return: cookie - the assigned number to this bluetooth_audio output
+  static uint16_t RegisterControlResultCback(
+      const SessionType_2_1& session_type,
+      const PortStatusCallbacks_2_2& cbacks) {
+    std::shared_ptr<BluetoothAudioSession_2_2> session_ptr =
+        BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->RegisterStatusCback(cbacks);
     }
     return kObserversCookieUndefined;
   }
 
   // The control API helps the bluetooth_audio module to unregister
-  // PortStatusCallbacks
+  // PortStatusCallbacks and PortStatusCallbacks_2_2
   // @param: cookie - indicates which bluetooth_audio output is
   static void UnregisterControlResultCback(const SessionType_2_1& session_type,
                                            uint16_t cookie) {
     std::shared_ptr<BluetoothAudioSession_2_2> session_ptr =
         BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type);
     if (session_ptr != nullptr) {
-      session_ptr->GetAudioSession()->UnregisterStatusCback(cookie);
+      session_ptr->UnregisterStatusCback(cookie);
     }
   }
 
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h
index 79121cc..17e140e 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h
+++ b/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h
@@ -60,7 +60,20 @@
     std::shared_ptr<BluetoothAudioSession_2_2> session_ptr =
         BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type);
     if (session_ptr != nullptr) {
-      session_ptr->GetAudioSession()->ReportControlStatus(start_resp, status);
+      session_ptr->ReportControlStatus(start_resp, status);
+    }
+  }
+  // The API reports the Bluetooth stack has replied the changed of the audio
+  // configuration, and will inform registered bluetooth_audio outputs
+  static void ReportAudioConfigChanged(
+      const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+          session_type,
+      const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration&
+          audio_config) {
+    std::shared_ptr<BluetoothAudioSession_2_2> session_ptr =
+        BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->ReportAudioConfigChanged(audio_config);
     }
   }
 };
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp
index db1619b..60ac4ec 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp
@@ -359,7 +359,7 @@
       audio_session->stack_iface_ = stack_iface;
       LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_)
                 << ", AudioConfiguration=" << toString(audio_config);
-      audio_session->ReportSessionStatus();
+      ReportSessionStatus();
     };
   }
 }
@@ -386,7 +386,150 @@
   audio_session->stack_iface_ = nullptr;
   audio_session->UpdateDataPath(nullptr);
   if (toggled) {
+    ReportSessionStatus();
+  }
+}
+
+// The control function helps the bluetooth_audio module to register
+// PortStatusCallbacks_2_2
+// @return: cookie - the assigned number to this bluetooth_audio output
+uint16_t BluetoothAudioSession_2_2::RegisterStatusCback(
+    const PortStatusCallbacks_2_2& cbacks) {
+  if (session_type_2_1_ !=
+          SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
+      session_type_2_1_ !=
+          SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
+    PortStatusCallbacks cb = {
+        .control_result_cb_ = cbacks.control_result_cb_,
+        .session_changed_cb_ = cbacks.session_changed_cb_};
+    return audio_session->RegisterStatusCback(cb);
+  }
+
+  std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
+  uint16_t cookie = ObserversCookieGetInitValue(session_type_2_1_);
+  uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_2_1_);
+
+  while (cookie < cookie_upper_bound) {
+    if (observers_.find(cookie) == observers_.end()) {
+      break;
+    }
+    ++cookie;
+  }
+  if (cookie >= cookie_upper_bound) {
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+               << " has " << observers_.size()
+               << " observers already (No Resource)";
+    return kObserversCookieUndefined;
+  }
+  std::shared_ptr<struct PortStatusCallbacks_2_2> cb =
+      std::make_shared<struct PortStatusCallbacks_2_2>();
+  *cb = cbacks;
+  observers_[cookie] = cb;
+  return cookie;
+}
+
+// The control function helps the bluetooth_audio module to unregister
+// PortStatusCallbacks_2_2
+// @param: cookie - indicates which bluetooth_audio output is
+void BluetoothAudioSession_2_2::UnregisterStatusCback(uint16_t cookie) {
+  std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
+  if (session_type_2_1_ !=
+          SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
+      session_type_2_1_ !=
+          SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
+    audio_session->UnregisterStatusCback(cookie);
+    return;
+  }
+  if (observers_.erase(cookie) != 1) {
+    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+                 << " no such provider=0x"
+                 << android::base::StringPrintf("%04x", cookie);
+  }
+}
+
+// invoking the registered session_changed_cb_
+void BluetoothAudioSession_2_2::ReportSessionStatus() {
+  // This is locked already by OnSessionStarted / OnSessionEnded
+  if (session_type_2_1_ !=
+          SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
+      session_type_2_1_ !=
+          SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
     audio_session->ReportSessionStatus();
+    return;
+  }
+  if (observers_.empty()) {
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+              << " has NO port state observer";
+    return;
+  }
+  for (auto& observer : observers_) {
+    uint16_t cookie = observer.first;
+    std::shared_ptr<struct PortStatusCallbacks_2_2> cb = observer.second;
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+              << " notify to bluetooth_audio=0x"
+              << android::base::StringPrintf("%04x", cookie);
+    cb->session_changed_cb_(cookie);
+  }
+}
+
+// The report function is used to report that the Bluetooth stack has notified
+// the result of startStream or suspendStream, and will invoke
+// control_result_cb_ to notify registered bluetooth_audio outputs
+void BluetoothAudioSession_2_2::ReportControlStatus(
+    bool start_resp, const BluetoothAudioStatus& status) {
+  if (session_type_2_1_ !=
+          SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
+      session_type_2_1_ !=
+          SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
+    audio_session->ReportControlStatus(start_resp, status);
+    return;
+  }
+  std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
+  if (observers_.empty()) {
+    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+                 << " has NO port state observer";
+    return;
+  }
+  for (auto& observer : observers_) {
+    uint16_t cookie = observer.first;
+    std::shared_ptr<struct PortStatusCallbacks_2_2> cb = observer.second;
+    LOG(INFO) << __func__ << " - status=" << toString(status)
+              << " for SessionType=" << toString(session_type_2_1_)
+              << ", bluetooth_audio=0x"
+              << android::base::StringPrintf("%04x", cookie)
+              << (start_resp ? " started" : " suspended");
+    cb->control_result_cb_(cookie, start_resp, status);
+  }
+}
+
+// The report function is used to report that the Bluetooth stack has notified
+// the result of startStream or suspendStream, and will invoke
+// control_result_cb_ to notify registered bluetooth_audio outputs
+void BluetoothAudioSession_2_2::ReportAudioConfigChanged(
+    const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration&
+        audio_config) {
+  if (session_type_2_1_ !=
+          SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
+      session_type_2_1_ !=
+          SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
+    return;
+  }
+  std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
+  audio_config_2_2_ = audio_config;
+  if (observers_.empty()) {
+    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+                 << " has NO port state observer";
+    return;
+  }
+  for (auto& observer : observers_) {
+    uint16_t cookie = observer.first;
+    std::shared_ptr<struct PortStatusCallbacks_2_2> cb = observer.second;
+    LOG(INFO) << __func__ << " for SessionType=" << toString(session_type_2_1_)
+              << ", bluetooth_audio=0x"
+              << android::base::StringPrintf("%04x", cookie);
+    if (cb->audio_configuration_changed_cb_ != nullptr) {
+      cb->audio_configuration_changed_cb_(cookie);
+    }
   }
 }
 
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h
index 6ac0188..3673fd8 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h
@@ -28,6 +28,40 @@
 namespace bluetooth {
 namespace audio {
 
+inline uint16_t ObserversCookieGetInitValue(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type) {
+  return (static_cast<uint16_t>(session_type) << 8 & 0xff00);
+}
+inline uint16_t ObserversCookieGetUpperBound(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type) {
+  return (static_cast<uint16_t>(session_type) << 8 & 0xff00) +
+         kObserversCookieSize;
+}
+
+struct PortStatusCallbacks_2_2 {
+  // control_result_cb_ - when the Bluetooth stack reports results of
+  // streamStarted or streamSuspended, the BluetoothAudioProvider will invoke
+  // this callback to report to the bluetooth_audio module.
+  // @param: cookie - indicates which bluetooth_audio output should handle
+  // @param: start_resp - this report is for startStream or not
+  // @param: status - the result of startStream
+  std::function<void(uint16_t cookie, bool start_resp,
+                     const BluetoothAudioStatus& status)>
+      control_result_cb_;
+  // session_changed_cb_ - when the Bluetooth stack start / end session, the
+  // BluetoothAudioProvider will invoke this callback to notify to the
+  // bluetooth_audio module.
+  // @param: cookie - indicates which bluetooth_audio output should handle
+  std::function<void(uint16_t cookie)> session_changed_cb_;
+  // audio_configuration_changed_cb_ - when the Bluetooth stack change the audio
+  // configuration, the BluetoothAudioProvider will invoke this callback to
+  // notify to the bluetooth_audio module.
+  // @param: cookie - indicates which bluetooth_audio output should handle
+  std::function<void(uint16_t cookie)> audio_configuration_changed_cb_;
+};
+
 class BluetoothAudioSession_2_2 {
  private:
   std::shared_ptr<BluetoothAudioSession> audio_session;
@@ -50,6 +84,13 @@
   static ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration
       invalidLeOffloadAudioConfiguration;
 
+  // saving those registered bluetooth_audio's callbacks
+  std::unordered_map<uint16_t, std::shared_ptr<struct PortStatusCallbacks_2_2>>
+      observers_;
+
+  // invoking the registered session_changed_cb_
+  void ReportSessionStatus();
+
  public:
   BluetoothAudioSession_2_2(
       const ::android::hardware::bluetooth::audio::V2_1::SessionType&
@@ -82,6 +123,29 @@
   bool SuspendStream();
   void StopStream();
 
+  // The control function helps the bluetooth_audio module to register
+  // PortStatusCallbacks_2_2
+  // @return: cookie - the assigned number to this bluetooth_audio output
+  uint16_t RegisterStatusCback(const PortStatusCallbacks_2_2& cbacks);
+
+  // The control function helps the bluetooth_audio module to unregister
+  // PortStatusCallbacks_2_2
+  // @param: cookie - indicates which bluetooth_audio output is
+  void UnregisterStatusCback(uint16_t cookie);
+
+  // The report function is used to report that the Bluetooth stack has notified
+  // the result of startStream or suspendStream, and will invoke
+  // control_result_cb_ to notify registered bluetooth_audio outputs
+  void ReportControlStatus(bool start_resp, const BluetoothAudioStatus& status);
+
+  // The report function is used to report that the Bluetooth stack has notified
+  // the audio configuration changed, and will invoke
+  // audio_configuration_changed_cb_ to notify registered bluetooth_audio
+  // outputs
+  void ReportAudioConfigChanged(
+      const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration&
+          audio_config);
+
   // The control function is for the bluetooth_audio module to get the current
   // AudioConfiguration
   const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration