Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c2f9bbd2 authored by Grzegorz Kołodziejczyk's avatar Grzegorz Kołodziejczyk
Browse files

le_audio: Introduce Sink listening mode

This allows service to monitor Sink HAL session streaming request and
stream suspension when going to IDLE state.
This is required for broadcast handover feature to inform Service when a
handover to Unicast should be made. As long as Audio Framework would
have connected Bluetooth Sink device, it'll pick it once some
application would require to open such device for recording like
Recorder etc.

Tag: #feature
Test: atest bluetooth_le_audio_test
Bug: 305943737
Bug: 308171251
Change-Id: I15317457329fe9fb660d7d2f7b4debfd13fb3c12
parent ec94418d
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ using bluetooth::le_audio::LeAudioBroadcasterCallbacks;
using bluetooth::le_audio::LeAudioBroadcasterInterface;
using bluetooth::le_audio::LeAudioClientCallbacks;
using bluetooth::le_audio::LeAudioClientInterface;
using bluetooth::le_audio::UnicastMonitorModeStatus;

namespace android {
static jmethodID method_onInitialized;
@@ -53,6 +54,7 @@ static jmethodID method_onAudioGroupCurrentCodecConf;
static jmethodID method_onAudioGroupSelectableCodecConf;
static jmethodID method_onHealthBasedRecommendationAction;
static jmethodID method_onHealthBasedGroupRecommendationAction;
static jmethodID method_onUnicastMonitorModeStatus;

static struct {
  jclass clazz;
@@ -345,6 +347,19 @@ class LeAudioClientCallbacksImpl : public LeAudioClientCallbacks {
                                 method_onHealthBasedGroupRecommendationAction,
                                 (jint)group_id, (jint)action);
  }

  void OnUnicastMonitorModeStatus(uint8_t direction,
                                  UnicastMonitorModeStatus status) override {
    LOG(INFO) << __func__;

    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;

    sCallbackEnv->CallVoidMethod(mCallbacksObj,
                                 method_onUnicastMonitorModeStatus,
                                 (jint)direction, (jint)status);
  }
};

static LeAudioClientCallbacksImpl sLeAudioClientCallbacks;
@@ -681,6 +696,17 @@ static void setInCallNative(JNIEnv* /* env */, jobject /* object */,
  sLeAudioClientInterface->SetInCall(inCall);
}

static void setUnicastMonitorModeNative(JNIEnv* /* env */, jobject /* object */,
                                        jint direction, jboolean enable) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sLeAudioClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth LeAudio Interface";
    return;
  }

  sLeAudioClientInterface->SetUnicastMonitorMode(direction, enable);
}

static void sendAudioProfilePreferencesNative(
    JNIEnv* /* env */, jint groupId, jboolean isOutputPreferenceLeAudio,
    jboolean isDuplexPreferenceLeAudio) {
@@ -1548,6 +1574,8 @@ int register_com_android_bluetooth_le_audio(JNIEnv* env) {
       (void*)setCodecConfigPreferenceNative},
      {"setCcidInformationNative", "(II)V", (void*)setCcidInformationNative},
      {"setInCallNative", "(Z)V", (void*)setInCallNative},
      {"setUnicastMonitorModeNative", "(IZ)V",
       (void*)setUnicastMonitorModeNative},
      {"sendAudioProfilePreferencesNative", "(IZZ)V",
       (void*)sendAudioProfilePreferencesNative},
  };
@@ -1582,6 +1610,8 @@ int register_com_android_bluetooth_le_audio(JNIEnv* env) {
       &method_onHealthBasedRecommendationAction},
      {"onHealthBasedGroupRecommendationAction", "(II)V",
       &method_onHealthBasedGroupRecommendationAction},
      {"onUnicastMonitorModeStatus", "(II)V",
       &method_onUnicastMonitorModeStatus},
  };
  GET_JAVA_METHODS(env, "com/android/bluetooth/le_audio/LeAudioNativeInterface",
                   javaMethods);
+30 −0
Original line number Diff line number Diff line
@@ -258,6 +258,20 @@ public class LeAudioNativeInterface {
        }
        sendMessageToService(event);
    }

    @VisibleForTesting
    void onUnicastMonitorModeStatus(int direction, int status) {
        LeAudioStackEvent event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS);
        event.valueInt1 = direction;
        event.valueInt2 = status;

        if (DBG) {
            Log.d(TAG, "onUnicastMonitorModeStatus: " + event);
        }
        sendMessageToService(event);
    }

    /**
     * Initializes the native interface.
     *
@@ -366,6 +380,20 @@ public class LeAudioNativeInterface {
        setInCallNative(inCall);
    }

    /**
     * Set unicast monitor mode flag.
     *
     * @param direction direction for which monitor mode should be used
     * @param enable true when LE Audio device should be listening for streaming status
     *     on direction stream. false otherwise
     */
    public void setUnicastMonitorMode(int direction, boolean enable) {
        if (DBG) {
            Log.d(TAG, "setUnicastMonitorMode enable: " + enable + ", direction : " + direction);
        }
        setUnicastMonitorModeNative(direction, enable);
    }

    /**
     * Sends the audio preferences for the groupId to the native stack.
     *
@@ -398,6 +426,8 @@ public class LeAudioNativeInterface {
            BluetoothLeAudioCodecConfig outputCodecConfig);
    private native void setCcidInformationNative(int ccid, int contextType);
    private native void setInCallNative(boolean inCall);

    private native void setUnicastMonitorModeNative(int direction, boolean enable);
    /*package*/
    private native void sendAudioProfilePreferencesNative(int groupId,
            boolean isOutputPreferenceLeAudio, boolean isDuplexPreferenceLeAudio);
+35 −1
Original line number Diff line number Diff line
@@ -40,8 +40,9 @@ public class LeAudioStackEvent {
    public static final int EVENT_TYPE_NATIVE_INITIALIZED = 9;
    public static final int EVENT_TYPE_HEALTH_BASED_DEV_RECOMMENDATION = 10;
    public static final int EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION = 11;
    public static final int EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS = 12;
    // -------- DO NOT PUT ANY NEW UNICAST EVENTS BELOW THIS LINE-------------
    public static final int EVENT_TYPE_UNICAST_MAX = 12;
    public static final int EVENT_TYPE_UNICAST_MAX = 13;

    // Broadcast related events
    public static final int EVENT_TYPE_BROADCAST_CREATED = EVENT_TYPE_UNICAST_MAX + 1;
@@ -77,6 +78,17 @@ public class LeAudioStackEvent {
    static final int BROADCAST_STATE_STOPPING = 3;
    static final int BROADCAST_STATE_STREAMING = 4;

    // Do not modify without updating the HAL bt_le_audio.h files.
    // Match up with UnicastMonitorModeStatus enum of bt_le_audio.h
    static final int STATUS_LOCAL_STREAM_REQUESTED = 0;
    static final int STATUS_LOCAL_STREAM_STREAMING = 1;
    static final int STATUS_LOCAL_STREAM_SUSPENDED = 2;

    // Do not modify without updating le_audio_types.h
    // Match up with defines of le_audio_types.h
    static final int DIRECTION_SINK = 1;
    static final int DIRECTION_SOURCE = 2;

    public int type = EVENT_TYPE_NONE;
    public BluetoothDevice device;
    public int valueInt1 = 0;
@@ -170,6 +182,8 @@ public class LeAudioStackEvent {
                return "EVENT_TYPE_HEALTH_BASED_DEV_RECOMMENDATION";
            case EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION:
                return "EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION";
            case EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS:
                return "EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS";
            default:
                return "EVENT_TYPE_UNKNOWN:" + type;
        }
@@ -223,6 +237,15 @@ public class LeAudioStackEvent {
                    default:
                        return "UNKNOWN";
                }
            case EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS:
                switch (value) {
                    case DIRECTION_SINK:
                        return "DIRECTION_SINK";
                    case DIRECTION_SOURCE:
                        return "DIRECTION_SOURCE";
                    default:
                        return "UNKNOWN";
                }
            default:
                break;
        }
@@ -265,6 +288,17 @@ public class LeAudioStackEvent {
                    default:
                        return "UNKNOWN";
                }
            case EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS:
                switch (value) {
                    case STATUS_LOCAL_STREAM_REQUESTED:
                        return "STATUS_LOCAL_STREAM_REQUESTED";
                    case STATUS_LOCAL_STREAM_STREAMING:
                        return "STATUS_LOCAL_STREAM_STREAMING";
                    case STATUS_LOCAL_STREAM_SUSPENDED:
                        return "STATUS_LOCAL_STREAM_SUSPENDED";
                    default:
                        return "UNKNOWN";
                }
            default:
                break;
        }
+1 −0
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ class LeAudioClient {
  virtual void SetInCall(bool in_call) = 0;
  virtual bool IsInCall() = 0;
  virtual void SetInVoipCall(bool in_call) = 0;
  virtual void SetUnicastMonitorMode(uint8_t direction, bool enable) = 0;
  virtual bool IsInVoipCall() = 0;
  virtual void SendAudioProfilePreferences(
      const int group_id, bool is_output_preference_le_audio,
+81 −3
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ using bluetooth::le_audio::GroupNodeStatus;
using bluetooth::le_audio::GroupStatus;
using bluetooth::le_audio::GroupStreamStatus;
using bluetooth::le_audio::LeAudioHealthBasedAction;
using bluetooth::le_audio::UnicastMonitorModeStatus;
using le_audio::CodecManager;
using le_audio::ContentControlIdKeeper;
using le_audio::DeviceConnectState;
@@ -231,6 +232,8 @@ class LeAudioClientImpl : public LeAudioClient {
        audio_sender_state_(AudioState::IDLE),
        in_call_(false),
        in_voip_call_(false),
        sink_monitor_mode_(false),
        sink_monitor_notified_status_(std::nullopt),
        current_source_codec_config({0, 0, 0, 0}),
        current_sink_codec_config({0, 0, 0, 0}),
        le_audio_source_hal_client_(nullptr),
@@ -1021,6 +1024,31 @@ class LeAudioClientImpl : public LeAudioClient {

  bool IsInVoipCall() override { return in_voip_call_; }

  void SetUnicastMonitorMode(uint8_t direction, bool enable) override {
    if (!IS_FLAG_ENABLED(leaudio_broadcast_audio_handover_policies)) {
      LOG_WARN("Monitor mode is disabled, Set Unicast Monitor mode is ignored");
      return;
    }

    if (direction == le_audio::types::kLeAudioDirectionSink) {
      /* Cleanup Sink HAL client interface if listening mode is toggled off
       * before group activation (active group context would take care of
       * Sink HAL client cleanup).
       */
      if (sink_monitor_mode_ && !enable && le_audio_sink_hal_client_ &&
          active_group_id_ == bluetooth::groups::kGroupUnknown) {
        local_metadata_context_types_.sink.clear();
        le_audio_sink_hal_client_->Stop();
        le_audio_sink_hal_client_.reset();
      }

      LOG_DEBUG("enable: %d", enable);
      sink_monitor_mode_ = enable;
    } else {
      LOG_ERROR("invalid direction: 0x%02x monitor mode set", direction);
    }
  }

  void SendAudioProfilePreferences(
      const int group_id, bool is_output_preference_le_audio,
      bool is_duplex_preference_le_audio) override {
@@ -1130,6 +1158,7 @@ class LeAudioClientImpl : public LeAudioClient {
    }
    auto group_id_to_close = active_group_id_;
    active_group_id_ = bluetooth::groups::kGroupUnknown;
    sink_monitor_notified_status_ = std::nullopt;

    LOG_INFO("Group id: %d", group_id_to_close);
    if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_);
@@ -1235,6 +1264,8 @@ class LeAudioClientImpl : public LeAudioClient {
      callbacks_->OnGroupStatus(previous_active_group, GroupStatus::INACTIVE);
    }

    /* Reset sink listener notified status */
    sink_monitor_notified_status_ = std::nullopt;
    callbacks_->OnGroupStatus(active_group_id_, GroupStatus::ACTIVE);
    SendAudioGroupSelectableCodecConfigChanged(group);
  }
@@ -3721,6 +3752,12 @@ class LeAudioClientImpl : public LeAudioClient {
    dprintf(fd, "  local sink metadata context type mask: %s\n",
            local_metadata_context_types_.sink.to_string().c_str());
    dprintf(fd, "  TBS state: %s\n", in_call_ ? " In call" : "No calls");
    dprintf(fd, "  Sink listening mode: %s\n",
            sink_monitor_mode_ ? "true" : "false");
    if (sink_monitor_notified_status_) {
      dprintf(fd, "  Local sink notified state: %d\n",
              sink_monitor_notified_status_.value());
    }
    dprintf(fd, "  Start time: ");
    for (auto t : stream_start_history_queue_) {
      dprintf(fd, ", %d ms", static_cast<int>(t));
@@ -3745,7 +3782,13 @@ class LeAudioClientImpl : public LeAudioClient {
    if (active_group_id_ != bluetooth::groups::kGroupUnknown) {
      /* Bluetooth turned off while streaming */
      StopAudio();
      SetUnicastMonitorMode(le_audio::types::kLeAudioDirectionSink, false);
      ClientAudioInterfaceRelease();
    } else {
      /* There may be not stopped Sink HAL client due to set Listening mode */
      if (sink_monitor_mode_) {
        SetUnicastMonitorMode(le_audio::types::kLeAudioDirectionSink, false);
      }
    }
    groupStateMachine_->Cleanup();
    aseGroups_.Cleanup();
@@ -4197,6 +4240,16 @@ class LeAudioClientImpl : public LeAudioClient {
        .has_value();
  }

  void notifyAudioLocalSink(UnicastMonitorModeStatus status) {
    if (sink_monitor_notified_status_ != status) {
      LOG_INFO("Stram monitoring status changed to: %d",
               static_cast<int>(status));
      sink_monitor_notified_status_ = status;
      callbacks_->OnUnicastMonitorModeStatus(
          le_audio::types::kLeAudioDirectionSink, status);
    }
  }

  void OnLocalAudioSinkResume() {
    LOG_INFO(
        "active group_id: %d IN: audio_receiver_state_: %s, "
@@ -4209,6 +4262,14 @@ class LeAudioClientImpl : public LeAudioClient {
        "r_state: " + ToString(audio_receiver_state_) +
            ", s_state: " + ToString(audio_sender_state_));

    if (active_group_id_ == bluetooth::groups::kGroupUnknown) {
      if (sink_monitor_mode_ && !sink_monitor_notified_status_) {
        notifyAudioLocalSink(UnicastMonitorModeStatus::STREAMING_REQUESTED);
      }
      CancelLocalAudioSinkStreamingRequest();
      return;
    }

    /* Stop the VBC close watchdog if needed */
    StopVbcCloseTimeout();

@@ -5436,6 +5497,12 @@ class LeAudioClientImpl : public LeAudioClient {
            LOG_INFO("Clear pending configuration flag for group %d",
                    group->group_id_);
            group->ClearPendingConfiguration();
          } else {
            if (sink_monitor_mode_) {
              callbacks_->OnUnicastMonitorModeStatus(
                  le_audio::types::kLeAudioDirectionSink,
                  UnicastMonitorModeStatus::STREAMING_SUSPENDED);
            }
          }
        }

@@ -5510,6 +5577,10 @@ class LeAudioClientImpl : public LeAudioClient {
  /* Keep in call state. */
  bool in_call_;
  bool in_voip_call_;
  /* Listen for streaming status on Sink stream */
  bool sink_monitor_mode_;
  /* Status which has been notified to Service */
  std::optional<UnicastMonitorModeStatus> sink_monitor_notified_status_;

  /* Reconnection mode */
  tBTM_BLE_CONN_TYPE reconnection_mode_;
@@ -5575,12 +5646,19 @@ class LeAudioClientImpl : public LeAudioClient {
      le_audio_source_hal_client_->Stop();
      le_audio_source_hal_client_.reset();
    }
    local_metadata_context_types_.sink.clear();

    if (le_audio_sink_hal_client_) {
      /* Keep session set up to monitor streaming request. This is required if
       * there is another LE Audio device streaming (e.g. Broadcast) and via
       * the session callbacks special action from this Module would be
       * required e.g. to Unicast handover.
       */
      if (!sink_monitor_mode_) {
        local_metadata_context_types_.sink.clear();
        le_audio_sink_hal_client_->Stop();
        le_audio_sink_hal_client_.reset();
      }
    }
    local_metadata_context_types_.source.clear();
    configuration_context_type_ = LeAudioContextType::UNINITIALIZED;

Loading