blob: 26c98893c3a6f0d81b6f2de45fca57abcde8a318 [file]
/*
* Copyright 2023 The Android Open Source Project
* Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA
* - www.ehima.com
*
* 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.
*/
/* LeAudioDeviceGroup class represents group of LeAudioDevices and allows to
* perform operations on them. Group states are ASE states due to nature of
* group which operates finally of ASEs.
*
* Group is created after adding a node to new group id (which is not on list).
*/
#pragma once
#include <map>
#include <memory>
#include <optional>
#include <utility> // for std::pair
#include <vector>
#ifdef __ANDROID__
#include <android/sysprop/BluetoothProperties.sysprop.h>
#endif
#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>
#include "common/strings.h"
#include "devices.h"
#include "le_audio_log_history.h"
#include "le_audio_types.h"
namespace bluetooth::le_audio {
class LeAudioDeviceGroup {
public:
const int group_id_;
class CigConfiguration {
public:
CigConfiguration() = delete;
CigConfiguration(LeAudioDeviceGroup* group)
: group_(group), state_(types::CigState::NONE) {}
types::CigState GetState(void) const { return state_; }
void SetState(bluetooth::le_audio::types::CigState state) {
log::verbose("{} -> {}", bluetooth::common::ToString(state_),
bluetooth::common::ToString(state));
state_ = state;
}
void GenerateCisIds(types::LeAudioContextType context_type);
bool AssignCisIds(LeAudioDevice* leAudioDevice);
void AssignCisConnHandles(const std::vector<uint16_t>& conn_handles);
void UnassignCis(LeAudioDevice* leAudioDevice);
std::vector<struct types::cis> cises;
private:
uint8_t GetFirstFreeCisId(types::CisType cis_type) const;
LeAudioDeviceGroup* group_;
types::CigState state_;
} cig;
bool IsGroupConfiguredTo(
const set_configurations::AudioSetConfiguration& cfg) {
if (!stream_conf.conf) return false;
return cfg == *stream_conf.conf;
}
/* Current configuration strategy - recalculated on demand */
mutable std::optional<types::LeAudioConfigurationStrategy> strategy_ =
std::nullopt;
/* Current audio stream configuration */
struct stream_configuration stream_conf;
bool notify_streaming_when_cises_are_ready_;
uint8_t audio_directions_;
types::AudioLocations snk_audio_locations_;
types::AudioLocations src_audio_locations_;
/* Whether LE Audio is preferred for OUTPUT_ONLY and DUPLEX cases */
bool is_output_preference_le_audio;
bool is_duplex_preference_le_audio;
struct {
DsaMode mode;
bool active;
} dsa_;
bool asymmetric_phy_for_unidirectional_cis_supported;
explicit LeAudioDeviceGroup(const int group_id)
: group_id_(group_id),
cig(this),
stream_conf({}),
notify_streaming_when_cises_are_ready_(false),
audio_directions_(0),
dsa_({DsaMode::DISABLED, false}),
is_enabled_(true),
transport_latency_mtos_us_(0),
transport_latency_stom_us_(0),
configuration_context_type_(types::LeAudioContextType::UNINITIALIZED),
metadata_context_type_({.sink = types::AudioContexts(
types::LeAudioContextType::UNINITIALIZED),
.source = types::AudioContexts(
types::LeAudioContextType::UNINITIALIZED)}),
group_available_contexts_(
{.sink =
types::AudioContexts(types::LeAudioContextType::UNINITIALIZED),
.source = types::AudioContexts(
types::LeAudioContextType::UNINITIALIZED)}),
pending_group_available_contexts_change_(
types::LeAudioContextType::UNINITIALIZED),
group_user_allowed_context_mask_(
{.sink = types::AudioContexts(types::kLeAudioContextAllTypes),
.source = types::AudioContexts(types::kLeAudioContextAllTypes)}),
target_state_(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE),
current_state_(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE),
in_transition_(false) {
#ifdef __ANDROID__
// 22 maps to BluetoothProfile#LE_AUDIO
is_output_preference_le_audio = android::sysprop::BluetoothProperties::
getDefaultOutputOnlyAudioProfile() ==
LE_AUDIO_PROFILE_CONSTANT;
is_duplex_preference_le_audio =
android::sysprop::BluetoothProperties::getDefaultDuplexAudioProfile() ==
LE_AUDIO_PROFILE_CONSTANT;
#else
is_output_preference_le_audio = true;
is_duplex_preference_le_audio = true;
#endif
asymmetric_phy_for_unidirectional_cis_supported =
com::android::bluetooth::flags::asymmetric_phy_for_unidirectional_cis();
}
~LeAudioDeviceGroup(void);
void AddNode(const std::shared_ptr<LeAudioDevice>& leAudioDevice);
void RemoveNode(const std::shared_ptr<LeAudioDevice>& leAudioDevice);
bool IsEmpty(void) const;
bool IsAnyDeviceConnected(void) const;
int Size(void) const;
int DesiredSize(void) const;
int NumOfConnected() const;
int NumOfAvailableForDirection(int direction) const;
bool Activate(types::LeAudioContextType context_type,
const types::BidirectionalPair<types::AudioContexts>&
metadata_context_types,
types::BidirectionalPair<std::vector<uint8_t>> ccid_lists);
void Deactivate(void);
void ClearSinksFromConfiguration(void);
void ClearSourcesFromConfiguration(void);
void Cleanup(void);
LeAudioDevice* GetFirstDevice(void) const;
LeAudioDevice* GetFirstDeviceWithAvailableContext(
types::LeAudioContextType context_type) const;
types::LeAudioConfigurationStrategy GetGroupSinkStrategy(void) const;
inline void InvalidateGroupStrategy(void) { strategy_ = std::nullopt; }
int GetAseCount(uint8_t direction) const;
LeAudioDevice* GetNextDevice(LeAudioDevice* leAudioDevice) const;
LeAudioDevice* GetNextDeviceWithAvailableContext(
LeAudioDevice* leAudioDevice,
types::LeAudioContextType context_type) const;
LeAudioDevice* GetFirstActiveDevice(void) const;
LeAudioDevice* GetNextActiveDevice(LeAudioDevice* leAudioDevice) const;
LeAudioDevice* GetFirstActiveDeviceByCisAndDataPathState(
types::CisState cis_state, types::DataPathState data_path_state) const;
LeAudioDevice* GetNextActiveDeviceByCisAndDataPathState(
LeAudioDevice* leAudioDevice, types::CisState cis_state,
types::DataPathState data_path_state) const;
bool IsDeviceInTheGroup(LeAudioDevice* leAudioDevice) const;
bool HaveAllActiveDevicesAsesTheSameState(types::AseState state) const;
bool HaveAnyActiveDeviceInUnconfiguredState() const;
bool IsGroupStreamReady(void) const;
bool IsGroupReadyToCreateStream(void) const;
bool IsGroupReadyToSuspendStream(void) const;
bool HaveAllCisesDisconnected(void) const;
void ClearAllCises(void);
void UpdateCisConfiguration(uint8_t direction);
void AssignCisConnHandlesToAses(LeAudioDevice* leAudioDevice);
void AssignCisConnHandlesToAses(void);
bool Configure(types::LeAudioContextType context_type,
const types::BidirectionalPair<types::AudioContexts>&
metadata_context_types,
types::BidirectionalPair<std::vector<uint8_t>> ccid_lists = {
.sink = {}, .source = {}});
uint32_t GetSduInterval(uint8_t direction) const;
uint8_t GetSCA(void) const;
uint8_t GetPacking(void) const;
uint8_t GetFraming(void) const;
uint16_t GetMaxTransportLatencyStom(void) const;
uint16_t GetMaxTransportLatencyMtos(void) const;
void SetTransportLatency(uint8_t direction, uint32_t transport_latency_us);
uint8_t GetRtn(uint8_t direction, uint8_t cis_id) const;
uint16_t GetMaxSduSize(uint8_t direction, uint8_t cis_id) const;
uint8_t GetPhyBitmask(uint8_t direction) const;
uint8_t GetTargetPhy(uint8_t direction) const;
bool GetPresentationDelay(uint32_t* delay, uint8_t direction) const;
uint16_t GetRemoteDelay(uint8_t direction) const;
bool UpdateAudioContextAvailability(void);
bool UpdateAudioSetConfigurationCache(
types::LeAudioContextType ctx_type) const;
CodecManager::UnicastConfigurationRequirements
GetAudioSetConfigurationRequirements(
types::LeAudioContextType ctx_type) const;
bool ReloadAudioLocations(void);
bool ReloadAudioDirections(void);
std::shared_ptr<const set_configurations::AudioSetConfiguration>
GetActiveConfiguration(void) const;
bool IsPendingConfiguration(void) const;
std::shared_ptr<const set_configurations::AudioSetConfiguration>
GetConfiguration(types::LeAudioContextType ctx_type) const;
std::shared_ptr<const set_configurations::AudioSetConfiguration>
GetCachedConfiguration(types::LeAudioContextType ctx_type) const;
void InvalidateCachedConfigurations(void);
void SetPendingConfiguration(void);
void ClearPendingConfiguration(void);
void AddToAllowListNotConnectedGroupMembers(int gatt_if);
void ApplyReconnectionMode(int gatt_if, tBTM_BLE_CONN_TYPE reconnection_mode);
void Disable(int gatt_if);
void Enable(int gatt_if, tBTM_BLE_CONN_TYPE reconnection_mode);
bool IsEnabled(void) const;
LeAudioCodecConfiguration GetAudioSessionCodecConfigForDirection(
types::LeAudioContextType group_context_type, uint8_t direction) const;
bool HasCodecConfigurationForDirection(
types::LeAudioContextType group_context_type, uint8_t direction) const;
bool IsAudioSetConfigurationAvailable(
types::LeAudioContextType group_context_type);
bool IsMetadataChanged(
const types::BidirectionalPair<types::AudioContexts>& context_types,
const types::BidirectionalPair<std::vector<uint8_t>>& ccid_lists) const;
bool IsConfiguredForContext(types::LeAudioContextType context_type) const;
void RemoveCisFromStreamIfNeeded(LeAudioDevice* leAudioDevice,
uint16_t cis_conn_hdl);
inline types::AseState GetState(void) const { return current_state_; }
void SetState(types::AseState state) {
log::info("current state: {}, new state {}, in_transition_ {}",
bluetooth::common::ToString(current_state_),
bluetooth::common::ToString(state), in_transition_);
LeAudioLogHistory::Get()->AddLogHistory(
kLogStateMachineTag, group_id_, RawAddress::kEmpty, kLogStateChangedOp,
bluetooth::common::ToString(current_state_) + "->" +
bluetooth::common::ToString(state));
current_state_ = state;
if (target_state_ == current_state_) {
in_transition_ = false;
log::info("In transition flag cleared");
}
}
inline types::AseState GetTargetState(void) const { return target_state_; }
inline void SetNotifyStreamingWhenCisesAreReadyFlag(bool value) {
notify_streaming_when_cises_are_ready_ = value;
}
inline bool GetNotifyStreamingWhenCisesAreReadyFlag(void) {
return notify_streaming_when_cises_are_ready_;
}
void SetTargetState(types::AseState state) {
log::info("target state: {}, new target state: {}, in_transition_ {}",
bluetooth::common::ToString(target_state_),
bluetooth::common::ToString(state), in_transition_);
LeAudioLogHistory::Get()->AddLogHistory(
kLogStateMachineTag, group_id_, RawAddress::kEmpty,
kLogTargetStateChangedOp,
bluetooth::common::ToString(target_state_) + "->" +
bluetooth::common::ToString(state));
target_state_ = state;
in_transition_ = target_state_ != current_state_;
log::info("In transition flag = {}", in_transition_);
}
/* Returns context types for which support was recently added or removed */
inline types::AudioContexts GetPendingAvailableContextsChange() const {
return pending_group_available_contexts_change_;
}
/* Set which context types were recently added or removed */
inline void SetPendingAvailableContextsChange(
types::AudioContexts audio_contexts) {
pending_group_available_contexts_change_ = audio_contexts;
}
inline void ClearPendingAvailableContextsChange() {
pending_group_available_contexts_change_.clear();
}
inline void SetConfigurationContextType(
types::LeAudioContextType context_type) {
configuration_context_type_ = context_type;
}
inline types::LeAudioContextType GetConfigurationContextType(void) const {
return configuration_context_type_;
}
inline types::BidirectionalPair<types::AudioContexts> GetMetadataContexts()
const {
return metadata_context_type_;
}
inline void SetAvailableContexts(
types::BidirectionalPair<types::AudioContexts> new_contexts) {
group_available_contexts_ = new_contexts;
log::debug(
"group id: {}, available contexts sink: {}, available contexts source: "
"{}",
group_id_, group_available_contexts_.sink.to_string(),
group_available_contexts_.source.to_string());
}
types::AudioContexts GetAvailableContexts(
int direction = types::kLeAudioDirectionBoth) const {
log::assert_that(direction <= (types::kLeAudioDirectionBoth),
"Invalid direction used.");
if (direction < types::kLeAudioDirectionBoth) {
log::debug(
"group id: {}, available contexts sink: {}, available contexts "
"source: {}",
group_id_, group_available_contexts_.sink.to_string(),
group_available_contexts_.source.to_string());
return group_available_contexts_.get(direction);
} else {
return types::get_bidirectional(group_available_contexts_);
}
}
inline void SetAllowedContextMask(
types::BidirectionalPair<types::AudioContexts>& context_types) {
group_user_allowed_context_mask_ = context_types;
log::debug(
"group id: {}, allowed contexts sink: {}, allowed contexts source: "
"{}",
group_id_, group_user_allowed_context_mask_.sink.to_string(),
group_user_allowed_context_mask_.source.to_string());
}
types::AudioContexts GetAllowedContextMask(
int direction = types::kLeAudioDirectionBoth) const {
log::assert_that(direction <= (types::kLeAudioDirectionBoth),
"Invalid direction used.");
if (direction < types::kLeAudioDirectionBoth) {
log::debug(
"group id: {}, allowed contexts sink: {}, allowed contexts "
"source: {}",
group_id_, group_user_allowed_context_mask_.sink.to_string(),
group_user_allowed_context_mask_.source.to_string());
return group_user_allowed_context_mask_.get(direction);
} else {
return types::get_bidirectional(group_user_allowed_context_mask_);
}
}
types::AudioContexts GetSupportedContexts(
int direction = types::kLeAudioDirectionBoth) const;
DsaModes GetAllowedDsaModes() {
if (!com::android::bluetooth::flags::leaudio_dynamic_spatial_audio()) {
return {DsaMode::DISABLED};
}
DsaModes dsa_modes{};
std::set<DsaMode> dsa_mode_set{};
for (auto leAudioDevice : leAudioDevices_) {
if (leAudioDevice.expired()) continue;
auto device_dsa_modes = leAudioDevice.lock()->GetDsaModes();
dsa_mode_set.insert(device_dsa_modes.begin(), device_dsa_modes.end());
}
dsa_modes.assign(dsa_mode_set.begin(), dsa_mode_set.end());
return dsa_modes;
}
std::vector<DsaModes> GetAllowedDsaModesList() {
std::vector<DsaModes> dsa_modes_list = {};
for (auto leAudioDevice : leAudioDevices_) {
DsaModes dsa_modes = {};
if (!leAudioDevice.expired()) {
dsa_modes = leAudioDevice.lock()->GetDsaModes();
}
dsa_modes_list.push_back(dsa_modes);
}
return dsa_modes_list;
}
types::BidirectionalPair<types::AudioContexts> GetLatestAvailableContexts(
void) const;
bool IsInTransition(void) const;
bool IsStreaming(void) const;
bool IsReleasingOrIdle(void) const;
void PrintDebugState(void) const;
void Dump(int fd, int active_group_id) const;
/* Codec configuration matcher supporting the legacy configuration provider
* mechanism for the non-vendor and software codecs. Only if the codec
* parameters are using the common LTV data format, the BT stack can verify
* them against the remote device capabilities and find the best possible
* configurations. This will not be used for finding best possible vendor
* codec configuration.
*/
const set_configurations::AudioSetConfiguration*
FindFirstSupportedConfiguration(
const CodecManager::UnicastConfigurationRequirements& requirements,
const set_configurations::AudioSetConfigurations* confs) const;
private:
bool is_enabled_;
uint32_t transport_latency_mtos_us_;
uint32_t transport_latency_stom_us_;
bool ConfigureAses(
const set_configurations::AudioSetConfiguration* audio_set_conf,
types::LeAudioContextType context_type,
const types::BidirectionalPair<types::AudioContexts>&
metadata_context_types,
const types::BidirectionalPair<std::vector<uint8_t>>& ccid_lists);
bool IsAudioSetConfigurationSupported(
const CodecManager::UnicastConfigurationRequirements& requirements,
const set_configurations::AudioSetConfiguration* audio_set_configuration)
const;
uint32_t GetTransportLatencyUs(uint8_t direction) const;
bool IsCisPartOfCurrentStream(uint16_t cis_conn_hdl) const;
/* Current configuration and metadata context types */
types::LeAudioContextType configuration_context_type_;
types::BidirectionalPair<types::AudioContexts> metadata_context_type_;
/* Mask of contexts that the whole group can handle at its current state
* It's being updated each time group members connect, disconnect or their
* individual available audio contexts are changed.
*/
types::BidirectionalPair<types::AudioContexts> group_available_contexts_;
/* A temporary mask for bits which were either added or removed when the
* group available context type changes. It usually means we should refresh
* our group configuration capabilities to clear this.
*/
types::AudioContexts pending_group_available_contexts_change_;
/* Mask of currently allowed context types. Not set a value not set will
* result in streaming rejection.
*/
types::BidirectionalPair<types::AudioContexts>
group_user_allowed_context_mask_;
/* Possible configuration cache - refreshed on each group context availability
* change. Stored as a pair of (is_valid_cache, configuration*). `pair.first`
* being `false` means that the cached value should be refreshed.
*/
mutable std::map<
types::LeAudioContextType,
std::pair<bool, const std::shared_ptr<
set_configurations::AudioSetConfiguration>>>
context_to_configuration_cache_map;
types::AseState target_state_;
types::AseState current_state_;
bool in_transition_;
std::vector<std::weak_ptr<LeAudioDevice>> leAudioDevices_;
};
/* LeAudioDeviceGroup class represents a wraper helper over all device groups in
* le audio implementation. It allows to operate on device group from a list
* (vector container) using determinants like id.
*/
class LeAudioDeviceGroups {
public:
LeAudioDeviceGroup* Add(int group_id);
void Remove(const int group_id);
LeAudioDeviceGroup* FindById(int group_id) const;
std::vector<int> GetGroupsIds(void) const;
size_t Size() const;
bool IsAnyInTransition() const;
void Cleanup(void);
void Dump(int fd, int active_group_id) const;
private:
std::vector<std::unique_ptr<LeAudioDeviceGroup>> groups_;
};
} // namespace bluetooth::le_audio