summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--audio_hal_interface/aidl/a2dp_encoding.cc63
-rw-r--r--audio_hal_interface/aidl/a2dp_transport.h5
-rw-r--r--audio_hal_interface/aidl/bluetooth_audio_port_impl.cc4
-rw-r--r--audio_hal_interface/aidl/client_interface.cc12
-rw-r--r--audio_hal_interface/aidl/client_interface.h6
-rw-r--r--audio_hal_interface/aidl/hearing_aid_software_encoding.cc5
-rw-r--r--audio_hal_interface/aidl/le_audio_software.cc10
-rw-r--r--audio_hal_interface/aidl/le_audio_software.h9
-rw-r--r--audio_hal_interface/aidl/transport_instance.h5
-rw-r--r--bta/Android.bp5
-rw-r--r--bta/has/has_client.cc2187
-rw-r--r--bta/has/has_client_test.cc3202
-rw-r--r--bta/has/has_ctp.cc287
-rw-r--r--bta/has/has_ctp.h261
-rw-r--r--bta/has/has_journal.cc58
-rw-r--r--bta/has/has_journal.h113
-rw-r--r--bta/has/has_preset.cc116
-rw-r--r--bta/has/has_preset.h114
-rw-r--r--bta/has/has_types.cc30
-rw-r--r--bta/has/has_types.h420
-rw-r--r--bta/include/bta_has_api.h54
-rw-r--r--btif/Android.bp2
-rw-r--r--btif/include/btif_ahim.h5
-rw-r--r--btif/include/btif_storage.h11
-rw-r--r--btif/src/bluetooth.cc10
-rw-r--r--btif/src/btif_a2dp_source.cc7
-rw-r--r--btif/src/btif_ahim.cc122
-rw-r--r--btif/src/btif_dm.cc27
-rw-r--r--btif/src/btif_has_client.cc245
-rw-r--r--btif/src/btif_storage.cc138
-rw-r--r--include/hardware/bluetooth.h1
-rw-r--r--include/hardware/bt_has.h156
-rw-r--r--internal_include/bt_target.h8
33 files changed, 81 insertions, 7617 deletions
diff --git a/audio_hal_interface/aidl/a2dp_encoding.cc b/audio_hal_interface/aidl/a2dp_encoding.cc
index 722d09334..3ed701dcd 100644
--- a/audio_hal_interface/aidl/a2dp_encoding.cc
+++ b/audio_hal_interface/aidl/a2dp_encoding.cc
@@ -14,8 +14,7 @@
* limitations under the License.
*/
/*
- * Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
@@ -169,52 +168,20 @@ bool A2dpTransport::GetPresentationPosition(uint64_t* remote_delay_report_ns,
}
void A2dpTransport::SourceMetadataChanged(
- const source_metadata_t& source_metadata) {
-
+ const source_metadata_t& source_metadata) {
auto track_count = source_metadata.track_count;
auto tracks = source_metadata.tracks;
-
LOG(INFO) << __func__ << "AIDL: " << track_count << " track(s) received";
- if (track_count == 0) {
- LOG(WARNING) << __func__ << ": Invalid number of metadata changed tracks";
- return;
+ while (track_count) {
+ LOG(INFO) << __func__ << "AIDL: usage=" << tracks->usage
+ << ", content_type=" << tracks->content_type
+ << ", gain=" << tracks->gain;
+ --track_count;
+ ++tracks;
}
-
- auto usage = source_metadata.tracks->usage;
-
- LOG(INFO) << __func__ << ", content_type=" << tracks->content_type
- << ", track_count: " << track_count
- << ", usage: " << usage;
-
- btif_ahim_update_src_metadata(source_metadata);
-
}
-void A2dpTransport::SinkMetadataChanged(
- const sink_metadata_t& sink_metadata) {
-
- auto track_count = sink_metadata.track_count;
- auto tracks = sink_metadata.tracks;
-
- LOG(INFO) << __func__ << "AIDL: " << track_count << " track(s) received";
- if (track_count == 0) {
- LOG(WARNING) << __func__ << ": Invalid number of metadata changed tracks";
- return;
- }
-
- auto source = sink_metadata.tracks->source;
-
- LOG(INFO) << __func__ << ", track_count: " << track_count
- << ", source: " << source;
-
- btif_ahim_update_sink_metadata(sink_metadata);
-
-}
-
-void A2dpTransport::SetLatencyMode(bool is_low_latency) {
- LOG(INFO) << __func__ << " is_low_latency: " << is_low_latency;
- btif_ahim_set_latency_mode(is_low_latency);
-}
+void A2dpTransport::SinkMetadataChanged(const sink_metadata_t&) {}
tA2DP_CTRL_CMD A2dpTransport::GetPendingCmd() const {
LOG(ERROR) << ": AIDL Is this function called";
@@ -638,18 +605,6 @@ bool setup_codec() {
pcm_config_global = pcm_config;
audio_config.set<AudioConfiguration::pcmConfig>(pcm_config);
}
-
- A2dpCodecConfig* a2dp_config = bta_av_get_a2dp_current_codec();
- if (a2dp_config && active_hal_interface) {
- btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig();
- if (current_codec.codec_type == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_ADAPTIVE) {
- active_hal_interface->set_low_latency_allowed(true);
- }
- else {
- active_hal_interface->set_low_latency_allowed(false);
- }
- }
-
return active_hal_interface->UpdateAudioConfig(audio_config);
}
diff --git a/audio_hal_interface/aidl/a2dp_transport.h b/audio_hal_interface/aidl/a2dp_transport.h
index 8371dcb54..6c2c1e423 100644
--- a/audio_hal_interface/aidl/a2dp_transport.h
+++ b/audio_hal_interface/aidl/a2dp_transport.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
/*
- * Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
@@ -84,8 +83,6 @@ class A2dpTransport
void SinkMetadataChanged(const sink_metadata_t&) override;
- void SetLatencyMode(bool is_low_latency);
-
tA2DP_CTRL_CMD GetPendingCmd() const;
void ResetPendingCmd();
diff --git a/audio_hal_interface/aidl/bluetooth_audio_port_impl.cc b/audio_hal_interface/aidl/bluetooth_audio_port_impl.cc
index 25f321354..cb616a809 100644
--- a/audio_hal_interface/aidl/bluetooth_audio_port_impl.cc
+++ b/audio_hal_interface/aidl/bluetooth_audio_port_impl.cc
@@ -14,8 +14,7 @@
* limitations under the License.
*/
/*
- * Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
@@ -173,7 +172,6 @@ ndk::ScopedAStatus BluetoothAudioPortImpl::updateSinkMetadata(
ndk::ScopedAStatus BluetoothAudioPortImpl::setLatencyMode(
LatencyMode latency_mode) {
bool is_low_latency = latency_mode == LatencyMode::LOW_LATENCY ? true : false;
- transport_instance_->SetLatencyMode(is_low_latency);
return ndk::ScopedAStatus::ok();
}
diff --git a/audio_hal_interface/aidl/client_interface.cc b/audio_hal_interface/aidl/client_interface.cc
index 1edd236df..b6e9b8dd4 100644
--- a/audio_hal_interface/aidl/client_interface.cc
+++ b/audio_hal_interface/aidl/client_interface.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
/* Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
@@ -110,12 +110,6 @@ bool BluetoothAudioClientInterface::is_aidl_available() {
return aidl_available;
}
-void BluetoothAudioClientInterface::set_low_latency_allowed(
- bool is_low_latency_allowed) {
- LOG(INFO) << __func__ << ": is_low_latency_allowed: " << is_low_latency_allowed;
- is_low_latency_allowed_ = is_low_latency_allowed;
-}
-
std::vector<AudioCapabilities>
BluetoothAudioClientInterface::GetAudioCapabilities() const {
LOG(INFO) << __func__ << ": AIDL";
@@ -384,10 +378,6 @@ int BluetoothAudioClientInterface::StartSession() {
DataMQDesc mq_desc;
std::vector<LatencyMode> latency_modes = {LatencyMode::FREE};
- if (is_low_latency_allowed_) {
- LOG(INFO) << __func__ << ": Adding Low latency to latency modes: ";
- latency_modes.push_back(LatencyMode::LOW_LATENCY);
- }
binder_status_t binder_status = AIBinder_linkToDeath(
provider_factory_->asBinder().get(), death_recipient_.get(), this);
diff --git a/audio_hal_interface/aidl/client_interface.h b/audio_hal_interface/aidl/client_interface.h
index 918fc45c7..ac8132456 100644
--- a/audio_hal_interface/aidl/client_interface.h
+++ b/audio_hal_interface/aidl/client_interface.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
/*
- * Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
@@ -123,8 +122,6 @@ class BluetoothAudioClientInterface {
void FlushAudioData();
- void set_low_latency_allowed(bool is_low_latency_allowed);
-
static constexpr PcmConfiguration kInvalidPcmConfiguration = {};
static bool is_aidl_available();
@@ -160,7 +157,6 @@ class BluetoothAudioClientInterface {
private:
static inline bool aidl_available = true;
- bool is_low_latency_allowed_ = false;
IBluetoothTransportInstance* transport_;
std::vector<AudioCapabilities> capabilities_;
static std::unordered_set<BluetoothAudioClientInterface *> objs_address_;
diff --git a/audio_hal_interface/aidl/hearing_aid_software_encoding.cc b/audio_hal_interface/aidl/hearing_aid_software_encoding.cc
index 55c142c6d..b41fd9bb3 100644
--- a/audio_hal_interface/aidl/hearing_aid_software_encoding.cc
+++ b/audio_hal_interface/aidl/hearing_aid_software_encoding.cc
@@ -14,8 +14,7 @@
* limitations under the License.
*/
/*
- * Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
@@ -144,8 +143,6 @@ class HearingAidTransport
void SinkMetadataChanged(const sink_metadata_t&) override {}
- void SetLatencyMode(bool is_low_latency) override {}
-
void ResetPresentationPosition() override {
VLOG(2) << __func__ << ": called.";
remote_delay_report_ms_ = 0;
diff --git a/audio_hal_interface/aidl/le_audio_software.cc b/audio_hal_interface/aidl/le_audio_software.cc
index 407ca2ce5..1cd5329d4 100644
--- a/audio_hal_interface/aidl/le_audio_software.cc
+++ b/audio_hal_interface/aidl/le_audio_software.cc
@@ -16,7 +16,7 @@
*/
/*
* Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
@@ -207,8 +207,6 @@ void LeAudioTransport::SinkMetadataChanged(
btif_ahim_update_sink_metadata(sink_metadata);
}
-void LeAudioTransport::SetLatencyMode(bool is_low_latency) {
-}
tA2DP_CTRL_CMD LeAudioTransport:: GetPendingCmd() const {
return lea_pending_cmd_;
@@ -337,9 +335,6 @@ void LeAudioSinkTransport::SinkMetadataChanged(
transport_->SinkMetadataChanged(sink_metadata);
}
-void LeAudioSinkTransport::SetLatencyMode(bool is_low_latency) {
-}
-
void LeAudioSinkTransport::ResetPresentationPosition() {
transport_->ResetPresentationPosition();
}
@@ -437,9 +432,6 @@ void LeAudioSourceTransport::SinkMetadataChanged(
transport_->SinkMetadataChanged(sink_metadata);
}
-void LeAudioSourceTransport::SetLatencyMode(bool is_low_latency) {
-}
-
void LeAudioSourceTransport::ResetPresentationPosition() {
transport_->ResetPresentationPosition();
}
diff --git a/audio_hal_interface/aidl/le_audio_software.h b/audio_hal_interface/aidl/le_audio_software.h
index 2b4af9f27..faa3a61fb 100644
--- a/audio_hal_interface/aidl/le_audio_software.h
+++ b/audio_hal_interface/aidl/le_audio_software.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
/*
- * Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
@@ -118,8 +117,6 @@ class LeAudioTransport {
void SinkMetadataChanged(const sink_metadata_t& sink_metadata);
- void SetLatencyMode(bool is_low_latency);
-
tA2DP_CTRL_CMD GetPendingCmd() const;
void ResetPendingCmd();
@@ -176,8 +173,6 @@ class LeAudioSinkTransport
void SinkMetadataChanged(const sink_metadata_t& sink_metadata) override;
- void SetLatencyMode(bool is_low_latency) override;
-
tA2DP_CTRL_CMD GetPendingCmd() const;
void ResetPendingCmd();
@@ -229,8 +224,6 @@ class LeAudioSourceTransport
void SinkMetadataChanged(const sink_metadata_t& sink_metadata) override;
- void SetLatencyMode(bool is_low_latency) override;
-
tA2DP_CTRL_CMD GetPendingCmd() const;
void ResetPendingCmd();
diff --git a/audio_hal_interface/aidl/transport_instance.h b/audio_hal_interface/aidl/transport_instance.h
index 56e2a0eae..f1d0311d5 100644
--- a/audio_hal_interface/aidl/transport_instance.h
+++ b/audio_hal_interface/aidl/transport_instance.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
/*
- * Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
@@ -141,8 +140,6 @@ class IBluetoothTransportInstance {
const source_metadata_t& source_metadata) = 0;
virtual void SinkMetadataChanged(const sink_metadata_t& sink_metadata) = 0;
- virtual void SetLatencyMode(bool is_low_latency) = 0;
-
//virtual tA2DP_CTRL_CMD GetPendingCmd() const;
//virtual void ResetPendingCmd();
diff --git a/bta/Android.bp b/bta/Android.bp
index 4610d3a9b..ec9646e73 100644
--- a/bta/Android.bp
+++ b/bta/Android.bp
@@ -123,11 +123,6 @@ cc_library_static {
"gatt/database_builder.cc",
"hearing_aid/hearing_aid.cc",
"hearing_aid/hearing_aid_audio_source.cc",
- "has/has_client.cc",
- "has/has_ctp.cc",
- "has/has_journal.cc",
- "has/has_preset.cc",
- "has/has_types.cc",
"hf_client/bta_hf_client_act.cc",
"hf_client/bta_hf_client_api.cc",
"hf_client/bta_hf_client_at.cc",
diff --git a/bta/has/has_client.cc b/bta/has/has_client.cc
deleted file mode 100644
index 02f65822b..000000000
--- a/bta/has/has_client.cc
+++ /dev/null
@@ -1,2187 +0,0 @@
-/*
- * Copyright 2021 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.
- *
- * Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
- * SPDX-License-Identifier: BSD-3-Clause-Clear
- */
-
-#include <base/bind.h>
-#include <base/callback.h>
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <hardware/bt_gatt_types.h>
-#include <hardware/bt_has.h>
-
-#include <list>
-#include <map>
-#include <string>
-#include <vector>
-
-#include "bta_csip_api.h"
-#include "bta_gatt_api.h"
-#include "bta_gatt_queue.h"
-//#include "bta_groups.h"
-#include "bta_has_api.h"
-//#include "bta_le_audio_uuids.h"
-#include "btm_int.h"
-#include "btm_api.h"
-#include "bt_types.h"
-//#include "btm_sec.h"
-#include "device/include/controller.h"
-#include "gap_api.h"
-#include "gatt_api.h"
-#include "has_types.h"
-#include "osi/include/log.h"
-#include "osi/include/osi.h"
-#include "osi/include/properties.h"
-
-using base::Closure;
-using bluetooth::Uuid;
-//using bluetooth::csis::CsisClient;
-using bluetooth::has::ConnectionState;
-using bluetooth::has::ErrorCode;
-using bluetooth::has::kFeatureBitPresetSynchronizationSupported;
-using bluetooth::has::kHasPresetIndexInvalid;
-using bluetooth::has::PresetInfo;
-using bluetooth::has::PresetInfoReason;
-using le_audio::has::HasClient;
-using le_audio::has::HasCtpGroupOpCoordinator;
-using le_audio::has::HasCtpNtf;
-using le_audio::has::HasCtpOp;
-using le_audio::has::HasDevice;
-using le_audio::has::HasGattOpContext;
-using le_audio::has::HasJournalRecord;
-using le_audio::has::HasPreset;
-using le_audio::has::kControlPointMandatoryOpcodesBitmask;
-using le_audio::has::kControlPointSynchronizedOpcodesBitmask;
-using le_audio::has::kUuidActivePresetIndex;
-using le_audio::has::kUuidHearingAccessService;
-using le_audio::has::kUuidHearingAidFeatures;
-using le_audio::has::kUuidHearingAidPresetControlPoint;
-using le_audio::has::PresetCtpChangeId;
-using le_audio::has::PresetCtpOpcode;
-
-void btif_storage_add_leaudio_has_device(const RawAddress& address,
- std::vector<uint8_t> presets_bin,
- uint8_t features,
- uint8_t active_preset);
-bool btif_storage_get_leaudio_has_presets(const RawAddress& address,
- std::vector<uint8_t>& presets_bin,
- uint8_t& active_preset);
-void btif_storage_set_leaudio_has_presets(const RawAddress& address,
- std::vector<uint8_t> presets_bin);
-bool btif_storage_get_leaudio_has_features(const RawAddress& address,
- uint8_t& features);
-void btif_storage_set_leaudio_has_features(const RawAddress& address,
- uint8_t features);
-void btif_storage_set_leaudio_has_active_preset(const RawAddress& address,
- uint8_t active_preset);
-void btif_storage_remove_leaudio_has(const RawAddress& address);
-
-extern bool gatt_profile_get_eatt_support(const RawAddress& remote_bda);
-
-namespace {
-class HasClientImpl;
-HasClientImpl* instance;
-
-/**
- * -----------------------------------------------------------------------------
- * Hearing Access Service - Client role
- * -----------------------------------------------------------------------------
- * Overview:
- *
- * This is Hearing Access Service client class.
- *
- * Each connected peer device supporting Hearing Access Service (HAS) is being
- * connected and has its characteristics discovered. All the characteristics
- * and descriptors (incl. the optional ones) are being read or written during
- * this initial connection stage. Encryption is also verified. If all of this
- * succeeds the appropriate callbacks are being called to notify upper layer
- * about the successful HAS device connection and its features and the list
- * of available audio configuration presets.
- *
- * Each HA device is expected to have the HAS service instantiated. It must
- * contain Hearing Aid Features characteristic and optionally Presets Control
- * Point and Active Preset Index characteristics, allowing the user to read
- * preset details, switch currently active preset and possibly rename some of
- * them.
- *
- * Hearing Aid Features characteristic informs the client about the type of
- * Hearign Aids device (Monaural, Binaural or Banded), which operations are
- * supported via the Preset Control Point characteristic, about dynamically
- * changing list of available presets, writable presets and the support for
- * synchronised preset change operations on the Binaural Hearing Aid devices.
- */
-class HasClientImpl : public HasClient {
- public:
- HasClientImpl(bluetooth::has::HasClientCallbacks* callbacks,
- base::Closure initCb)
- : gatt_if_(0), callbacks_(callbacks) {
- BTA_GATTC_AppRegister(
- [](tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
- if (instance && p_data) instance->GattcCallback(event, p_data);
- },
- base::Bind(
- [](base::Closure initCb, uint8_t client_id, uint8_t status) {
- if (status != GATT_SUCCESS) {
- LOG(ERROR) << ": Can't start Hearing Aid Service client "
- "profile - no gatt clients left!";
- return;
- }
- if (instance) {
- instance->gatt_if_ = client_id;
- }
- initCb.Run();
- },
- initCb),
- true);
- }
-
- ~HasClientImpl() override = default;
-
- void Connect(const RawAddress& address) override {
- LOG(INFO) << __func__ << ": " << address;
-
- std::vector<RawAddress> addresses = {address};
- tBTA_CSIP_CSET cset_info;
- memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
- cset_info = BTA_CsipGetCoordinatedSet(address.address[5]);
- LOG(INFO) << __func__ << ":set size: " << loghex(cset_info.size);
- addresses = cset_info.set_members;
- //RawAddress addr = {};
- /*auto csis_api = CsisClient::Get();
- if (csis_api != nullptr) {
- // Connect entire CAS set of devices
- auto group_id = csis_api->GetGroupId(
- address, bluetooth::Uuid::From16Bit(UUID_COMMON_AUDIO_SERVICE));
- addresses = csis_api->GetDeviceList(group_id);
- }*/
-
- if (addresses.empty()) {
- LOG(WARNING) << __func__ << ": " << address << " is not part of any set";
- addresses = {address};
- }
-
- for (auto const& addr : addresses) {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(addr));
- if (device == devices_.end()) {
- devices_.emplace_back(addr, true);
- LOG(WARNING) << __func__ << ": " << address << " Gattc Open is called";
- BTA_GATTC_Open(gatt_if_, addr, true, GATT_TRANSPORT_LE, false);
-
- } else {
- device->is_connecting_actively = true;
- if (!device->IsConnected())
- BTA_GATTC_Open(gatt_if_, addr, true, GATT_TRANSPORT_LE, false);
- }
- }
- }
-
- void AddFromStorage(const RawAddress& address, uint8_t features,
- uint16_t is_acceptlisted) {
- LOG(INFO) << __func__ << ": " <<address
- << ", features=" << loghex(features)
- << ", isAcceptlisted=" << is_acceptlisted;
-
- /* Notify upper layer about the device */
- callbacks_->OnDeviceAvailable(address, features);
- if (is_acceptlisted) {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(address));
- if (device == devices_.end())
- devices_.push_back(HasDevice(address, features));
-
- /* Connect in background */
- BTA_GATTC_Open(gatt_if_, address, true, BT_TRANSPORT_LE, false);
- }
- }
-
- void Disconnect(const RawAddress& address) override {
- LOG(INFO) << __func__ << ": " << address;
-
- std::vector<RawAddress> addresses = {address};
- tBTA_CSIP_CSET cset_info;
- memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
- cset_info = BTA_CsipGetCoordinatedSet(address.address[5]);
- LOG(INFO) << __func__ << ": set size: " << loghex(cset_info.size);
- addresses = cset_info.set_members;
- /*auto csis_api = CsisClient::Get();
- if (csis_api != nullptr) {
- // Disconnect entire CAS set of devices
- auto group_id = csis_api->GetGroupId(
- address, bluetooth::Uuid::From16Bit(UUID_COMMON_AUDIO_SERVICE));
- addresses = csis_api->GetDeviceList(group_id);
- }*/
-
- if (addresses.empty()) {
- LOG(WARNING) << __func__ << ": " << address << " is not part of any set";
- addresses = {address};
- }
-
- for (auto const& addr : addresses) {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(addr));
- if (device == devices_.end()) {
- LOG(WARNING) << ": Device not connected to profile" << addr;
- return;
- }
-
- auto conn_id = device->conn_id;
- auto is_connecting_actively = device->is_connecting_actively;
- devices_.erase(device);
-
- if (conn_id != GATT_INVALID_CONN_ID) {
- BTA_GATTC_Close(conn_id);
- callbacks_->OnConnectionState(ConnectionState::DISCONNECTED, addr);
- } else {
- /* Removes active connection. */
- if (is_connecting_actively) BTA_GATTC_CancelOpen(gatt_if_, addr, true);
- }
-
- /* Removes all registrations for connection. */
- BTA_GATTC_CancelOpen(0, addr, false);
- }
- }
-
- void UpdateJournalOpEntryStatus(HasDevice& device, HasGattOpContext context,
- tGATT_STATUS status) {
- /* Find journal entry by the context and update */
- auto journal_entry = std::find_if(
- device.has_journal_.begin(), device.has_journal_.end(),
- [&context](auto const& record) {
- if (record.is_operation) {
- return HasGattOpContext(record.op_context_handle) == context;
- }
- return false;
- });
-
- if (journal_entry == device.has_journal_.end()) {
- LOG(WARNING) << ": Journaling error or journal length limit was set to "
- "low. Unable to log the operation outcome.";
- return;
- }
-
- if (journal_entry == device.has_journal_.end()) {
- LOG(ERROR) << __func__
- << ": Unable to find operation context in the journal!";
- return;
- }
-
- journal_entry->op_status = status;
- }
-
- std::optional<HasCtpOp> ExtractPendingCtpOp(uint16_t op_id) {
- auto op_it =
- std::find_if(pending_operations_.begin(), pending_operations_.end(),
- [op_id](auto const& el) { return op_id == el.op_id; });
-
- if (op_it != pending_operations_.end()) {
- auto op = *op_it;
- pending_operations_.erase(op_it);
-
- return op;
- }
- return std::nullopt;
- }
-
- void EnqueueCtpOp(HasCtpOp op) { pending_operations_.push_back(op); }
-
- void OnHasActivePresetCycleStatus(uint16_t conn_id, tGATT_STATUS status,
- void* user_data) {
- LOG(INFO) << __func__ << ": status: " << +status;
-
- auto device = GetDevice(conn_id);
- if (!device) {
- LOG(WARNING) << ": Device not connected to profile, conn_id=" << +conn_id;
- return;
- }
-
- /* Journal update */
- LOG_ASSERT(user_data != nullptr) << "Has operation context is missing!";
- auto context = HasGattOpContext(user_data);
- UpdateJournalOpEntryStatus(*device, context, status);
-
- auto op_opt = ExtractPendingCtpOp(context.ctp_op_id);
- if (status == GATT_SUCCESS) return;
-
- /* This could be one of the coordinated group preset change request */
- pending_group_operation_timeouts_.erase(context.ctp_op_id);
-
- /* Error handling */
- if (!op_opt.has_value()) {
- LOG(ERROR) << __func__ << ": Unknown operation error";
- return;
- }
- auto op = op_opt.value();
- callbacks_->OnActivePresetSelectError(op.addr_or_group,
- GattStatus2SvcErrorCode(status));
-
- if (status == GATT_DATABASE_OUT_OF_SYNC) {
-// LOG_INFO("Database out of sync for %s", device->addr);
- ClearDeviceInformationAndStartSearch(device);
- }
- }
-
- void OnHasPresetNameSetStatus(uint16_t conn_id, tGATT_STATUS status,
- void* user_data) {
- auto device = GetDevice(conn_id);
- if (!device) {
- LOG(WARNING) << ": Device not connected to profile, conn_id=" << +conn_id;
- return;
- }
-
- LOG_ASSERT(user_data != nullptr) << ": Has operation context is missing!";
- HasGattOpContext context(user_data);
-
- /* Journal update */
- UpdateJournalOpEntryStatus(*device, context, status);
-
- auto op_opt = ExtractPendingCtpOp(context.ctp_op_id);
- if (status == GATT_SUCCESS) return;
-
- /* This could be one of the coordinated group preset change request */
- pending_group_operation_timeouts_.erase(context.ctp_op_id);
-
- /* Error handling */
- if (!op_opt.has_value()) {
- LOG(ERROR) << __func__ << ": Unknown operation error";
- return;
- }
- auto op = op_opt.value();
- callbacks_->OnSetPresetNameError(device->addr, op.index,
- GattStatus2SvcErrorCode(status));
- if (status == GATT_DATABASE_OUT_OF_SYNC) {
- //LOG_INFO("Database out of sync for %s", device->addr);
- ClearDeviceInformationAndStartSearch(device);
- }
- }
-
- void OnHasPresetNameGetStatus(uint16_t conn_id, tGATT_STATUS status,
- void* user_data) {
- auto device = GetDevice(conn_id);
- if (!device) {
- LOG(WARNING) << ": Device not connected to profile, conn_id=" << +conn_id;
- return;
- }
-
- LOG_ASSERT(user_data != nullptr) << ": Has operation context is missing!";
- HasGattOpContext context(user_data);
-
- /* Journal update */
- UpdateJournalOpEntryStatus(*device, context, status);
-
- auto op_opt = ExtractPendingCtpOp(context.ctp_op_id);
- if (status == GATT_SUCCESS) return;
-
- /* Error handling */
- if (!op_opt.has_value()) {
- LOG(ERROR) << __func__ << ": Unknown operation error";
- return;
- }
- auto op = op_opt.value();
- callbacks_->OnPresetInfoError(device->addr, op.index,
- GattStatus2SvcErrorCode(status));
-
- if (status == GATT_DATABASE_OUT_OF_SYNC) {
- //LOG_INFO("Database out of sync for %s", device->addr);
- ClearDeviceInformationAndStartSearch(device);
- } else {
- //LOG_ERROR("Devices %s: Control point not usable. Disconnecting!", device->addr);
- BTA_GATTC_Close(device->conn_id);
- }
- }
-
- void OnHasPresetIndexOperation(uint16_t conn_id, tGATT_STATUS status,
- void* user_data) {
- LOG(INFO) << __func__;
-
- auto device = GetDevice(conn_id);
- if (!device) {
- LOG(WARNING) << ": Device not connected to profile, conn_id=" << +conn_id;
- return;
- }
-
- LOG_ASSERT(user_data != nullptr) << ": Has operation context is missing!";
- HasGattOpContext context(user_data);
-
- /* Journal update */
- UpdateJournalOpEntryStatus(*device, context, status);
-
- auto op_opt = ExtractPendingCtpOp(context.ctp_op_id);
- if (status == GATT_SUCCESS) return;
-
- /* This could be one of the coordinated group preset change request */
- pending_group_operation_timeouts_.erase(context.ctp_op_id);
-
- /* Error handling */
- if (!op_opt.has_value()) {
- LOG(ERROR) << __func__ << ": Unknown operation error";
- return;
- }
-
- auto op = op_opt.value();
- if (op.opcode == PresetCtpOpcode::READ_PRESETS) {
- callbacks_->OnPresetInfoError(device->addr, op.index,
- GattStatus2SvcErrorCode(status));
-
- } else {
- callbacks_->OnActivePresetSelectError(op.addr_or_group,
- GattStatus2SvcErrorCode(status));
- }
-
- if (status == GATT_DATABASE_OUT_OF_SYNC) {
- //LOG_INFO("Database out of sync for %s", device->addr);
- ClearDeviceInformationAndStartSearch(device);
- } else {
- //LOG_ERROR("Devices %s: Control point not usable. Disconnecting!", device->addr);
- BTA_GATTC_Close(device->conn_id);
- }
- }
-
- void CpReadAllPresetsOperation(HasCtpOp operation) {
- LOG(INFO) << __func__ << ": Operation: " << operation;
-
- if (std::holds_alternative<int>(operation.addr_or_group)) {
- LOG(ERROR) << __func__
- << ": Read all presets on the entire group not supported.";
- callbacks_->OnPresetInfoError(operation.addr_or_group, operation.index,
- ErrorCode::OPERATION_NOT_POSSIBLE);
- return;
- }
-
- auto device = std::find_if(
- devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(std::get<RawAddress>(operation.addr_or_group)));
- if (device == devices_.end()) {
- LOG(WARNING) << __func__ << ": Device not connected to profile addr: "
- << std::get<RawAddress>(operation.addr_or_group);
- callbacks_->OnPresetInfoError(device->addr, operation.index,
- ErrorCode::OPERATION_NOT_POSSIBLE);
- return;
- }
-
- if (!device->SupportsPresets()) {
- callbacks_->OnPresetInfoError(device->addr, operation.index,
- ErrorCode::OPERATION_NOT_SUPPORTED);
- }
-
- auto context = HasGattOpContext(operation);
-
- /* Journal update */
- device->has_journal_.Append(HasJournalRecord(operation, context));
-
- /* Write to control point */
- EnqueueCtpOp(operation);
- BtaGattQueue::WriteCharacteristic(
- device->conn_id, device->cp_handle, operation.ToCharacteristicValue(),
- GATT_WRITE,
- [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
- const uint8_t* value, void* user_data) {
- if (instance)
- instance->OnHasPresetNameGetStatus(conn_id, status, user_data);
- },
- context);
- }
-
- ErrorCode CpPresetIndexOperationWriteReq(HasDevice& device,
- HasCtpOp& operation) {
- LOG(INFO) << __func__ << ": Operation: " << operation;
-
- if (!device.IsConnected()) return ErrorCode::OPERATION_NOT_POSSIBLE;
-
- if (!device.SupportsPresets()) return ErrorCode::OPERATION_NOT_SUPPORTED;
-
- if (!device.SupportsOperation(operation.opcode))
- return operation.IsGroupRequest()
- ? ErrorCode::GROUP_OPERATION_NOT_SUPPORTED
- : ErrorCode::OPERATION_NOT_SUPPORTED;
-
- if (!device.IsValidPreset(operation.index))
- return ErrorCode::INVALID_PRESET_INDEX;
-
- auto context = HasGattOpContext(operation);
-
- /* Journal update */
- device.has_journal_.Append(HasJournalRecord(operation, context));
-
- /* Write to control point */
- EnqueueCtpOp(operation);
- BtaGattQueue::WriteCharacteristic(
- device.conn_id, device.cp_handle, operation.ToCharacteristicValue(),
- GATT_WRITE,
- [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
- const uint8_t* value, void* user_data) {
- if (instance)
- instance->OnHasPresetIndexOperation(conn_id, status, user_data);
- },
- context);
-
- return ErrorCode::NO_ERROR;
- }
-
- bool AreAllDevicesAvailable(const std::vector<RawAddress>& addresses) {
- for (auto& addr : addresses) {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(addr));
- if (device == devices_.end() || !device->IsConnected()) {
- return false;
- }
- }
- return true;
- }
-
- ErrorCode CpPresetOperationCaller(
- HasCtpOp operation,
- std::function<ErrorCode(HasDevice& device, HasCtpOp& operation)>
- write_cb) {
- LOG(INFO) << __func__ << ": Operation: " << operation;
- auto status = ErrorCode::NO_ERROR;
-
- if (operation.IsGroupRequest()) {
- /*auto csis_api = CsisClient::Get();
- if (csis_api == nullptr) {
- No CSIS means no group operations
- status = ErrorCode::GROUP_OPERATION_NOT_SUPPORTED;
-
- } else {
- auto group_id = operation.GetGroupId();
- auto addresses = csis_api->GetDeviceList(group_id);*/
- std::vector<RawAddress> addresses;
- tBTA_CSIP_CSET cset_info;
- memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
- cset_info = BTA_CsipGetCoordinatedSet(operation.GetDeviceAddr().address[5]);
- LOG(INFO) << __func__ << ": set size: " << loghex(cset_info.size);
- addresses = cset_info.set_members;
-
- /* Perform the operation only when all the devices are available */
- if (!AreAllDevicesAvailable(addresses)) {
- addresses.clear();
- }
-
- if (addresses.empty()) {
- status = ErrorCode::OPERATION_NOT_POSSIBLE;
-
- } else {
- /* Make this a coordinated operation */
- pending_group_operation_timeouts_.emplace(
- operation.op_id, HasCtpGroupOpCoordinator(addresses, operation));
-
- if (operation.IsSyncedOperation()) {
- status = ErrorCode::GROUP_OPERATION_NOT_SUPPORTED;
-
- /* Clear the error if we find device to forward the operation */
- bool was_sent = false;
- for (auto& addr : addresses) {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(addr));
- if (device != devices_.end()) {
- status = write_cb(*device, operation);
- if (status == ErrorCode::NO_ERROR) {
- was_sent = true;
- break;
- }
- }
- }
- if (!was_sent) status = ErrorCode::OPERATION_NOT_POSSIBLE;
-
- } else {
- status = ErrorCode::GROUP_OPERATION_NOT_SUPPORTED;
-
- for (auto& addr : addresses) {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(addr));
- if (device != devices_.end()) {
- status = write_cb(*device, operation);
- if (status != ErrorCode::NO_ERROR) break;
- }
- }
- }
-
- /* Erase group op coordinator on error */
- if (status != ErrorCode::NO_ERROR) {
- pending_group_operation_timeouts_.erase(operation.op_id);
- }
- }
- } else {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(std::get<RawAddress>(
- operation.addr_or_group)));
- status = ErrorCode::OPERATION_NOT_POSSIBLE;
- if (device != devices_.end()) status = write_cb(*device, operation);
- }
-
- return status;
- }
-
- void CpPresetIndexOperation(HasCtpOp operation) {
- LOG(INFO) << __func__ << ": Operation: " << operation;
-
- auto status = CpPresetOperationCaller(
- operation, [](HasDevice& device, HasCtpOp operation) -> ErrorCode {
- if (instance)
- return instance->CpPresetIndexOperationWriteReq(device, operation);
- return ErrorCode::OPERATION_NOT_POSSIBLE;
- });
-
- if (status != ErrorCode::NO_ERROR) {
- switch (operation.opcode) {
- case PresetCtpOpcode::READ_PRESETS:
- LOG_ASSERT(
- std::holds_alternative<RawAddress>(operation.addr_or_group))
- << ": Unsupported group operation!";
-
- callbacks_->OnPresetInfoError(
- std::get<RawAddress>(operation.addr_or_group), operation.index,
- status);
- break;
- case PresetCtpOpcode::SET_ACTIVE_PRESET:
- case PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC:
- callbacks_->OnActivePresetSelectError(operation.addr_or_group,
- status);
- break;
- default:
- break;
- }
- }
- }
-
- ErrorCode CpPresetsCycleOperationWriteReq(HasDevice& device,
- HasCtpOp& operation) {
- LOG(INFO) << __func__ << ": addr: " << device.addr
- << " operation: " << operation;
-
- if (!device.IsConnected()) return ErrorCode::OPERATION_NOT_POSSIBLE;
-
- if (!device.SupportsPresets()) return ErrorCode::OPERATION_NOT_SUPPORTED;
-
- if (!device.SupportsOperation(operation.opcode))
- return operation.IsGroupRequest()
- ? ErrorCode::GROUP_OPERATION_NOT_SUPPORTED
- : ErrorCode::OPERATION_NOT_SUPPORTED;
-
- auto context = HasGattOpContext(operation);
-
- /* Journal update */
- device.has_journal_.Append(HasJournalRecord(operation, context));
-
- /* Write to control point */
- EnqueueCtpOp(operation);
- BtaGattQueue::WriteCharacteristic(
- device.conn_id, device.cp_handle, operation.ToCharacteristicValue(),
- GATT_WRITE,
- [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
- const uint8_t* value, void* user_data) {
- if (instance)
- instance->OnHasActivePresetCycleStatus(conn_id, status, user_data);
- },
- context);
- return ErrorCode::NO_ERROR;
- }
-
- void CpPresetsCycleOperation(HasCtpOp operation) {
- LOG(INFO) << __func__ << ": Operation: " << operation;
-
- auto status = CpPresetOperationCaller(
- operation, [](HasDevice& device, HasCtpOp operation) -> ErrorCode {
- if (instance)
- return instance->CpPresetsCycleOperationWriteReq(device, operation);
- return ErrorCode::OPERATION_NOT_POSSIBLE;
- });
-
- if (status != ErrorCode::NO_ERROR)
- callbacks_->OnActivePresetSelectError(operation.addr_or_group, status);
- }
-
- ErrorCode CpWritePresetNameOperationWriteReq(HasDevice& device,
- HasCtpOp operation) {
- LOG(INFO) << __func__ << ": addr: " << device.addr
- << " operation: " << operation;
-
- if (!device.IsConnected()) return ErrorCode::OPERATION_NOT_POSSIBLE;
-
- if (!device.SupportsPresets()) return ErrorCode::OPERATION_NOT_SUPPORTED;
-
- if (!device.IsValidPreset(operation.index, true))
- return device.IsValidPreset(operation.index)
- ? ErrorCode::SET_NAME_NOT_ALLOWED
- : ErrorCode::INVALID_PRESET_INDEX;
-
- if (!device.SupportsOperation(operation.opcode))
- return ErrorCode::OPERATION_NOT_SUPPORTED;
-
- if (operation.name.value_or("").length() >
- le_audio::has::HasPreset::kPresetNameLengthLimit)
- return ErrorCode::INVALID_PRESET_NAME_LENGTH;
-
- auto context = HasGattOpContext(operation, operation.index);
-
- /* Journal update */
- device.has_journal_.Append(HasJournalRecord(operation, context));
-
- /* Write to control point */
- EnqueueCtpOp(operation);
- BtaGattQueue::WriteCharacteristic(
- device.conn_id, device.cp_handle, operation.ToCharacteristicValue(),
- GATT_WRITE,
- [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
- const uint8_t* value, void* user_data) {
- if (instance)
- instance->OnHasPresetNameSetStatus(conn_id, status, user_data);
- },
- context);
-
- return ErrorCode::NO_ERROR;
- }
-
- void CpWritePresetNameOperation(HasCtpOp operation) {
- LOG(INFO) << __func__ << ": operation: " << operation;
-
- auto status = ErrorCode::NO_ERROR;
-
- std::vector<RawAddress> addresses;
- //address = {operation.GetDeviceAddr()};
- if (operation.IsGroupRequest()) {
- /*auto csis_api = CsisClient::Get();
- if (csis_api != nullptr) {
- addresses = csis_api->GetDeviceList(operation.GetGroupId());*/
- tBTA_CSIP_CSET cset_info;
- memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
- cset_info = BTA_CsipGetCoordinatedSet(operation.GetDeviceAddr().address[5]);
- LOG(INFO) << __func__ << ": set size: " << loghex(cset_info.size);
- addresses = cset_info.set_members;
-
- /* Make this a coordinated operation */
- pending_group_operation_timeouts_.emplace(
- operation.op_id, HasCtpGroupOpCoordinator(addresses, operation));
- //}
-
- } else {
- addresses = {operation.GetDeviceAddr()};
- }
-
- status = ErrorCode::OPERATION_NOT_POSSIBLE;
-
- /* Perform the operation only when all the devices are available */
- if (!AreAllDevicesAvailable(addresses)) {
- addresses.clear();
- }
-
- for (auto& addr : addresses) {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(addr));
- if (device != devices_.end()) {
- status = CpWritePresetNameOperationWriteReq(*device, operation);
- if (status != ErrorCode::NO_ERROR) {
- LOG(ERROR) << __func__
- << ": Control point write error: " << (int)status;
- break;
- }
- }
- }
-
- if (status != ErrorCode::NO_ERROR) {
- if (operation.IsGroupRequest())
- pending_group_operation_timeouts_.erase(operation.op_id);
-
- callbacks_->OnSetPresetNameError(operation.addr_or_group, operation.index,
- status);
- }
- }
-
- bool shouldRequestSyncedOp(std::variant<RawAddress, int> addr_or_group_id,
- PresetCtpOpcode opcode) {
- /* Do not select locally synced ops when not performing group operations,
- * You never know if the user will make another call for the other devices
- * in this set even though the may support locally synced operations.
- */
- if (std::holds_alternative<RawAddress>(addr_or_group_id)) return false;
-
- /*auto csis_api = CsisClient::Get();
- if (csis_api == nullptr) return false;
-
- auto addresses = csis_api->GetDeviceList(std::get<int>(addr_or_group_id));*/
- std::vector<RawAddress> addresses;
- RawAddress address = std::get<RawAddress>(addr_or_group_id);
- tBTA_CSIP_CSET cset_info;
- memset(&cset_info, 0, sizeof(tBTA_CSIP_CSET));
- cset_info = BTA_CsipGetCoordinatedSet(address.address[5]);
- LOG(INFO) << __func__ << "set size: " << loghex(cset_info.size);
- addresses = cset_info.set_members;
- if (addresses.empty()) return false;
-
- for (auto& addr : addresses) {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(addr));
- if (device != devices_.end()) {
- if (device->SupportsOperation(opcode)) return true;
- }
- }
-
- return false;
- }
-
- void SelectActivePreset(std::variant<RawAddress, int> addr_or_group_id,
- uint8_t preset_index) override {
- LOG(INFO) << __func__;
-
- auto opcode = shouldRequestSyncedOp(addr_or_group_id,
- PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC)
- ? PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC
- : PresetCtpOpcode::SET_ACTIVE_PRESET;
-
- CpPresetIndexOperation(HasCtpOp(addr_or_group_id, opcode, preset_index));
- }
-
- void NextActivePreset(
- std::variant<RawAddress, int> addr_or_group_id) override {
- LOG(INFO) << __func__;
-
- auto opcode = shouldRequestSyncedOp(addr_or_group_id,
- PresetCtpOpcode::SET_NEXT_PRESET_SYNC)
- ? PresetCtpOpcode::SET_NEXT_PRESET_SYNC
- : PresetCtpOpcode::SET_NEXT_PRESET;
-
- CpPresetsCycleOperation(HasCtpOp(addr_or_group_id, opcode));
- }
-
- void PreviousActivePreset(
- std::variant<RawAddress, int> addr_or_group_id) override {
- LOG(INFO) << __func__;
-
- auto opcode = shouldRequestSyncedOp(addr_or_group_id,
- PresetCtpOpcode::SET_PREV_PRESET_SYNC)
- ? PresetCtpOpcode::SET_PREV_PRESET_SYNC
- : PresetCtpOpcode::SET_PREV_PRESET;
-
- CpPresetsCycleOperation(HasCtpOp(addr_or_group_id, opcode));
- }
-
- void GetPresetInfo(const RawAddress& address, uint8_t preset_index) override {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(address));
- if (device == devices_.end()) {
- LOG(WARNING) << ": Device not connected to profile"
- << address;
- return;
- }
-
- LOG(INFO) << __func__ << ": preset idx: " << +preset_index;
-
- /* Due to mandatory control point notifications or indications, preset
- * details are always up to date. However we have to be able to do the
- * READ_PRESET_BY_INDEX, to pass the test specification requirements.
- */
- if (property_get_bool("persist.bluetooth.has.always_use_preset_cache",
- true)) {
- auto* preset = device->GetPreset(preset_index);
- if (preset == nullptr) {
- LOG(ERROR) << __func__ << ": Invalid preset request"
- << address;
- callbacks_->OnPresetInfoError(address, preset_index,
- ErrorCode::INVALID_PRESET_INDEX);
- return;
- }
-
- callbacks_->OnPresetInfo(address,
- PresetInfoReason::PRESET_INFO_REQUEST_RESPONSE,
- {{.preset_index = preset_index,
- .writable = preset->IsWritable(),
- .available = preset->IsAvailable(),
- .preset_name = preset->GetName()}});
- } else {
- CpPresetIndexOperation(
- HasCtpOp(address, PresetCtpOpcode::READ_PRESETS, preset_index));
- }
- }
-
- void SetPresetName(std::variant<RawAddress, int> addr_or_group_id,
- uint8_t preset_index, std::string name) override {
- LOG(INFO) << __func__ << ": preset_idx: " << +preset_index
- << ", name: " << name;
-
- CpWritePresetNameOperation(HasCtpOp(addr_or_group_id,
- PresetCtpOpcode::WRITE_PRESET_NAME,
- preset_index, 1 /* Don't care */, name));
- }
-
- void CleanUp() {
- BTA_GATTC_AppDeregister(gatt_if_);
- for (auto& device : devices_) {
- if (device.conn_id != GATT_INVALID_CONN_ID)
- BTA_GATTC_Close(device.conn_id);
- DoDisconnectCleanUp(device);
- }
-
- devices_.clear();
- pending_operations_.clear();
- }
-
- void Dump(int fd) const {
- std::stringstream stream;
- if (devices_.size()) {
- stream << " {\"Known HAS devices\": [";
- for (const auto& device : devices_) {
- stream << "\n {";
- device.Dump(stream);
- stream << "\n },\n";
- }
- stream << " ]}\n\n";
- } else {
- stream << " \"No known HAS devices\"\n\n";
- }
- dprintf(fd, "%s", stream.str().c_str());
- }
-
- void OnGroupOpCoordinatorTimeout(void* p) {
- LOG(ERROR) << __func__ << ": Coordinated operation timeout: "
- << " not all the devices notified their state change on time.";
-
- /* Clear pending group operations */
- pending_group_operation_timeouts_.clear();
- HasCtpGroupOpCoordinator::Cleanup();
- }
-
- private:
- void WriteAllNeededCcc(const HasDevice& device) {
- if (device.conn_id == GATT_INVALID_CONN_ID) {
- //LOG_ERROR("Device %s is not connected", device.addr);
- return;
- }
-
- /* Write CCC values even remote should have it */
-// LOG_INFO("Subscribing for notification/indications");
- if (device.SupportsFeaturesNotification()) {
- SubscribeForNotifications(device.conn_id, device.addr,
- device.features_handle,
- device.features_ccc_handle);
- }
-
- if (device.SupportsPresets()) {
- SubscribeForNotifications(device.conn_id, device.addr, device.cp_handle,
- device.cp_ccc_handle, device.cp_ccc_val);
- SubscribeForNotifications(device.conn_id, device.addr,
- device.active_preset_handle,
- device.active_preset_ccc_handle);
- }
-
- if (property_get_bool("persist.bluetooth.has.always_use_preset_cache",
- true) == false) {
- CpReadAllPresetsOperation(HasCtpOp(
- device.addr, PresetCtpOpcode::READ_PRESETS,
- le_audio::has::kStartPresetIndex, le_audio::has::kMaxNumOfPresets));
- }
- }
-
- void OnEncrypted(HasDevice& device) {
- LOG(INFO) << __func__ << ": " << device.addr;
-
- if (device.isGattServiceValid()) {
- device.is_connecting_actively = false;
- NotifyHasDeviceValid(device);
- callbacks_->OnPresetInfo(device.addr, PresetInfoReason::ALL_PRESET_INFO,
- device.GetAllPresetInfo());
- callbacks_->OnActivePresetSelected(device.addr,
- device.currently_active_preset);
- WriteAllNeededCcc(device);
- } else {
- LOG(INFO) << __func__ << ": Gattc_Service Search request";
- BTA_GATTC_ServiceSearchRequest(device.conn_id,
- &kUuidHearingAccessService);
- }
- }
-
- void NotifyHasDeviceValid(const HasDevice& device) {
- LOG(INFO) << __func__ << ": addr:" << device.addr;
-
- std::vector<uint8_t> preset_indices;
- preset_indices.reserve(device.has_presets.size());
- for (auto const& preset : device.has_presets) {
- preset_indices.push_back(preset.GetIndex());
- }
-
- /* Notify that we are ready to go */
- callbacks_->OnConnectionState(ConnectionState::CONNECTED, device.addr);
- }
-
- void MarkDeviceValidIfInInitialDiscovery(HasDevice& device) {
- if (device.isGattServiceValid()) return;
-
- --device.gatt_svc_validation_steps;
-
- if (device.isGattServiceValid()) {
- device.is_connecting_actively = false;
-
- std::vector<uint8_t> presets_bin;
- if (device.SerializePresets(presets_bin)) {
- btif_storage_add_leaudio_has_device(device.addr, presets_bin,
- device.GetFeatures(),
- device.currently_active_preset);
- }
- NotifyHasDeviceValid(device);
- }
- }
-
- void OnGattWriteCcc(uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
- void* user_data) {
- LOG(INFO) << __func__ << ": handle=" << loghex(handle);
-
- auto device = GetDevice(conn_id);
- if (!device) {
- LOG(ERROR) << __func__ << ": unknown conn_id=" << loghex(conn_id);
- BtaGattQueue::Clean(conn_id);
- return;
- }
-
- if (status == GATT_DATABASE_OUT_OF_SYNC) {
- //LOG_INFO("Database out of sync for %s", device->addr);
- ClearDeviceInformationAndStartSearch(device);
- return;
- }
-
- HasGattOpContext context(user_data);
- bool enabling_ntf = context.context_flags &
- HasGattOpContext::kContextFlagsEnableNotification;
-
- if (handle == device->features_ccc_handle) {
- if (status == GATT_SUCCESS)
- device->features_notifications_enabled = enabling_ntf;
-
- } else if ((handle == device->active_preset_ccc_handle) ||
- (handle == device->cp_ccc_handle)) {
- /* Both of these CCC are mandatory */
- if (enabling_ntf && (status != GATT_SUCCESS)) {
- LOG(ERROR) << __func__
- << ": Failed to register for notifications on handle="
- << loghex(handle);
- BTA_GATTC_Close(conn_id);
- return;
- }
- }
- }
-
- void OnHasNotification(uint16_t conn_id, uint16_t handle, uint16_t len,
- const uint8_t* value) {
- auto device = GetDevice(conn_id);
- if (!device) {
- LOG(WARNING) << ": Skipping unknown device, conn_id=" << loghex(conn_id);
- return;
- }
-
- if (handle == device->features_handle) {
- OnHasFeaturesValue(&(*device), GATT_SUCCESS, handle, len, value);
-
- } else if (handle == device->cp_handle) {
- OnHasCtpValueNotification(&(*device), len, value);
-
- } else if (handle == device->active_preset_handle) {
- OnHasActivePresetValue(&(*device), GATT_SUCCESS, handle, len, value);
- }
- }
-
- /* Gets the device from variant, possibly searching by conn_id */
- HasDevice* GetDevice(
- std::variant<uint16_t, HasDevice*> conn_id_device_variant) {
- HasDevice* device = nullptr;
-
- if (std::holds_alternative<HasDevice*>(conn_id_device_variant)) {
- device = std::get<HasDevice*>(conn_id_device_variant);
- } else {
- auto it = std::find_if(
- devices_.begin(), devices_.end(),
- HasDevice::MatchConnId(std::get<uint16_t>(conn_id_device_variant)));
- if (it != devices_.end()) device = &(*it);
- }
-
- return device;
- }
-
- void OnHasFeaturesValue(
- std::variant<uint16_t, HasDevice*> conn_id_device_variant,
- tGATT_STATUS status, uint16_t handle, uint16_t len, const uint8_t* value,
- void* user_data = nullptr) {
- LOG(INFO) << __func__;
-
- auto device = GetDevice(conn_id_device_variant);
- if (!device) {
- LOG(ERROR) << __func__ << ": Unknown device!";
- return;
- }
-
- if (status != GATT_SUCCESS) {
- if (status == GATT_DATABASE_OUT_OF_SYNC) {
- //LOG_INFO("Database out of sync for %s", device->addr);
- ClearDeviceInformationAndStartSearch(device);
- } else {
- //LOG_ERROR("Could not read characteristic at handle=0x%04x", handle);
- BTA_GATTC_Close(device->conn_id);
- }
- return;
- }
-
- if (len != 1) {
- LOG(ERROR) << ": Invalid features value length=" << +len
- << " at handle=" << loghex(handle);
- BTA_GATTC_Close(device->conn_id);
- return;
- }
-
- /* Store features value */
- uint8_t features;
- STREAM_TO_UINT8(features, value);
- device->UpdateFeatures(features);
-
- if (device->isGattServiceValid()) {
- btif_storage_set_leaudio_has_features(device->addr, features);
- }
-
- /* Journal update */
- device->has_journal_.Append(HasJournalRecord(features, true));
-
- /* When service is not yet validated, report the available device with
- * features.
- */
- if (!device->isGattServiceValid())
- callbacks_->OnDeviceAvailable(device->addr, device->GetFeatures());
-
- /* Notify features */
- callbacks_->OnFeaturesUpdate(device->addr, device->GetFeatures());
-
- MarkDeviceValidIfInInitialDiscovery(*device);
- }
-
- /* Translates GATT statuses to application specific error codes */
- static ErrorCode GattStatus2SvcErrorCode(tGATT_STATUS status) {
- switch (status) {
- case 0x80:
- /* Invalid Opcode */
- /* Unlikely to happen as we would not allow unsupported operations */
- return ErrorCode::OPERATION_NOT_SUPPORTED;
- case 0x81:
- /* Write Name Not Allowed */
- return ErrorCode::SET_NAME_NOT_ALLOWED;
- case 0x82:
- /* Synchronization Not Supported */
- return ErrorCode::OPERATION_NOT_SUPPORTED;
- case 0x83:
- /* Preset Operation Not Possible */
- return ErrorCode::OPERATION_NOT_POSSIBLE;
- case 0x84:
- /* Preset Name Too Long */
- return ErrorCode::INVALID_PRESET_NAME_LENGTH;
- case 0xFE:
- /* Procedure Already in Progress */
- return ErrorCode::PROCEDURE_ALREADY_IN_PROGRESS;
- default:
- return ErrorCode::OPERATION_NOT_POSSIBLE;
- }
- }
-
- void OnHasPresetReadResponseNotification(HasDevice& device) {
- LOG(INFO) << __func__;
-
- while (device.ctp_notifications_.size() != 0) {
- auto ntf = device.ctp_notifications_.front();
- /* Process only read response events */
- if (ntf.opcode != PresetCtpOpcode::READ_PRESET_RESPONSE) break;
-
- /* Update preset values */
- if (ntf.preset.has_value()) {
- device.has_presets.erase(ntf.preset->GetIndex());
- device.has_presets.insert(ntf.preset.value());
- }
-
- /* We currently do READ_ALL_PRESETS only during the service validation.
- * If service is already valid, this must be the READ_PRESET_BY_INDEX.
- */
- if (device.isGattServiceValid()) {
- auto info = device.GetPresetInfo(ntf.preset.value().GetIndex());
- if (info.has_value())
- callbacks_->OnPresetInfo(
- device.addr, PresetInfoReason::PRESET_INFO_REQUEST_RESPONSE,
- {{info.value()}});
- }
-
- /* Journal update */
- device.has_journal_.Append(HasJournalRecord(ntf));
- device.ctp_notifications_.pop_front();
- }
-
- auto in_svc_validation = !device.isGattServiceValid();
- MarkDeviceValidIfInInitialDiscovery(device);
-
- /* We currently do READ_ALL_PRESETS only during the service validation.
- * ALL_PRESET_INFO will be sent only during this initial phase.
- */
- if (in_svc_validation) {
- callbacks_->OnPresetInfo(device.addr, PresetInfoReason::ALL_PRESET_INFO,
- device.GetAllPresetInfo());
-
- /* If this was the last validation step then send the currently active
- * preset as well.
- */
- if (device.isGattServiceValid())
- callbacks_->OnActivePresetSelected(device.addr,
- device.currently_active_preset);
- }
- }
-
- void OnHasPresetGenericUpdate(HasDevice& device) {
- LOG(ERROR) << __func__;
-
- std::vector<PresetInfo> updated_infos;
- std::vector<PresetInfo> deleted_infos;
-
- /* Process the entire train of preset changes with generic updates */
- while (device.ctp_notifications_.size() != 0) {
- auto nt = device.ctp_notifications_.front();
-
- /* Break if not a generic update anymore */
- if (nt.opcode != PresetCtpOpcode::PRESET_CHANGED) break;
- if (nt.change_id != PresetCtpChangeId::PRESET_GENERIC_UPDATE) break;
-
- if (nt.preset.has_value()) {
- /* Erase old value if exist */
- device.has_presets.erase(nt.preset->GetIndex());
-
- /* Erase in-between indices */
- if (nt.prev_index != 0) {
- auto it = device.has_presets.begin();
- while (it != device.has_presets.end()) {
- if ((it->GetIndex() > nt.prev_index) &&
- (it->GetIndex() < nt.preset->GetIndex())) {
- auto info = device.GetPresetInfo(it->GetIndex());
- if (info.has_value()) deleted_infos.push_back(info.value());
-
- it = device.has_presets.erase(it);
-
- } else {
- ++it;
- }
- }
- }
- /* Update presets */
- device.has_presets.insert(*nt.preset);
-
- auto info = device.GetPresetInfo(nt.preset->GetIndex());
- if (info.has_value()) updated_infos.push_back(info.value());
- }
-
- /* Journal update */
- device.has_journal_.Append(HasJournalRecord(nt));
- device.ctp_notifications_.pop_front();
- }
-
- if (device.isGattServiceValid()) {
- /* Update preset values in the storage */
- std::vector<uint8_t> presets_bin;
- if (device.SerializePresets(presets_bin)) {
- btif_storage_set_leaudio_has_presets(device.addr, presets_bin);
- }
-
- /* Check for the matching coordinated group op. to use group callbacks */
- for (auto it = pending_group_operation_timeouts_.rbegin();
- it != pending_group_operation_timeouts_.rend(); ++it) {
- auto& group_op_coordinator = it->second;
-
- /* Here we interested only in valid preset name changes */
- if (!((group_op_coordinator.operation.opcode ==
- PresetCtpOpcode::WRITE_PRESET_NAME) &&
- group_op_coordinator.operation.name.has_value()))
- continue;
-
- /* Match preset update results with the triggering operation */
- auto renamed_preset_info = std::find_if(
- updated_infos.begin(), updated_infos.end(),
- [&group_op_coordinator](const auto& info) {
- return (group_op_coordinator.operation.name.value() ==
- info.preset_name);
- });
- if (renamed_preset_info == updated_infos.end()) continue;
-
- if (group_op_coordinator.SetCompleted(device.addr)) {
- group_op_coordinator.preset_info_verification_list.push_back(
- *renamed_preset_info);
-
- /* Call the proper group operation completion callback */
- if (group_op_coordinator.IsFullyCompleted()) {
- callbacks_->OnPresetInfo(
- group_op_coordinator.operation.GetGroupId(),
- PresetInfoReason::PRESET_INFO_UPDATE, {*renamed_preset_info});
- pending_group_operation_timeouts_.erase(it->first);
- }
-
- /* Erase it from the 'updated_infos' since later we'll be sending
- * this as a group callback when the other device completes the
- * coordinated group name change.
- *
- * WARNING: There might an issue with callbacks call reordering due to
- * some of them being kept for group callbacks called later, when all
- * the grouped devices complete the coordinated group rename
- * operation. In most cases this should not be a major problem.
- */
- updated_infos.erase(renamed_preset_info);
- break;
- }
- }
-
- if (!updated_infos.empty())
- callbacks_->OnPresetInfo(
- device.addr, PresetInfoReason::PRESET_INFO_UPDATE, updated_infos);
-
- if (!deleted_infos.empty())
- callbacks_->OnPresetInfo(device.addr, PresetInfoReason::PRESET_DELETED,
- deleted_infos);
- }
- }
-
- void OnHasPresetAvailabilityChanged(HasDevice& device) {
- LOG(INFO) << __func__;
-
- std::vector<PresetInfo> infos;
-
- while (device.ctp_notifications_.size() != 0) {
- auto nt = device.ctp_notifications_.front();
-
- /* Process only preset change notifications */
- if (nt.opcode != PresetCtpOpcode::PRESET_CHANGED) break;
-
- auto preset = device.has_presets.extract(nt.index).value();
- auto new_props = preset.GetProperties();
-
- /* Process only the preset availability changes and then notify */
- if ((nt.change_id != PresetCtpChangeId::PRESET_AVAILABLE) &&
- (nt.change_id != PresetCtpChangeId::PRESET_UNAVAILABLE))
- break;
-
- /* Availability change */
- if (nt.change_id == PresetCtpChangeId::PRESET_AVAILABLE) {
- new_props |= HasPreset::kPropertyAvailable;
- } else {
- new_props &= !HasPreset::kPropertyAvailable;
- }
- device.has_presets.insert(
- HasPreset(preset.GetIndex(), new_props, preset.GetName()));
-
- auto info = device.GetPresetInfo(nt.index);
- if (info.has_value()) infos.push_back(info.value());
-
- /* Journal update */
- device.has_journal_.Append(HasJournalRecord(nt));
- device.ctp_notifications_.pop_front();
- }
-
- /* Update preset storage */
- if (device.isGattServiceValid()) {
- std::vector<uint8_t> presets_bin;
- if (device.SerializePresets(presets_bin)) {
- btif_storage_set_leaudio_has_presets(device.addr, presets_bin);
- }
- }
-
- callbacks_->OnPresetInfo(
- device.addr, PresetInfoReason::PRESET_AVAILABILITY_CHANGED, infos);
- }
-
- void OnHasPresetDeleted(HasDevice& device) {
- LOG(INFO) << __func__;
-
- std::vector<PresetInfo> infos;
- bool is_deleted = false;
-
- while (device.ctp_notifications_.size() != 0) {
- auto nt = device.ctp_notifications_.front();
-
- /* Process only preset change notifications */
- if (nt.opcode != PresetCtpOpcode::PRESET_CHANGED) break;
-
- /* Process only the deletions and then notify */
- if (nt.change_id != PresetCtpChangeId::PRESET_DELETED) break;
-
- auto info = device.GetPresetInfo(nt.index);
- if (info.has_value()) infos.push_back(info.value());
-
- if (device.has_presets.count(nt.index)) {
- is_deleted = true;
- device.has_presets.erase(nt.index);
- }
-
- /* Journal update */
- device.has_journal_.Append(HasJournalRecord(nt));
- device.ctp_notifications_.pop_front();
- }
-
- /* Update preset storage */
- if (device.isGattServiceValid()) {
- std::vector<uint8_t> presets_bin;
- if (device.SerializePresets(presets_bin)) {
- btif_storage_set_leaudio_has_presets(device.addr, presets_bin);
- }
- }
-
- if (is_deleted)
- callbacks_->OnPresetInfo(device.addr, PresetInfoReason::PRESET_DELETED,
- infos);
- }
-
- void ProcessCtpNotificationQueue(HasDevice& device) {
- std::vector<PresetInfo> infos;
-
- while (device.ctp_notifications_.size() != 0) {
- auto ntf = device.ctp_notifications_.front();
- LOG(INFO) << __func__ << ": ntf: " << ntf;
-
- if (ntf.opcode == PresetCtpOpcode::PRESET_CHANGED) {
- switch (ntf.change_id) {
- case PresetCtpChangeId::PRESET_GENERIC_UPDATE:
- OnHasPresetGenericUpdate(device);
- break;
- case PresetCtpChangeId::PRESET_AVAILABLE:
- OnHasPresetAvailabilityChanged(device);
- break;
- case PresetCtpChangeId::PRESET_UNAVAILABLE:
- OnHasPresetAvailabilityChanged(device);
- break;
- case PresetCtpChangeId::PRESET_DELETED:
- OnHasPresetDeleted(device);
- break;
- default:
- LOG(ERROR) << __func__ << ": Invalid notification: " << ntf;
- break;
- }
-
- } else if (ntf.opcode == PresetCtpOpcode::READ_PRESET_RESPONSE) {
- OnHasPresetReadResponseNotification(device);
-
- } else {
- LOG(ERROR) << __func__ << ": Unsupported preset notification: " << ntf;
- }
- }
- }
-
- void OnHasCtpValueNotification(HasDevice* device, uint16_t len,
- const uint8_t* value) {
- auto ntf_opt = HasCtpNtf::FromCharacteristicValue(len, value);
- if (!ntf_opt.has_value()) {
- LOG(ERROR) << __func__
- << ": Unhandled notification for device: " << *device;
- BTA_GATTC_Close(device->conn_id);
- return;
- }
-
- auto ntf = ntf_opt.value();
- LOG(INFO) << __func__ << ":" <<ntf;
-
- device->ctp_notifications_.push_back(ntf);
- if (ntf.is_last) ProcessCtpNotificationQueue(*device);
- }
-
- void OnHasActivePresetValue(
- std::variant<uint16_t, HasDevice*> conn_id_device_variant,
- tGATT_STATUS status, uint16_t handle, uint16_t len, const uint8_t* value,
- void* user_data = nullptr) {
- LOG(INFO) << __func__;
-
- auto device = GetDevice(conn_id_device_variant);
- if (!device) {
- LOG(ERROR) << ": Skipping unknown device!";
- return;
- }
-
- if (status != GATT_SUCCESS) {
- if (status == GATT_DATABASE_OUT_OF_SYNC) {
- //LOG_INFO("Database out of sync for %s", device->addr);
- ClearDeviceInformationAndStartSearch(device);
- } else {
- //LOG_ERROR("Could not read characteristic at handle=0x%04x", handle);
- BTA_GATTC_Close(device->conn_id);
- }
- }
-
- if (len != 1) {
- LOG(ERROR) << ": Invalid preset value length=" << +len
- << " at handle=" << loghex(handle);
- BTA_GATTC_Close(device->conn_id);
- return;
- }
-
- /* Get the active preset value */
- auto* pp = value;
- STREAM_TO_UINT8(device->currently_active_preset, pp);
-
- if (device->isGattServiceValid()) {
- btif_storage_set_leaudio_has_active_preset(
- device->addr, device->currently_active_preset);
- }
-
- /* Journal update */
- device->has_journal_.Append(
- HasJournalRecord(device->currently_active_preset, false));
-
- /* If svc not marked valid, this might be the last validation step. */
- MarkDeviceValidIfInInitialDiscovery(*device);
-
- if (device->isGattServiceValid()) {
- if (!pending_group_operation_timeouts_.empty()) {
- for (auto it = pending_group_operation_timeouts_.rbegin();
- it != pending_group_operation_timeouts_.rend(); ++it) {
- auto& group_op_coordinator = it->second;
-
- bool matches = false;
- switch (group_op_coordinator.operation.opcode) {
- case PresetCtpOpcode::SET_ACTIVE_PRESET:
- [[fallthrough]];
- case PresetCtpOpcode::SET_NEXT_PRESET:
- [[fallthrough]];
- case PresetCtpOpcode::SET_PREV_PRESET:
- [[fallthrough]];
- case PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC:
- [[fallthrough]];
- case PresetCtpOpcode::SET_NEXT_PRESET_SYNC:
- [[fallthrough]];
- case PresetCtpOpcode::SET_PREV_PRESET_SYNC: {
- if (group_op_coordinator.SetCompleted(device->addr)) {
- matches = true;
- break;
- }
- } break;
- default:
- /* Ignore */
- break;
- }
- if (group_op_coordinator.IsFullyCompleted()) {
- callbacks_->OnActivePresetSelected(
- group_op_coordinator.operation.GetGroupId(),
- device->currently_active_preset);
- pending_group_operation_timeouts_.erase(it->first);
- }
- if (matches) break;
- }
-
- } else {
- callbacks_->OnActivePresetSelected(device->addr,
- device->currently_active_preset);
- }
- }
- }
-
- void DeregisterNotifications(HasDevice& device) {
- /* Deregister from optional features notifications */
- if (device.features_ccc_handle != GAP_INVALID_HANDLE) {
- BTA_GATTC_DeregisterForNotifications(gatt_if_, device.addr,
- device.features_handle);
- }
-
- /* Deregister from active presets notifications if presets exist */
- if (device.active_preset_ccc_handle != GAP_INVALID_HANDLE) {
- BTA_GATTC_DeregisterForNotifications(gatt_if_, device.addr,
- device.active_preset_handle);
- }
-
- /* Deregister from control point notifications */
- if (device.cp_ccc_handle != GAP_INVALID_HANDLE) {
- BTA_GATTC_DeregisterForNotifications(gatt_if_, device.addr,
- device.cp_handle);
- }
- }
-
- /* Cleans up after the device disconnection */
- void DoDisconnectCleanUp(HasDevice& device,
- bool invalidate_gatt_service = true) {
- //LOG_DEBUG(": device=%s", device.addr);
-
- DeregisterNotifications(device);
-
- if (device.conn_id != GATT_INVALID_CONN_ID) {
- BtaGattQueue::Clean(device.conn_id);
- if (invalidate_gatt_service) device.gatt_svc_validation_steps = 0xFE;
- }
-
- /* Clear pending operations */
- auto addr = device.addr;
- pending_operations_.erase(
- std::remove_if(
- pending_operations_.begin(), pending_operations_.end(),
- [&addr](auto& el) {
- if (std::holds_alternative<RawAddress>(el.addr_or_group)) {
- return std::get<RawAddress>(el.addr_or_group) == addr;
- }
- return false;
- }),
- pending_operations_.end());
-
- device.ConnectionCleanUp();
- }
-
- /* These below are all GATT service discovery, validation, cache & storage */
- bool CacheAttributeHandles(const gatt::Service& service, HasDevice* device) {
- LOG(INFO) << __func__ << ": device="
- << device->addr;
-
- for (const gatt::Characteristic& charac : service.characteristics) {
- if (charac.uuid == kUuidActivePresetIndex) {
- /* Find the mandatory CCC descriptor */
- uint16_t ccc_handle =
- FindCccHandle(device->conn_id, charac.value_handle);
- if (ccc_handle == GAP_INVALID_HANDLE) {
- LOG(ERROR) << __func__
- << ": no HAS Active Preset CCC descriptor found!";
- return false;
- }
- device->active_preset_ccc_handle = ccc_handle;
- device->active_preset_handle = charac.value_handle;
-
- } else if (charac.uuid == kUuidHearingAidPresetControlPoint) {
- /* Find the mandatory CCC descriptor */
- uint16_t ccc_handle =
- FindCccHandle(device->conn_id, charac.value_handle);
- if (ccc_handle == GAP_INVALID_HANDLE) {
- LOG(ERROR) << __func__
- << ": no HAS Control Point CCC descriptor found!";
- return false;
- }
- uint8_t ccc_val = 0;
- if (charac.properties & GATT_CHAR_PROP_BIT_NOTIFY)
- ccc_val |= GATT_CHAR_CLIENT_CONFIG_NOTIFICATION;
-
- if (charac.properties & GATT_CHAR_PROP_BIT_INDICATE)
- ccc_val |= GATT_CHAR_CLIENT_CONFIG_INDICTION;
-
- if (ccc_val == 0) {
- //LOG_ERROR("Invalid properties for the control point 0x%02x", charac.properties);
- return false;
- }
-
- device->cp_ccc_handle = ccc_handle;
- device->cp_handle = charac.value_handle;
- device->cp_ccc_val = ccc_val;
- } else if (charac.uuid == kUuidHearingAidFeatures) {
- /* Find the optional CCC descriptor */
- uint16_t ccc_handle =
- FindCccHandle(device->conn_id, charac.value_handle);
- device->features_ccc_handle = ccc_handle;
- device->features_handle = charac.value_handle;
- }
- }
- return true;
- }
-
- bool LoadHasDetailsFromStorage(HasDevice* device) {
- LOG(INFO) << __func__ << ": device="
- << device->addr;
-
- std::vector<uint8_t> presets_bin;
- uint8_t active_preset;
-
- if (!btif_storage_get_leaudio_has_presets(device->addr, presets_bin,
- active_preset))
- return false;
-
- if (!HasDevice::DeserializePresets(presets_bin.data(), presets_bin.size(),
- *device))
- return false;
-
- VLOG(1) << ": Loading HAS service details from storage.";
-
- device->currently_active_preset = active_preset;
-
- /* Update features and refresh opcode support map */
- uint8_t val;
- if (btif_storage_get_leaudio_has_features(device->addr, val))
- device->UpdateFeatures(val);
-
- /* With all the details loaded we can already mark it as valid */
- device->gatt_svc_validation_steps = 0;
- device->is_connecting_actively = false;
-
- NotifyHasDeviceValid(*device);
- callbacks_->OnPresetInfo(device->addr, PresetInfoReason::ALL_PRESET_INFO,
- device->GetAllPresetInfo());
- callbacks_->OnActivePresetSelected(device->addr,
- device->currently_active_preset);
- if (device->conn_id == GATT_INVALID_CONN_ID) return true;
-
- /* Be mistrustful here: write CCC values even remote should have it */
-// LOG_INFO("Subscribing for notification/indications");
- WriteAllNeededCcc(*device);
-
- return true;
- }
-
- bool StartInitialHasDetailsReadAndValidation(const gatt::Service& service,
- HasDevice* device) {
- // Validate service structure
- if (device->features_handle == GAP_INVALID_HANDLE) {
- /* Missing key characteristic */
- LOG(ERROR) << __func__ << ": Service has broken structure";
- return false;
- }
-
- if (device->cp_handle != GAP_INVALID_HANDLE) {
- if (device->active_preset_handle == GAP_INVALID_HANDLE) return false;
- if (device->active_preset_ccc_handle == GAP_INVALID_HANDLE) return false;
- }
-
- /* Number of reads or notifications required to validate the service */
- device->gatt_svc_validation_steps = 1 + (device->SupportsPresets() ? 2 : 0);
-
- /* Read the initial features */
- BtaGattQueue::ReadCharacteristic(
- device->conn_id, device->features_handle,
- [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
- uint8_t* value, void* user_data) {
- if (instance)
- instance->OnHasFeaturesValue(conn_id, status, handle, len, value,
- user_data);
- },
- nullptr);
-
- /* Register for features notifications */
- if (device->SupportsFeaturesNotification()) {
- SubscribeForNotifications(device->conn_id, device->addr,
- device->features_handle,
- device->features_ccc_handle);
- } else {
- LOG(WARNING) << __func__
- << ": server does not support features notification";
- }
-
- /* If Presets are supported we should read them all and subscribe for the
- * mandatory active preset index notifications.
- */
- if (device->SupportsPresets()) {
- /* Subscribe for active preset notifications */
- SubscribeForNotifications(device->conn_id, device->addr,
- device->active_preset_handle,
- device->active_preset_ccc_handle);
-
- SubscribeForNotifications(device->conn_id, device->addr,
- device->cp_handle, device->cp_ccc_handle,
- device->cp_ccc_val);
-
- /* Get all the presets */
- CpReadAllPresetsOperation(HasCtpOp(
- device->addr, PresetCtpOpcode::READ_PRESETS,
- le_audio::has::kStartPresetIndex, le_audio::has::kMaxNumOfPresets));
-
- /* Read the current active preset index */
- BtaGattQueue::ReadCharacteristic(
- device->conn_id, device->active_preset_handle,
- [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
- uint16_t len, uint8_t* value, void* user_data) {
- if (instance)
- instance->OnHasActivePresetValue(conn_id, status, handle, len,
- value, user_data);
- },
- nullptr);
- } else {
- LOG(WARNING) << __func__
- << ": server can only report HAS features, other "
- "functionality is disabled";
- }
-
- return true;
- }
-
- bool OnHasServiceFound(const gatt::Service& service, void* context) {
- LOG(INFO) << __func__;
-
- auto* device = static_cast<HasDevice*>(context);
-
- /* Initially validate and store GATT service discovery data */
- if (!CacheAttributeHandles(service, device)) return false;
-
- /* If deatails are loaded from storage we are done here */
- if (LoadHasDetailsFromStorage(device)) return true;
-
- /* No storred details - read all the details and validate */
- return StartInitialHasDetailsReadAndValidation(service, device);
- }
-
- /* These below are all generic event handlers calling in HAS specific code. */
- void GattcCallback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
- LOG(INFO) << __func__ << ": event = " << static_cast<int>(event);
- uint8_t sec_flag = 0;
-
- switch (event) {
- case BTA_GATTC_DEREG_EVT:
- break;
-
- case BTA_GATTC_OPEN_EVT:
- OnGattConnected(p_data->open);
- break;
-
- case BTA_GATTC_CLOSE_EVT:
- OnGattDisconnected(p_data->close);
- break;
-
- case BTA_GATTC_SEARCH_CMPL_EVT:
- OnGattServiceSearchComplete(p_data->search_cmpl);
- break;
-
- case BTA_GATTC_NOTIF_EVT:
- OnGattNotification(p_data->notify);
- break;
-
- case BTA_GATTC_ENC_CMPL_CB_EVT:
- OnLeEncryptionComplete(p_data->enc_cmpl.remote_bda,
- BTM_GetSecurityFlagsByTransport(p_data->enc_cmpl.remote_bda, &sec_flag, BT_TRANSPORT_LE)); //BT_TRANSPORT_LE
- break;
-
- case BTA_GATTC_SRVC_CHG_EVT:
- OnGattServiceChangeEvent(p_data->remote_bda);
- break;
-
- case BTA_GATTC_SRVC_DISC_DONE_EVT:
- OnGattServiceDiscoveryDoneEvent(p_data->remote_bda);
- break;
-
- default:
- break;
- }
- }
-
- void OnGattConnected(const tBTA_GATTC_OPEN& evt) {
- LOG(INFO) << __func__ << ": address="
- << evt.remote_bda
- << ", conn_id=" << evt.conn_id;
-
-
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(evt.remote_bda));
- uint8_t sec_flag = 0;
- if (device == devices_.end()) {
- LOG(INFO) << ": Skipping unknown device, address="
- << evt.remote_bda;
- BTA_GATTC_Close(evt.conn_id);
- return;
- }
-
- if (evt.status != GATT_SUCCESS) {
- if (!device->is_connecting_actively) {
- // acceptlist connection failed, that's ok.
- return;
- }
-
- LOG(INFO) << "Failed to connect to server device";
- devices_.erase(device);
- callbacks_->OnConnectionState(ConnectionState::DISCONNECTED,
- evt.remote_bda);
- return;
- }
-
- device->conn_id = evt.conn_id;
-
- tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(device->addr);
- if (p_dev_rec) {
- if (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING ||
- p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) {
- /* if security collision happened, wait for encryption done
- * (BTA_GATTC_ENC_CMPL_CB_EVT)
- */
- return;
- }
- }
-
- /* verify bond */
- if (BTM_GetSecurityFlagsByTransport(device->addr, &sec_flag, BT_TRANSPORT_LE)) {
- /* if link has been encrypted */
- if (device->isGattServiceValid()) {
- instance->OnEncrypted(*device);
- } else {
- BTA_GATTC_ServiceSearchRequest(device->conn_id,
- &kUuidHearingAccessService);
- }
- return;
- }
-
- int result = BTM_SetEncryption(
- evt.remote_bda, BT_TRANSPORT_LE,
- [](const RawAddress* bd_addr, tBT_TRANSPORT transport, void* p_ref_data,
- tBTM_STATUS status) {
- if (instance)
- instance->OnLeEncryptionComplete(*bd_addr, status == BTM_SUCCESS);
- },
- nullptr, BTM_BLE_SEC_ENCRYPT);
-
- DLOG(INFO) << __func__ << ": Encryption request result: " << result;
- }
-
- void OnGattDisconnected(const tBTA_GATTC_CLOSE& evt) {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(evt.remote_bda));
- if (device == devices_.end()) {
- LOG(WARNING) << ": Skipping unknown device disconnect, conn_id="
- << loghex(evt.conn_id);
- return;
- }
- LOG(INFO) << __func__ << ": device="
- << device->addr
- << ": reason=" << loghex(static_cast<int>(evt.reason));
-
- /* Don't notify disconnect state for background connection that failed */
- if (device->is_connecting_actively || device->isGattServiceValid())
- callbacks_->OnConnectionState(ConnectionState::DISCONNECTED,
- evt.remote_bda);
-
- auto peer_disconnected = (evt.reason == GATT_CONN_TIMEOUT) ||
- (evt.reason == GATT_CONN_TERMINATE_PEER_USER);
- DoDisconnectCleanUp(*device, peer_disconnected ? false : true);
-
- /* Connect in background - is this ok? */
- if (peer_disconnected)
- BTA_GATTC_Open(gatt_if_, device->addr, true, BT_TRANSPORT_LE, false);
- }
-
- void OnGattServiceSearchComplete(const tBTA_GATTC_SEARCH_CMPL& evt) {
- auto device = GetDevice(evt.conn_id);
- if (!device) {
- LOG(INFO) << ": Skipping unknown device, conn_id="
- << loghex(evt.conn_id);
- return;
- }
-
- LOG(INFO) << __func__;
-
- /* Ignore if our service data is valid (service discovery initiated by
- * someone else?)
- */
- if (!device->isGattServiceValid()) {
- if (evt.status != GATT_SUCCESS) {
- LOG(INFO) << __func__ << ": Service discovery failed";
- BTA_GATTC_Close(device->conn_id);
- return;
- }
-
- const std::vector<gatt::Service>* all_services =
- BTA_GATTC_GetServices(device->conn_id);
-
- auto service =
- std::find_if(all_services->begin(), all_services->end(),
- [](const gatt::Service& svc) {
- return svc.uuid == kUuidHearingAccessService;
- });
- if (service == all_services->end()) {
- LOG(INFO) << ": No service found";
- BTA_GATTC_Close(device->conn_id);
- return;
- }
-
- /* Call the service specific verifier callback */
- if (!instance->OnHasServiceFound(*service, &(*device))) {
- LOG(INFO) << ": Not a valid service!";
- BTA_GATTC_Close(device->conn_id);
- return;
- }
- }
- }
-
- void OnGattNotification(const tBTA_GATTC_NOTIFY& evt) {
- /* Reject invalid lengths */
- if (evt.len > GATT_MAX_ATTR_LEN) {
- LOG(ERROR) << __func__ << ": rejected BTA_GATTC_NOTIF_EVT. is_notify = "
- << evt.is_notify << ", len=" << static_cast<int>(evt.len);
- }
- if (!evt.is_notify) BTA_GATTC_SendIndConfirm(evt.conn_id, evt.handle, evt.trans_id);
-
- OnHasNotification(evt.conn_id, evt.handle, evt.len, evt.value);
- }
-
- void OnLeEncryptionComplete(const RawAddress& address, bool success) {
- LOG(INFO) << __func__ << ": " <<address;
-
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(address));
- if (device == devices_.end()) {
- LOG(WARNING) << ": Skipping unknown device"
- << address;
- return;
- }
-
- if (!success) {
- LOG(ERROR) << ": Encryption failed for device "
- << address;
-
- BTA_GATTC_Close(device->conn_id);
- return;
- }
-
- if (device->isGattServiceValid()) {
- instance->OnEncrypted(*device);
- } else {
- BTA_GATTC_ServiceSearchRequest(device->conn_id,
- &kUuidHearingAccessService);
- }
- }
-
- void ClearDeviceInformationAndStartSearch(HasDevice* device) {
- if (!device) {
-// LOG_ERROR("Device is null");
- return;
- }
-
-// LOG_INFO("%s", device->addr);
-
- if (!device->isGattServiceValid()) {
- //LOG_INFO("Service already invalidated");
- return;
- }
-
- /* Invalidate service discovery results */
- DeregisterNotifications(*device);
- BtaGattQueue::Clean(device->conn_id);
- device->ClearSvcData();
- btif_storage_remove_leaudio_has(device->addr);
- BTA_GATTC_ServiceSearchRequest(device->conn_id, &kUuidHearingAccessService);
- }
-
- void OnGattServiceChangeEvent(const RawAddress& address) {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(address));
- if (device == devices_.end()) {
- LOG(WARNING) << ": Skipping unknown device" << address;
- return;
- }
- //LOG_INFO("%s", address);
- ClearDeviceInformationAndStartSearch(&(*device));
- }
-
- void OnGattServiceDiscoveryDoneEvent(const RawAddress& address) {
- auto device = std::find_if(devices_.begin(), devices_.end(),
- HasDevice::MatchAddress(address));
- if (device == devices_.end()) {
- LOG(WARNING) << ": Skipping unknown device"
- << address;
- return;
- }
-
- LOG(INFO) << __func__ << ": address="
- << address;
-
- if (!device->isGattServiceValid())
- BTA_GATTC_ServiceSearchRequest(device->conn_id,
- &kUuidHearingAccessService);
- }
-
- static uint16_t FindCccHandle(uint16_t conn_id, uint16_t char_handle) {
- const gatt::Characteristic* p_char =
- BTA_GATTC_GetCharacteristic(conn_id, char_handle);
- if (!p_char) {
- LOG(WARNING) << __func__ << ": No such characteristic: " << char_handle;
- return GAP_INVALID_HANDLE;
- }
-
- for (const gatt::Descriptor& desc : p_char->descriptors) {
- if (desc.uuid == Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG))
- return desc.handle;
- }
-
- return GAP_INVALID_HANDLE;
- }
-
- void SubscribeForNotifications(
- uint16_t conn_id, const RawAddress& address, uint16_t value_handle,
- uint16_t ccc_handle,
- uint16_t ccc_val = GATT_CHAR_CLIENT_CONFIG_NOTIFICATION) {
- if (value_handle != GAP_INVALID_HANDLE) {
- tGATT_STATUS register_status =
- BTA_GATTC_RegisterForNotifications(gatt_if_, address, value_handle);
- LOG(INFO) << __func__ << ": BTA_GATTC_RegisterForNotifications, status="
- << loghex(+register_status)
- << " value=" << loghex(value_handle)
- << " ccc=" << loghex(ccc_handle);
-
- if (register_status != GATT_SUCCESS) return;
- }
-
- std::vector<uint8_t> value(2);
- uint8_t* value_ptr = value.data();
- UINT16_TO_STREAM(value_ptr, ccc_val);
- BtaGattQueue::WriteDescriptor(
- conn_id, ccc_handle, std::move(value), GATT_WRITE,
- [](uint16_t conn_id, tGATT_STATUS status, uint16_t value_handle,
- uint16_t len, const uint8_t* value, void* data) {
- if (instance)
- instance->OnGattWriteCcc(conn_id, status, value_handle, data);
- },
- HasGattOpContext(HasGattOpContext::kContextFlagsEnableNotification));
- }
-
- uint8_t gatt_if_;
- bluetooth::has::HasClientCallbacks* callbacks_;
- std::list<HasDevice> devices_;
- std::list<HasCtpOp> pending_operations_;
-
- typedef std::map<decltype(HasCtpOp::op_id), HasCtpGroupOpCoordinator>
- has_operation_timeouts_t;
- has_operation_timeouts_t pending_group_operation_timeouts_;
-};
-
-} // namespace
-
-alarm_t* HasCtpGroupOpCoordinator::operation_timeout_timer = nullptr;
-size_t HasCtpGroupOpCoordinator::ref_cnt = 0u;
-alarm_callback_t HasCtpGroupOpCoordinator::cb = [](void*) {};
-
-void HasClient::Initialize(bluetooth::has::HasClientCallbacks* callbacks,
- base::Closure initCb) {
- if (instance) {
- LOG(ERROR) << ": Already initialized!";
- return;
- }
-
- HasCtpGroupOpCoordinator::Initialize([](void* p) {
- if (instance) instance->OnGroupOpCoordinatorTimeout(p);
- });
- instance = new HasClientImpl(callbacks, initCb);
-}
-
-bool HasClient::IsHasClientRunning() { return instance; }
-
-HasClient* HasClient::Get(void) {
- CHECK(instance);
- return instance;
-};
-
-void HasClient::AddFromStorage(const RawAddress& addr, uint8_t features,
- uint16_t is_acceptlisted) {
- if (!instance) {
- LOG(ERROR) << ": Not initialized yet";
- }
-
- instance->AddFromStorage(addr, features, is_acceptlisted);
-};
-
-void HasClient::CleanUp() {
- HasClientImpl* ptr = instance;
- instance = nullptr;
-
- if (ptr) {
- ptr->CleanUp();
- delete ptr;
- }
-
- HasCtpGroupOpCoordinator::Cleanup();
-};
-
-void HasClient::DebugDump(int fd) {
- dprintf(fd, ": Hearing Access Service Client:\n");
- if (instance)
- instance->Dump(fd);
- else
- dprintf(fd, " no instance\n\n");
-}
diff --git a/bta/has/has_client_test.cc b/bta/has/has_client_test.cc
deleted file mode 100644
index a1c77bca7..000000000
--- a/bta/has/has_client_test.cc
+++ /dev/null
@@ -1,3202 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#include <base/bind.h>
-#include <base/bind_helpers.h>
-#include <base/strings/string_number_conversions.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <osi/include/alarm.h>
-#include <sys/socket.h>
-
-#include <variant>
-
-#include "bta/le_audio/le_audio_types.h"
-#include "bta_csis_api.h"
-#include "bta_gatt_api_mock.h"
-#include "bta_gatt_queue_mock.h"
-#include "bta_has_api.h"
-#include "btif_storage_mock.h"
-#include "btm_api_mock.h"
-#include "gatt/database_builder.h"
-#include "hardware/bt_gatt_types.h"
-#include "has_types.h"
-#include "mock_controller.h"
-#include "mock_csis_client.h"
-
-bool gatt_profile_get_eatt_support(const RawAddress& addr) { return true; }
-void osi_property_set_bool(const char* key, bool value);
-
-std::map<std::string, int> mock_function_count_map;
-
-namespace bluetooth {
-namespace has {
-namespace internal {
-namespace {
-
-using base::HexEncode;
-
-using ::bluetooth::csis::CsisClient;
-using ::bluetooth::has::ConnectionState;
-using ::bluetooth::has::ErrorCode;
-using ::bluetooth::has::HasClientCallbacks;
-using ::bluetooth::has::PresetInfo;
-
-using ::le_audio::has::HasClient;
-using ::le_audio::has::HasCtpGroupOpCoordinator;
-using ::le_audio::has::HasCtpOp;
-using ::le_audio::has::HasDevice;
-using ::le_audio::has::HasPreset;
-
-using ::testing::_;
-using ::testing::AnyNumber;
-using ::testing::DoAll;
-using ::testing::DoDefault;
-using ::testing::Invoke;
-using ::testing::Mock;
-using ::testing::NotNull;
-using ::testing::Return;
-using ::testing::SaveArg;
-using ::testing::Sequence;
-using ::testing::SetArgPointee;
-using ::testing::WithArg;
-
-// Disables most likely false-positives from base::SplitString()
-// extern "C" const char* __asan_default_options() {
-// return "detect_container_overflow=0";
-// }
-
-RawAddress GetTestAddress(int index) {
- CHECK_LT(index, UINT8_MAX);
- RawAddress result = {
- {0xC0, 0xDE, 0xC0, 0xDE, 0x00, static_cast<uint8_t>(index)}};
- return result;
-}
-
-static uint16_t GetTestConnId(const RawAddress& address) {
- return address.address[RawAddress::kLength - 1];
-}
-
-class MockHasCallbacks : public HasClientCallbacks {
- public:
- MockHasCallbacks() = default;
- MockHasCallbacks(const MockHasCallbacks&) = delete;
- MockHasCallbacks& operator=(const MockHasCallbacks&) = delete;
-
- ~MockHasCallbacks() override = default;
-
- MOCK_METHOD((void), OnConnectionState,
- (ConnectionState state, const RawAddress& address), (override));
- MOCK_METHOD((void), OnDeviceAvailable,
- (const RawAddress& address, uint8_t features), (override));
- MOCK_METHOD((void), OnFeaturesUpdate,
- (const RawAddress& address, uint8_t features), (override));
- MOCK_METHOD((void), OnActivePresetSelected,
- ((std::variant<RawAddress, int> addr_or_group_id),
- uint8_t preset_index),
- (override));
- MOCK_METHOD((void), OnActivePresetSelectError,
- ((std::variant<RawAddress, int> addr_or_group_id),
- ErrorCode result),
- (override));
- MOCK_METHOD((void), OnPresetInfo,
- ((std::variant<RawAddress, int> addr_or_group_id),
- PresetInfoReason change_id,
- std::vector<PresetInfo> preset_change_records),
- (override));
- MOCK_METHOD((void), OnPresetInfoError,
- ((std::variant<RawAddress, int> addr_or_group_id),
- uint8_t preset_index, ErrorCode error_code),
- (override));
- MOCK_METHOD((void), OnSetPresetNameError,
- ((std::variant<RawAddress, int> addr_or_group_id),
- uint8_t preset_index, ErrorCode error_code),
- (override));
-};
-
-class HasClientTestBase : public ::testing::Test {
- protected:
- std::map<uint16_t, uint8_t> current_peer_active_preset_idx_;
- std::map<uint16_t, uint8_t> current_peer_features_val_;
- std::map<uint16_t, std::set<HasPreset, HasPreset::ComparatorDesc>>
- current_peer_presets_;
-
- struct HasDbBuilder {
- bool has;
-
- static constexpr uint16_t kGapSvcStartHdl = 0x0001;
- static constexpr uint16_t kGapDeviceNameValHdl = 0x0003;
- static constexpr uint16_t kGapSvcEndHdl = kGapDeviceNameValHdl;
-
- static constexpr uint16_t kSvcStartHdl = 0x0010;
- static constexpr uint16_t kFeaturesValHdl = 0x0012;
- static constexpr uint16_t kPresetsCtpValHdl = 0x0015;
- static constexpr uint16_t kActivePresetIndexValHdl = 0x0018;
- static constexpr uint16_t kSvcEndHdl = 0x001E;
-
- static constexpr uint16_t kGattSvcStartHdl = 0x0090;
- static constexpr uint16_t kGattSvcChangedValHdl = 0x0092;
- static constexpr uint16_t kGattSvcEndHdl = kGattSvcChangedValHdl + 1;
-
- bool features;
- bool features_ntf;
-
- bool preset_cp;
- bool preset_cp_ntf;
- bool preset_cp_ind;
-
- bool active_preset_idx;
- bool active_preset_idx_ntf;
-
- const gatt::Database Build() {
- gatt::DatabaseBuilder bob;
-
- /* Generic Access Service */
- bob.AddService(kGapSvcStartHdl, kGapSvcEndHdl, Uuid::From16Bit(0x1800),
- true);
- /* Device Name Char. */
- bob.AddCharacteristic(kGapDeviceNameValHdl - 1, kGapDeviceNameValHdl,
- Uuid::From16Bit(0x2a00), GATT_CHAR_PROP_BIT_READ);
-
- /* 0x0004-0x000f left empty on purpose */
- if (has) {
- bob.AddService(kSvcStartHdl, kSvcEndHdl,
- ::le_audio::has::kUuidHearingAccessService, true);
-
- if (features) {
- bob.AddCharacteristic(
- kFeaturesValHdl - 1, kFeaturesValHdl,
- ::le_audio::has::kUuidHearingAidFeatures,
- GATT_CHAR_PROP_BIT_READ |
- (features_ntf ? GATT_CHAR_PROP_BIT_NOTIFY : 0));
-
- if (features_ntf) {
- bob.AddDescriptor(kFeaturesValHdl + 1,
- Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
- }
- }
-
- if (preset_cp) {
- bob.AddCharacteristic(
- kPresetsCtpValHdl - 1, kPresetsCtpValHdl,
- ::le_audio::has::kUuidHearingAidPresetControlPoint,
- GATT_CHAR_PROP_BIT_WRITE |
- (preset_cp_ntf ? GATT_CHAR_PROP_BIT_NOTIFY : 0) |
- (preset_cp_ind ? GATT_CHAR_PROP_BIT_INDICATE : 0));
-
- if (preset_cp_ntf || preset_cp_ind) {
- bob.AddDescriptor(kPresetsCtpValHdl + 1,
- Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
- }
- }
-
- if (active_preset_idx) {
- bob.AddCharacteristic(
- kActivePresetIndexValHdl - 1, kActivePresetIndexValHdl,
- ::le_audio::has::kUuidActivePresetIndex,
- GATT_CHAR_PROP_BIT_READ |
- (active_preset_idx_ntf ? GATT_CHAR_PROP_BIT_NOTIFY : 0));
-
- if (active_preset_idx_ntf)
- bob.AddDescriptor(kActivePresetIndexValHdl + 1,
- Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
- }
- }
-
- /* GATTS */
- /* 0x001F-0x0090 left empty on purpose */
- bob.AddService(kGattSvcStartHdl, kGattSvcEndHdl,
- Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER), true);
- bob.AddCharacteristic(kGattSvcChangedValHdl - 1, kGattSvcChangedValHdl,
- Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD),
- GATT_CHAR_PROP_BIT_NOTIFY);
- bob.AddDescriptor(kGattSvcChangedValHdl + 1,
- Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
- return bob.Build();
- };
- };
-
- const gatt::Characteristic* FindCharacteristicByValueHandle(
- const gatt::Service* svc, uint16_t handle) {
- if (svc == nullptr) return nullptr;
-
- auto it =
- std::find_if(svc->characteristics.cbegin(), svc->characteristics.cend(),
- [handle](const auto& characteristic) {
- return characteristic.value_handle == handle;
- });
- return (it != svc->characteristics.cend()) ? &(*it) : nullptr;
- }
-
- void set_sample_database(
- const RawAddress& address, HasDbBuilder& builder,
- uint8_t features_val = 0x0,
- std::optional<std::set<HasPreset, HasPreset::ComparatorDesc>> presets_op =
- std::nullopt) {
- uint16_t conn_id = GetTestConnId(address);
-
- /* For some test cases these defaults are enough */
- if (!presets_op)
- presets_op = {{
- HasPreset(6, HasPreset::kPropertyAvailable, "Universal"),
- HasPreset(
- 55, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "YourPreset55"),
- }};
- auto& presets = presets_op.value();
- auto const active_preset = presets.begin();
-
- services_map[conn_id] = builder.Build().Services();
- current_peer_features_val_.insert_or_assign(conn_id, features_val);
- current_peer_active_preset_idx_.insert_or_assign(conn_id,
- active_preset->GetIndex());
- current_peer_presets_.insert_or_assign(conn_id, std::move(presets));
-
- ON_CALL(gatt_queue, ReadCharacteristic(conn_id, _, _, _))
- .WillByDefault(Invoke([this](uint16_t conn_id, uint16_t handle,
- GATT_READ_OP_CB cb, void* cb_data) {
- auto* svc = gatt::FindService(services_map[conn_id], handle);
- if (svc == nullptr) return;
-
- std::vector<uint8_t> value;
- tGATT_STATUS status = GATT_SUCCESS;
-
- switch (handle) {
- case HasDbBuilder::kGapDeviceNameValHdl:
- value.resize(20);
- break;
- case HasDbBuilder::kFeaturesValHdl:
- value.resize(1);
- value[0] = current_peer_features_val_.at(conn_id);
- break;
- case HasDbBuilder::kActivePresetIndexValHdl:
- value.resize(1);
- value[0] = current_peer_active_preset_idx_.at(conn_id);
- break;
- case HasDbBuilder::kPresetsCtpValHdl:
- /* passthrough */
- default:
- status = GATT_READ_NOT_PERMIT;
- break;
- }
-
- if (cb)
- cb(conn_id, status, handle, value.size(), value.data(), cb_data);
- }));
-
- /* Default action for the Control Point operation writes */
- ON_CALL(gatt_queue,
- WriteCharacteristic(conn_id, HasDbBuilder::kPresetsCtpValHdl, _,
- GATT_WRITE, _, _))
- .WillByDefault(Invoke([this, address](uint16_t conn_id, uint16_t handle,
- std::vector<uint8_t> value,
- tGATT_WRITE_TYPE write_type,
- GATT_WRITE_OP_CB cb,
- void* cb_data) {
- auto pp = value.data();
- auto len = value.size();
- uint8_t op, index, num_of_indices;
-
- const bool indicate = false;
-
- if (len < 1) {
- if (cb)
- cb(conn_id, GATT_INVALID_ATTR_LEN, handle, value.size(),
- value.data(), cb_data);
- return;
- }
-
- STREAM_TO_UINT8(op, pp)
- --len;
- if (op >
- static_cast<
- std::underlying_type_t<::le_audio::has::PresetCtpOpcode>>(
- ::le_audio::has::PresetCtpOpcode::OP_MAX_)) {
- /* Invalid Opcode */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x80, handle, value.size(),
- value.data(), cb_data);
- return;
- }
-
- switch (static_cast<::le_audio::has::PresetCtpOpcode>(op)) {
- case ::le_audio::has::PresetCtpOpcode::READ_PRESETS:
- if (len < 2) {
- if (cb)
- cb(conn_id, GATT_INVALID_ATTR_LEN, handle, value.size(),
- value.data(), cb_data);
-
- } else {
- STREAM_TO_UINT8(index, pp);
- STREAM_TO_UINT8(num_of_indices, pp);
- len -= 2;
- ASSERT_EQ(0u, len);
-
- InjectNotifyReadPresetsResponse(conn_id, address, handle, value,
- indicate, index, num_of_indices,
- cb, cb_data);
- }
- break;
-
- case ::le_audio::has::PresetCtpOpcode::SET_ACTIVE_PRESET: {
- if (len < 1) {
- if (cb)
- cb(conn_id, GATT_INVALID_ATTR_LEN, handle, value.size(),
- value.data(), cb_data);
- break;
- }
- STREAM_TO_UINT8(index, pp);
- --len;
- ASSERT_EQ(0u, len);
-
- auto presets = current_peer_presets_.at(conn_id);
- if (presets.count(index)) {
- current_peer_active_preset_idx_.insert_or_assign(conn_id,
- index);
- if (cb)
- cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(),
- cb_data);
- InjectActivePresetNotification(conn_id, address, handle, value,
- index, cb, cb_data);
- } else {
- /* Preset Operation Not Possible */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x83, handle, value.size(),
- value.data(), cb_data);
- }
- } break;
-
- case ::le_audio::has::PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC: {
- auto features = current_peer_features_val_.at(conn_id);
- if ((features & ::bluetooth::has::
- kFeatureBitPresetSynchronizationSupported) ==
- 0) {
- /* Synchronization Not Supported */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x82, handle, value.size(),
- value.data(), cb_data);
- break;
- }
-
- if (len < 1) {
- if (cb)
- cb(conn_id, GATT_INVALID_ATTR_LEN, handle, value.size(),
- value.data(), cb_data);
- break;
- }
- STREAM_TO_UINT8(index, pp);
- --len;
- ASSERT_EQ(0u, len);
-
- auto csis_api = CsisClient::Get();
- int group_id = bluetooth::groups::kGroupUnknown;
- if (csis_api != nullptr) {
- group_id = csis_api->GetGroupId(
- address, ::le_audio::uuid::kCapServiceUuid);
- }
-
- if (group_id != bluetooth::groups::kGroupUnknown) {
- if (cb)
- cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(),
- cb_data);
- /* Send notification from all grouped devices */
- auto addresses = csis_api->GetDeviceList(group_id);
- for (auto& addr : addresses) {
- auto conn = GetTestConnId(addr);
- InjectActivePresetNotification(conn, addr, handle, value,
- index, cb, cb_data);
- }
- } else {
- /* Preset Operation Not Possible */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x83, handle, value.size(),
- value.data(), cb_data);
- }
- } break;
-
- case ::le_audio::has::PresetCtpOpcode::SET_NEXT_PRESET: {
- ASSERT_EQ(0u, len);
- ASSERT_NE(0u, current_peer_active_preset_idx_.count(conn_id));
- ASSERT_NE(0u, current_peer_presets_.count(conn_id));
-
- auto current_preset = current_peer_active_preset_idx_.at(conn_id);
- auto presets = current_peer_presets_.at(conn_id);
- auto current = presets.find(current_preset);
- if (current != presets.end()) {
- ++current;
- if (current == presets.end()) current = presets.begin();
-
- current_peer_active_preset_idx_.insert_or_assign(
- conn_id, current->GetIndex());
- InjectActivePresetNotification(conn_id, address, handle, value,
- current->GetIndex(), cb,
- cb_data);
-
- } else {
- /* Preset Operation Not Possible */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x83, handle, value.size(),
- value.data(), cb_data);
- }
- } break;
-
- case ::le_audio::has::PresetCtpOpcode::SET_PREV_PRESET: {
- ASSERT_EQ(0u, len);
- ASSERT_NE(0u, current_peer_active_preset_idx_.count(conn_id));
- ASSERT_NE(0u, current_peer_presets_.count(conn_id));
-
- auto current_preset = current_peer_active_preset_idx_.at(conn_id);
- auto presets = current_peer_presets_.at(conn_id);
- auto rit = presets.rbegin();
- while (rit != presets.rend()) {
- if (rit->GetIndex() == current_preset) {
- rit++;
- /* Wrap around */
- if (rit == presets.rend()) {
- rit = presets.rbegin();
- }
- break;
- }
- rit++;
- }
-
- if (rit != presets.rend()) {
- if (cb)
- cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(),
- cb_data);
- current_peer_active_preset_idx_.insert_or_assign(
- conn_id, rit->GetIndex());
- InjectActivePresetNotification(conn_id, address, handle, value,
- rit->GetIndex(), cb, cb_data);
- } else {
- /* Preset Operation Not Possible */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x83, handle, value.size(),
- value.data(), cb_data);
- }
- } break;
-
- case ::le_audio::has::PresetCtpOpcode::SET_NEXT_PRESET_SYNC: {
- ASSERT_EQ(0u, len);
- auto features = current_peer_features_val_.at(conn_id);
- if ((features & ::bluetooth::has::
- kFeatureBitPresetSynchronizationSupported) ==
- 0) {
- /* Synchronization Not Supported */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x82, handle, value.size(),
- value.data(), cb_data);
- break;
- }
-
- auto current_preset = current_peer_active_preset_idx_.at(conn_id);
- auto presets = current_peer_presets_.at(conn_id);
- auto rit = presets.begin();
- while (rit != presets.end()) {
- if (rit->GetIndex() == current_preset) {
- rit++;
- /* Wrap around */
- if (rit == presets.end()) {
- rit = presets.begin();
- }
- break;
- }
- rit++;
- }
-
- if (rit != presets.end()) {
- auto synced_group = mock_csis_client_module_.GetGroupId(
- GetTestAddress(conn_id), ::le_audio::uuid::kCapServiceUuid);
- auto addresses =
- mock_csis_client_module_.GetDeviceList(synced_group);
-
- // Emulate locally synced op. - notify from all of the devices
- for (auto addr : addresses) {
- auto cid = GetTestConnId(addr);
- if ((cid == conn_id) && (cb != nullptr))
- cb(cid, GATT_SUCCESS, handle, value.size(), value.data(),
- cb_data);
-
- current_peer_active_preset_idx_.insert_or_assign(
- conn_id, rit->GetIndex());
- InjectActivePresetNotification(cid, addr, handle, value,
- rit->GetIndex(), cb, cb_data);
- }
- } else {
- /* Preset Operation Not Possible */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x83, handle, value.size(),
- value.data(), cb_data);
- }
- } break;
-
- case ::le_audio::has::PresetCtpOpcode::SET_PREV_PRESET_SYNC: {
- ASSERT_EQ(0u, len);
- auto features = current_peer_features_val_.at(conn_id);
- if ((features & ::bluetooth::has::
- kFeatureBitPresetSynchronizationSupported) ==
- 0) {
- /* Synchronization Not Supported */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x82, handle, value.size(),
- value.data(), cb_data);
- break;
- }
-
- auto current_preset = current_peer_active_preset_idx_.at(conn_id);
- auto presets = current_peer_presets_.at(conn_id);
- auto rit = presets.rbegin();
- while (rit != presets.rend()) {
- if (rit->GetIndex() == current_preset) {
- rit++;
- /* Wrap around */
- if (rit == presets.rend()) {
- rit = presets.rbegin();
- }
- break;
- }
- rit++;
- }
-
- if (rit != presets.rend()) {
- auto synced_group = mock_csis_client_module_.GetGroupId(
- GetTestAddress(conn_id), ::le_audio::uuid::kCapServiceUuid);
- auto addresses =
- mock_csis_client_module_.GetDeviceList(synced_group);
-
- // Emulate locally synced op. - notify from all of the devices
- for (auto addr : addresses) {
- auto cid = GetTestConnId(addr);
- if ((cid == conn_id) && (cb != nullptr))
- cb(cid, GATT_SUCCESS, handle, value.size(), value.data(),
- cb_data);
-
- current_peer_active_preset_idx_.insert_or_assign(
- conn_id, rit->GetIndex());
- InjectActivePresetNotification(cid, addr, handle, value,
- rit->GetIndex(), cb, cb_data);
- }
- } else {
- /* Preset Operation Not Possible */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x83, handle, value.size(),
- value.data(), cb_data);
- }
- } break;
-
- case ::le_audio::has::PresetCtpOpcode::WRITE_PRESET_NAME: {
- STREAM_TO_UINT8(index, pp);
- --len;
- auto name = std::string(pp, pp + len);
- len = 0;
-
- ASSERT_NE(0u, current_peer_presets_.count(conn_id));
- auto presets = current_peer_presets_.at(conn_id);
- auto rit = presets.rbegin();
- auto current = rit;
- while (rit != presets.rend()) {
- if (rit->GetIndex() == index) {
- current = rit;
- rit++;
- break;
- }
- rit++;
- }
-
- auto prev_index = (rit == presets.rend()) ? 0 : rit->GetIndex();
-
- ASSERT_NE(current, presets.rend());
- if (cb)
- cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(),
- cb_data);
-
- auto new_preset = HasPreset(current->GetIndex(),
- current->GetProperties(), name);
- presets.erase(current->GetIndex());
- presets.insert(new_preset);
-
- InjectPresetChanged(
- conn_id, address, indicate, new_preset, prev_index,
- ::le_audio::has::PresetCtpChangeId::PRESET_GENERIC_UPDATE,
- true);
- } break;
-
- default:
- if (cb)
- cb(conn_id, GATT_INVALID_HANDLE, handle, value.size(),
- value.data(), cb_data);
- break;
- }
- }));
- }
-
- void SetUp(void) override {
- mock_function_count_map.clear();
- controller::SetMockControllerInterface(&controller_interface_);
- bluetooth::manager::SetMockBtmInterface(&btm_interface);
- bluetooth::storage::SetMockBtifStorageInterface(&btif_storage_interface_);
- gatt::SetMockBtaGattInterface(&gatt_interface);
- gatt::SetMockBtaGattQueue(&gatt_queue);
- callbacks.reset(new MockHasCallbacks());
-
- encryption_result = true;
-
- MockCsisClient::SetMockInstanceForTesting(&mock_csis_client_module_);
- ON_CALL(mock_csis_client_module_, Get())
- .WillByDefault(Return(&mock_csis_client_module_));
- ON_CALL(mock_csis_client_module_, IsCsisClientRunning())
- .WillByDefault(Return(true));
-
- /* default action for GetCharacteristic function call */
- ON_CALL(gatt_interface, GetCharacteristic(_, _))
- .WillByDefault(
- Invoke([&](uint16_t conn_id,
- uint16_t handle) -> const gatt::Characteristic* {
- std::list<gatt::Service>& services = services_map[conn_id];
- for (auto const& service : services) {
- for (auto const& characteristic : service.characteristics) {
- if (characteristic.value_handle == handle) {
- return &characteristic;
- }
- }
- }
-
- return nullptr;
- }));
-
- /* default action for GetOwningService function call */
- ON_CALL(gatt_interface, GetOwningService(_, _))
- .WillByDefault(Invoke(
- [&](uint16_t conn_id, uint16_t handle) -> const gatt::Service* {
- std::list<gatt::Service>& services = services_map[conn_id];
- for (auto const& service : services) {
- if (service.handle <= handle && service.end_handle >= handle) {
- return &service;
- }
- }
-
- return nullptr;
- }));
-
- ON_CALL(gatt_interface, ServiceSearchRequest(_, _))
- .WillByDefault(WithArg<0>(Invoke(
- [&](uint16_t conn_id) { InjectSearchCompleteEvent(conn_id); })));
-
- /* default action for GetServices function call */
- ON_CALL(gatt_interface, GetServices(_))
- .WillByDefault(WithArg<0>(
- Invoke([&](uint16_t conn_id) -> std::list<gatt::Service>* {
- return &services_map[conn_id];
- })));
-
- /* default action for RegisterForNotifications function call */
- ON_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, _))
- .WillByDefault(Return(GATT_SUCCESS));
-
- /* default action for DeregisterForNotifications function call */
- ON_CALL(gatt_interface, DeregisterForNotifications(gatt_if, _, _))
- .WillByDefault(Return(GATT_SUCCESS));
-
- /* default action for WriteDescriptor function call */
- ON_CALL(gatt_queue, WriteDescriptor(_, _, _, _, _, _))
- .WillByDefault(
- Invoke([](uint16_t conn_id, uint16_t handle,
- std::vector<uint8_t> value, tGATT_WRITE_TYPE write_type,
- GATT_WRITE_OP_CB cb, void* cb_data) -> void {
- if (cb)
- cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(),
- cb_data);
- }));
-
- /* by default connect only direct connection requests */
- ON_CALL(gatt_interface, Open(_, _, _, _))
- .WillByDefault(
- Invoke([&](tGATT_IF client_if, const RawAddress& remote_bda,
- tBTM_BLE_CONN_TYPE connection_type, bool opportunistic) {
- if (connection_type == BTM_BLE_DIRECT_CONNECTION)
- InjectConnectedEvent(remote_bda, GetTestConnId(remote_bda));
- }));
-
- ON_CALL(gatt_interface, Close(_))
- .WillByDefault(Invoke(
- [&](uint16_t conn_id) { InjectDisconnectedEvent(conn_id); }));
- }
-
- void TearDown(void) override {
- services_map.clear();
- gatt::SetMockBtaGattQueue(nullptr);
- gatt::SetMockBtaGattInterface(nullptr);
- bluetooth::storage::SetMockBtifStorageInterface(nullptr);
- bluetooth::manager::SetMockBtmInterface(nullptr);
- controller::SetMockControllerInterface(nullptr);
- callbacks.reset();
-
- current_peer_active_preset_idx_.clear();
- current_peer_features_val_.clear();
- }
-
- void TestAppRegister(void) {
- BtaAppRegisterCallback app_register_callback;
- EXPECT_CALL(gatt_interface, AppRegister(_, _, _))
- .WillOnce(DoAll(SaveArg<0>(&gatt_callback),
- SaveArg<1>(&app_register_callback)));
- HasClient::Initialize(callbacks.get(), base::DoNothing());
- ASSERT_TRUE(gatt_callback);
- ASSERT_TRUE(app_register_callback);
- app_register_callback.Run(gatt_if, GATT_SUCCESS);
- ASSERT_TRUE(HasClient::IsHasClientRunning());
- Mock::VerifyAndClearExpectations(&gatt_interface);
- }
-
- void TestAppUnregister(void) {
- EXPECT_CALL(gatt_interface, AppDeregister(gatt_if));
- HasClient::CleanUp();
- ASSERT_FALSE(HasClient::IsHasClientRunning());
- gatt_callback = nullptr;
- }
-
- void TestConnect(const RawAddress& address) {
- ON_CALL(btm_interface, BTM_IsEncrypted(address, _))
- .WillByDefault(DoAll(Return(encryption_result)));
-
- EXPECT_CALL(gatt_interface,
- Open(gatt_if, address, BTM_BLE_DIRECT_CONNECTION, _));
- HasClient::Get()->Connect(address);
-
- Mock::VerifyAndClearExpectations(&*callbacks);
- Mock::VerifyAndClearExpectations(&gatt_queue);
- Mock::VerifyAndClearExpectations(&gatt_interface);
- Mock::VerifyAndClearExpectations(&btm_interface);
- }
-
- void TestDisconnect(const RawAddress& address, uint16_t conn_id) {
- EXPECT_CALL(gatt_interface, CancelOpen(_, address, _)).Times(AnyNumber());
- if (conn_id != GATT_INVALID_CONN_ID) {
- assert(0);
- EXPECT_CALL(gatt_interface, Close(conn_id));
- } else {
- EXPECT_CALL(gatt_interface, CancelOpen(gatt_if, address, _));
- }
- HasClient::Get()->Disconnect(address);
- }
-
- void TestAddFromStorage(const RawAddress& address, uint8_t features,
- bool auto_connect) {
- if (auto_connect) {
- EXPECT_CALL(gatt_interface,
- Open(gatt_if, address, BTM_BLE_BKG_CONNECT_ALLOW_LIST, _));
- HasClient::Get()->AddFromStorage(address, features, auto_connect);
-
- /* Inject connected event for autoconnect/background connection */
- InjectConnectedEvent(address, GetTestConnId(address));
- } else {
- EXPECT_CALL(gatt_interface, Open(gatt_if, address, _, _)).Times(0);
- HasClient::Get()->AddFromStorage(address, features, auto_connect);
- }
-
- Mock::VerifyAndClearExpectations(&gatt_interface);
- }
-
- void InjectConnectedEvent(const RawAddress& address, uint16_t conn_id,
- tGATT_STATUS status = GATT_SUCCESS) {
- tBTA_GATTC_OPEN event_data = {
- .status = status,
- .conn_id = conn_id,
- .client_if = gatt_if,
- .remote_bda = address,
- .transport = GATT_TRANSPORT_LE,
- .mtu = 240,
- };
-
- connected_devices[conn_id] = address;
- gatt_callback(BTA_GATTC_OPEN_EVT, (tBTA_GATTC*)&event_data);
- }
-
- void InjectDisconnectedEvent(
- uint16_t conn_id,
- tGATT_DISCONN_REASON reason = GATT_CONN_TERMINATE_LOCAL_HOST,
- bool allow_fake_conn = false) {
- if (!allow_fake_conn) ASSERT_NE(connected_devices.count(conn_id), 0u);
-
- tBTA_GATTC_CLOSE event_data = {
- .status = GATT_SUCCESS,
- .conn_id = conn_id,
- .client_if = gatt_if,
- .remote_bda = connected_devices[conn_id],
- .reason = reason,
- };
-
- connected_devices.erase(conn_id);
- gatt_callback(BTA_GATTC_CLOSE_EVT, (tBTA_GATTC*)&event_data);
- }
-
- void InjectSearchCompleteEvent(uint16_t conn_id) {
- tBTA_GATTC_SEARCH_CMPL event_data = {
- .status = GATT_SUCCESS,
- .conn_id = conn_id,
- };
-
- gatt_callback(BTA_GATTC_SEARCH_CMPL_EVT, (tBTA_GATTC*)&event_data);
- }
-
- void InjectNotificationEvent(const RawAddress& test_address, uint16_t conn_id,
- uint16_t handle, std::vector<uint8_t> value,
- bool indicate = false) {
- tBTA_GATTC_NOTIFY event_data = {
- .conn_id = conn_id,
- .bda = test_address,
- .handle = handle,
- .len = (uint8_t)value.size(),
- .is_notify = !indicate,
- };
-
- ASSERT_TRUE(value.size() < GATT_MAX_ATTR_LEN);
- std::copy(value.begin(), value.end(), event_data.value);
- gatt_callback(BTA_GATTC_NOTIF_EVT, (tBTA_GATTC*)&event_data);
- }
-
- void SetEncryptionResult(const RawAddress& address, bool success) {
- encryption_result = success;
- ON_CALL(btm_interface, BTM_IsEncrypted(address, _))
- .WillByDefault(Return(success));
- ON_CALL(btm_interface, GetSecurityFlagsByTransport(address, NotNull(), _))
- .WillByDefault(
- DoAll(SetArgPointee<1>(success ? BTM_SEC_FLAG_ENCRYPTED : 0),
- Return(true)));
- if (!success) {
- EXPECT_CALL(btm_interface,
- SetEncryption(address, _, NotNull(), _, BTM_BLE_SEC_ENCRYPT))
- .WillOnce(Invoke(
- [success](const RawAddress& bd_addr, tBT_TRANSPORT transport,
- tBTM_SEC_CALLBACK* p_callback, void* p_ref_data,
- tBTM_BLE_SEC_ACT sec_act) -> tBTM_STATUS {
- p_callback(&bd_addr, transport, p_ref_data,
- success ? BTM_SUCCESS : BTM_FAILED_ON_SECURITY);
- return BTM_SUCCESS;
- }));
- }
- }
-
- void InjectNotifyReadPresetResponse(uint16_t conn_id,
- RawAddress const& address,
- uint16_t handle, const HasPreset& preset,
- bool indicate, bool is_last) {
- std::vector<uint8_t> value;
-
- value.push_back(
- static_cast<std::underlying_type_t<::le_audio::has::PresetCtpOpcode>>(
- ::le_audio::has::PresetCtpOpcode::READ_PRESET_RESPONSE));
- value.push_back(is_last ? 0x01 : 0x00);
-
- preset.ToCharacteristicValue(value);
- InjectNotificationEvent(address, conn_id, handle, value, indicate);
- }
-
- void InjectPresetChanged(uint16_t conn_id, RawAddress const& address,
- bool indicate, const HasPreset& preset,
- uint8_t prev_index,
- ::le_audio::has::PresetCtpChangeId change_id,
- bool is_last) {
- std::vector<uint8_t> value;
-
- value.push_back(
- static_cast<std::underlying_type_t<::le_audio::has::PresetCtpOpcode>>(
- ::le_audio::has::PresetCtpOpcode::PRESET_CHANGED));
- value.push_back(static_cast<uint8_t>(change_id));
- value.push_back(is_last ? 0x01 : 0x00);
-
- switch (change_id) {
- case ::le_audio::has::PresetCtpChangeId::PRESET_GENERIC_UPDATE:
- value.push_back(prev_index);
- preset.ToCharacteristicValue(value);
- break;
- case ::le_audio::has::PresetCtpChangeId::PRESET_DELETED:
- case ::le_audio::has::PresetCtpChangeId::PRESET_AVAILABLE:
- case ::le_audio::has::PresetCtpChangeId::PRESET_UNAVAILABLE:
- default:
- value.push_back(preset.GetIndex());
- break;
- }
-
- InjectNotificationEvent(address, conn_id, HasDbBuilder::kPresetsCtpValHdl,
- value, indicate);
- }
-
- void InjectNotifyReadPresetsResponse(
- uint16_t conn_id, RawAddress const& address, uint16_t handle,
- std::vector<uint8_t> value, bool indicate, int index, int num_of_indices,
- GATT_WRITE_OP_CB cb, void* cb_data) {
- auto presets = current_peer_presets_.at(conn_id);
- LOG_ASSERT(!presets.empty()) << __func__ << " Mocking error!";
-
- /* Index is a start index, not necessary is a valid index for the
- * peer device */
- auto preset = presets.find(index);
- while (preset == presets.end() &&
- index++ <= ::le_audio::has::kMaxNumOfPresets) {
- preset = presets.find(index);
- }
-
- if (preset == presets.end()) {
- /* operation not possible */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x83, handle, value.size(), value.data(),
- cb_data);
-
- return;
- }
-
- if (cb)
- cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data);
- /* Notify presets */
- int num_of_notif = 1;
- while (1) {
- bool last =
- preset == std::prev(presets.end()) || num_of_notif == num_of_indices;
- InjectNotifyReadPresetResponse(conn_id, address, handle, *preset,
- indicate, (last));
- if (last) return;
-
- num_of_notif++;
- preset++;
- }
- }
-
- void InjectActivePresetNotification(uint16_t conn_id,
- RawAddress const& address,
- uint16_t handle,
- std::vector<uint8_t> wr_value,
- uint8_t index, GATT_WRITE_OP_CB cb,
- void* cb_data) {
- auto presets = current_peer_presets_.at(conn_id);
- LOG_ASSERT(!presets.empty()) << __func__ << " Mocking error!";
-
- auto preset = presets.find(index);
- if (preset == presets.end()) {
- /* preset operation not possible */
- if (cb)
- cb(conn_id, (tGATT_STATUS)0x83, handle, wr_value.size(),
- wr_value.data(), cb_data);
- return;
- }
-
- std::vector<uint8_t> value;
- value.push_back(index);
- InjectNotificationEvent(
- address, conn_id, HasDbBuilder::kActivePresetIndexValHdl, value, false);
- }
-
- void SetSampleDatabaseHasNoFeatures(const RawAddress& address) {
- HasDbBuilder builder = {
- .has = true,
- .features = false,
- .features_ntf = false,
- .preset_cp = true,
- .preset_cp_ntf = false,
- .preset_cp_ind = true,
- .active_preset_idx = true,
- .active_preset_idx_ntf = true,
- };
- set_sample_database(address, builder);
- }
-
- void SetSampleDatabaseHasNoPresetChange(const RawAddress& address,
- uint8_t features_value = 0x00) {
- HasDbBuilder builder = {
- .has = true,
- .features = true,
- .features_ntf = false,
- .preset_cp = false,
- .preset_cp_ntf = false,
- .preset_cp_ind = false,
- .active_preset_idx = false,
- .active_preset_idx_ntf = false,
- };
- set_sample_database(address, builder, features_value);
- }
-
- void SetSampleDatabaseHasNoOptionalNtf(const RawAddress& address,
- uint8_t features = 0x00) {
- HasDbBuilder builder = {
- .has = true,
- .features = true,
- .features_ntf = false,
- .preset_cp = true,
- .preset_cp_ntf = false,
- .preset_cp_ind = true,
- .active_preset_idx = true,
- .active_preset_idx_ntf = true,
- };
- set_sample_database(address, builder, features);
- }
-
- void SetSampleDatabaseNoHas(const RawAddress& address,
- uint8_t features = 0x00) {
- HasDbBuilder builder = {
- .has = false,
- .features = false,
- .features_ntf = false,
- .preset_cp = false,
- .preset_cp_ntf = false,
- .preset_cp_ind = false,
- .active_preset_idx = true,
- .active_preset_idx_ntf = true,
- };
- set_sample_database(address, builder, features);
- }
-
- void SetSampleDatabaseHasBrokenNoActivePreset(const RawAddress& address,
- uint8_t features = 0x00) {
- HasDbBuilder builder = {
- .has = true,
- .features = true,
- .features_ntf = false,
- .preset_cp = true,
- .preset_cp_ntf = true,
- .preset_cp_ind = true,
- .active_preset_idx = false,
- .active_preset_idx_ntf = false,
- };
- set_sample_database(address, builder, features);
- }
-
- void SetSampleDatabaseHasBrokenNoActivePresetNtf(const RawAddress& address,
- uint8_t features = 0x00) {
- HasDbBuilder builder = {
- .has = true,
- .features = true,
- .features_ntf = false,
- .preset_cp = true,
- .preset_cp_ntf = true,
- .preset_cp_ind = true,
- .active_preset_idx = true,
- .active_preset_idx_ntf = false,
- };
- set_sample_database(address, builder, features);
- }
-
- void SetSampleDatabaseHasOnlyFeaturesNtf(const RawAddress& address,
- uint8_t features = 0x00) {
- HasDbBuilder builder = {
- .has = true,
- .features = true,
- .features_ntf = true,
- .preset_cp = false,
- .preset_cp_ntf = false,
- .preset_cp_ind = false,
- .active_preset_idx = false,
- .active_preset_idx_ntf = false,
- };
- set_sample_database(address, builder, features);
- }
-
- void SetSampleDatabaseHasOnlyFeaturesNoNtf(const RawAddress& address,
- uint8_t features = 0x00) {
- HasDbBuilder builder = {
- .has = true,
- .features = true,
- .features_ntf = false,
- .preset_cp = false,
- .preset_cp_ntf = false,
- .preset_cp_ind = false,
- .active_preset_idx = false,
- .active_preset_idx_ntf = false,
- };
- set_sample_database(address, builder, features);
- }
-
- void SetSampleDatabaseHasPresetsNtf(
- const RawAddress& address,
- uint8_t features = bluetooth::has::kFeatureBitHearingAidTypeMonaural,
- std::optional<std::set<HasPreset, HasPreset::ComparatorDesc>> presets =
- std::nullopt) {
- HasDbBuilder builder = {
- .has = true,
- .features = true,
- .features_ntf = true,
- .preset_cp = true,
- .preset_cp_ntf = true,
- .preset_cp_ind = true,
- .active_preset_idx = true,
- .active_preset_idx_ntf = true,
- };
-
- set_sample_database(address, builder, features, presets);
- }
-
- void SetSampleDatabaseHasNoPresetsFlagsOnly(const RawAddress& address) {
- uint8_t features = bluetooth::has::kFeatureBitHearingAidTypeMonaural;
- HasDbBuilder builder = {
- .has = true,
- .features = true,
- .features_ntf = true,
- .preset_cp = false,
- .preset_cp_ntf = false,
- .preset_cp_ind = false,
- .active_preset_idx = false,
- .active_preset_idx_ntf = false,
- };
-
- set_sample_database(address, builder, features, std::nullopt);
- }
-
- std::unique_ptr<MockHasCallbacks> callbacks;
- bluetooth::manager::MockBtmInterface btm_interface;
- bluetooth::storage::MockBtifStorageInterface btif_storage_interface_;
- controller::MockControllerInterface controller_interface_;
- gatt::MockBtaGattInterface gatt_interface;
- gatt::MockBtaGattQueue gatt_queue;
- MockCsisClient mock_csis_client_module_;
- tBTA_GATTC_CBACK* gatt_callback;
- const uint8_t gatt_if = 0xfe;
- std::map<uint8_t, RawAddress> connected_devices;
- std::map<uint16_t, std::list<gatt::Service>> services_map;
- bool encryption_result;
-};
-
-class HasClientTest : public HasClientTestBase {
- void SetUp(void) override {
- HasClientTestBase::SetUp();
- TestAppRegister();
- }
- void TearDown(void) override {
- TestAppUnregister();
- HasClientTestBase::TearDown();
- }
-};
-
-TEST_F(HasClientTestBase, test_get_uninitialized) {
- ASSERT_DEATH(HasClient::Get(), "");
-}
-
-TEST_F(HasClientTestBase, test_initialize) {
- HasClient::Initialize(callbacks.get(), base::DoNothing());
- ASSERT_TRUE(HasClient::IsHasClientRunning());
- HasClient::CleanUp();
-}
-
-TEST_F(HasClientTestBase, test_initialize_twice) {
- HasClient::Initialize(callbacks.get(), base::DoNothing());
- HasClient* has_p = HasClient::Get();
- HasClient::Initialize(callbacks.get(), base::DoNothing());
- ASSERT_EQ(has_p, HasClient::Get());
- HasClient::CleanUp();
-}
-
-TEST_F(HasClientTestBase, test_cleanup_initialized) {
- HasClient::Initialize(callbacks.get(), base::DoNothing());
- HasClient::CleanUp();
- ASSERT_FALSE(HasClient::IsHasClientRunning());
-}
-
-TEST_F(HasClientTestBase, test_cleanup_uninitialized) {
- HasClient::CleanUp();
- ASSERT_FALSE(HasClient::IsHasClientRunning());
-}
-
-TEST_F(HasClientTestBase, test_app_registration) {
- TestAppRegister();
- TestAppUnregister();
-}
-
-TEST_F(HasClientTest, test_connect) { TestConnect(GetTestAddress(1)); }
-
-TEST_F(HasClientTest, test_add_from_storage) {
- TestAddFromStorage(GetTestAddress(1), 0, true);
- TestAddFromStorage(GetTestAddress(2), 0, false);
-}
-
-TEST_F(HasClientTest, test_disconnect_non_connected) {
- const RawAddress test_address = GetTestAddress(1);
-
- /* Override the default action to prevent us sendind the connected event */
- EXPECT_CALL(gatt_interface,
- Open(gatt_if, test_address, BTM_BLE_DIRECT_CONNECTION, _))
- .WillOnce(Return());
- HasClient::Get()->Connect(test_address);
- TestDisconnect(test_address, GATT_INVALID_CONN_ID);
-}
-
-TEST_F(HasClientTest, test_has_connected) {
- const RawAddress test_address = GetTestAddress(1);
- /* Minimal possible HA device (only feature flags) */
- SetSampleDatabaseHasNoPresetChange(
- test_address, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
-
- EXPECT_CALL(
- *callbacks,
- OnDeviceAvailable(test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBinaural));
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- TestConnect(test_address);
-}
-
-TEST_F(HasClientTest, test_disconnect_connected) {
- const RawAddress test_address = GetTestAddress(1);
- /* Minimal possible HA device (only feature flags) */
- SetSampleDatabaseHasNoPresetChange(
- test_address, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address))
- .Times(1);
- TestConnect(test_address);
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::DISCONNECTED, test_address))
- .Times(1);
- TestDisconnect(test_address, 1);
-}
-
-TEST_F(HasClientTest, test_disconnected_while_autoconnect) {
- const RawAddress test_address = GetTestAddress(1);
- TestAddFromStorage(test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBinaural, true);
- /* autoconnect - don't indicate disconnection */
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::DISCONNECTED, test_address))
- .Times(0);
- /* Verify that the device still can connect in te background */
- InjectDisconnectedEvent(1, GATT_CONN_TERMINATE_PEER_USER, true);
-}
-
-TEST_F(HasClientTest, test_encryption_failed) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasNoPresetChange(
- test_address, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::DISCONNECTED, test_address))
- .Times(1);
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address))
- .Times(0);
- SetEncryptionResult(test_address, false);
- TestConnect(test_address);
-}
-
-TEST_F(HasClientTest, test_reconnect_after_encryption_failed) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasNoPresetChange(
- test_address, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::DISCONNECTED, test_address))
- .Times(1);
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address))
- .Times(0);
- SetEncryptionResult(test_address, false);
- TestConnect(test_address);
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address))
- .Times(1);
- SetEncryptionResult(test_address, true);
- InjectConnectedEvent(test_address, GetTestConnId(test_address));
-}
-
-TEST_F(HasClientTest, test_reconnect_after_encryption_failed_from_storage) {
- const RawAddress test_address = GetTestAddress(1);
-
- SetSampleDatabaseHasNoPresetChange(
- test_address, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
- SetEncryptionResult(test_address, false);
- TestAddFromStorage(test_address, 0, true);
- /* autoconnect - don't indicate disconnection */
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::DISCONNECTED, test_address))
- .Times(0);
- Mock::VerifyAndClearExpectations(&btm_interface);
-
- /* Fake no persistent storage data */
- ON_CALL(btif_storage_interface_, GetLeaudioHasPresets(_, _, _))
- .WillByDefault([]() { return false; });
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address))
- .Times(1);
- SetEncryptionResult(test_address, true);
- InjectConnectedEvent(test_address, GetTestConnId(test_address));
-}
-
-TEST_F(HasClientTest, test_load_from_storage_and_connect) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(test_address, kFeatureBitDynamicPresets);
- SetEncryptionResult(test_address, true);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> has_presets = {{
- HasPreset(5, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "YourWritablePreset5"),
- HasPreset(55, HasPreset::kPropertyAvailable, "YourPreset55"),
- }};
-
- /* Load persistent storage data */
- ON_CALL(btif_storage_interface_, GetLeaudioHasPresets(test_address, _, _))
- .WillByDefault([&has_presets](const RawAddress& address,
- std::vector<uint8_t>& presets_bin,
- uint8_t& active_preset) {
- /* Generate presets binary to be used instead the attribute values */
- HasDevice device(address, 0);
- device.has_presets = has_presets;
- active_preset = 55;
-
- if (device.SerializePresets(presets_bin)) return true;
-
- return false;
- });
-
- EXPECT_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, _))
- .Times(1 // preset control point
- + 1 // active preset
- + 1); // features
-
- EXPECT_CALL(*callbacks,
- OnDeviceAvailable(test_address,
- (kFeatureBitWritablePresets |
- kFeatureBitPresetSynchronizationSupported |
- kFeatureBitHearingAidTypeBanded)));
-
- std::vector<PresetInfo> loaded_preset_details;
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::ALL_PRESET_INFO, _))
- .WillOnce(SaveArg<2>(&loaded_preset_details));
-
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address), 55));
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
-
- /* Expect no read or write operations when loading from storage */
- EXPECT_CALL(gatt_queue, ReadCharacteristic(1, _, _, _)).Times(0);
- EXPECT_CALL(gatt_queue, WriteDescriptor(1, _, _, _, _, _)).Times(3);
-
- TestAddFromStorage(test_address,
- kFeatureBitWritablePresets |
- kFeatureBitPresetSynchronizationSupported |
- kFeatureBitHearingAidTypeBanded,
- true);
-
- for (auto const& info : loaded_preset_details) {
- auto preset = has_presets.find(info.preset_index);
- ASSERT_NE(preset, has_presets.end());
- if (preset->GetProperties() & HasPreset::kPropertyAvailable)
- ASSERT_TRUE(info.available);
- if (preset->GetProperties() & HasPreset::kPropertyWritable)
- ASSERT_TRUE(info.writable);
- ASSERT_EQ(preset->GetName(), info.preset_name);
- }
-}
-
-TEST_F(HasClientTest, test_load_from_storage) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(test_address, kFeatureBitDynamicPresets);
- SetEncryptionResult(test_address, true);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> has_presets = {{
- HasPreset(5, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "YourWritablePreset5"),
- HasPreset(55, HasPreset::kPropertyAvailable, "YourPreset55"),
- }};
-
- /* Load persistent storage data */
- ON_CALL(btif_storage_interface_, GetLeaudioHasPresets(test_address, _, _))
- .WillByDefault([&has_presets](const RawAddress& address,
- std::vector<uint8_t>& presets_bin,
- uint8_t& active_preset) {
- /* Generate presets binary to be used instead the attribute values */
- HasDevice device(address, 0);
- device.has_presets = has_presets;
- active_preset = 55;
-
- if (device.SerializePresets(presets_bin)) return true;
-
- return false;
- });
-
- EXPECT_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, _))
- .Times(0); // features
-
- EXPECT_CALL(*callbacks,
- OnDeviceAvailable(test_address,
- (kFeatureBitWritablePresets |
- kFeatureBitPresetSynchronizationSupported |
- kFeatureBitHearingAidTypeBanded)));
-
- std::vector<PresetInfo> loaded_preset_details;
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::ALL_PRESET_INFO, _))
- .Times(0);
-
- /* Expect no read or write operations when loading from storage */
- EXPECT_CALL(gatt_queue, ReadCharacteristic(1, _, _, _)).Times(0);
- EXPECT_CALL(gatt_queue, WriteDescriptor(1, _, _, _, _, _)).Times(0);
-
- TestAddFromStorage(test_address,
- kFeatureBitWritablePresets |
- kFeatureBitPresetSynchronizationSupported |
- kFeatureBitHearingAidTypeBanded,
- false);
-}
-
-TEST_F(HasClientTest, test_write_to_storage) {
- const RawAddress test_address = GetTestAddress(1);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> has_presets = {{
- HasPreset(1, HasPreset::kPropertyAvailable, "Universal"),
- HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "Preset2"),
- }};
- SetSampleDatabaseHasPresetsNtf(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets,
- has_presets);
-
- std::vector<uint8_t> serialized;
- EXPECT_CALL(
- btif_storage_interface_,
- AddLeaudioHasDevice(test_address, _,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets,
- 1))
- .WillOnce(SaveArg<1>(&serialized));
- TestConnect(test_address);
-
- /* Deserialize the written binary to verify the content */
- HasDevice clone(test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets);
- ASSERT_TRUE(HasDevice::DeserializePresets(serialized.data(),
- serialized.size(), clone));
- auto storage_info = clone.GetAllPresetInfo();
- ASSERT_EQ(storage_info.size(), has_presets.size());
- for (auto const& info : storage_info) {
- auto preset = has_presets.find(info.preset_index);
- ASSERT_NE(preset, has_presets.end());
- if (preset->GetProperties() & HasPreset::kPropertyAvailable)
- ASSERT_TRUE(info.available);
- if (preset->GetProperties() & HasPreset::kPropertyWritable)
- ASSERT_TRUE(info.writable);
- ASSERT_EQ(preset->GetName(), info.preset_name);
- }
-}
-
-TEST_F(HasClientTest, test_discovery_basic_has_no_opt_ntf) {
- const RawAddress test_address = GetTestAddress(1);
- auto test_conn_id = GetTestConnId(test_address);
-
- SetSampleDatabaseHasNoOptionalNtf(test_address);
-
- std::variant<RawAddress, int> addr_or_group = test_address;
- std::vector<PresetInfo> preset_details;
- uint8_t active_preset_index;
- uint8_t has_features;
-
- EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _))
- .WillOnce(SaveArg<1>(&has_features));
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- EXPECT_CALL(*callbacks, OnPresetInfo(_, PresetInfoReason::ALL_PRESET_INFO, _))
- .WillOnce(DoAll(SaveArg<0>(&addr_or_group), SaveArg<2>(&preset_details)));
- EXPECT_CALL(*callbacks, OnActivePresetSelected(_, _))
- .WillOnce(
- DoAll(SaveArg<0>(&addr_or_group), SaveArg<1>(&active_preset_index)));
- TestConnect(test_address);
-
- /* Verify sample database content */
- ASSERT_TRUE(std::holds_alternative<RawAddress>(addr_or_group));
- ASSERT_EQ(std::get<RawAddress>(addr_or_group), test_address);
- ASSERT_EQ(has_features, 0x00);
- ASSERT_EQ(active_preset_index,
- current_peer_presets_.at(test_conn_id).begin()->GetIndex());
-
- /* Verify presets */
- uint16_t conn_id = GetTestConnId(test_address);
- ASSERT_NE(preset_details.size(), 0u);
- ASSERT_EQ(current_peer_presets_.at(conn_id).size(), preset_details.size());
-
- for (auto const& preset : current_peer_presets_.at(conn_id)) {
- auto it =
- std::find_if(preset_details.cbegin(), preset_details.cend(),
- [&preset](auto const& preset_info) {
- return preset_info.preset_index == preset.GetIndex();
- });
- ASSERT_NE(it, preset_details.cend());
- ASSERT_EQ(preset.GetName(), it->preset_name);
- ASSERT_EQ(preset.IsAvailable(), it->available);
- ASSERT_EQ(preset.IsWritable(), it->writable);
- }
-
- /* Verify active preset is there */
- ASSERT_EQ(preset_details.size(),
- current_peer_presets_.at(test_conn_id).size());
- ASSERT_TRUE(std::find_if(preset_details.begin(), preset_details.end(),
- [active_preset_index](auto const& preset_info) {
- return preset_info.preset_index ==
- active_preset_index;
- }) != preset_details.end());
-}
-
-TEST_F(HasClientTest, test_discovery_has_not_found) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseNoHas(test_address);
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address))
- .Times(0);
- EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _)).Times(0);
- EXPECT_CALL(*callbacks, OnFeaturesUpdate(test_address, _)).Times(0);
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::DISCONNECTED, test_address));
-
- TestConnect(test_address);
-}
-
-TEST_F(HasClientTest, test_discovery_has_broken_no_active_preset) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasBrokenNoActivePreset(test_address);
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address))
- .Times(0);
- EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _)).Times(0);
- EXPECT_CALL(*callbacks, OnFeaturesUpdate(test_address, _)).Times(0);
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::DISCONNECTED, test_address));
-
- TestConnect(test_address);
-}
-
-TEST_F(HasClientTest, test_discovery_has_broken_no_active_preset_ntf) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasBrokenNoActivePresetNtf(test_address);
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address))
- .Times(0);
- EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _)).Times(0);
- EXPECT_CALL(*callbacks, OnFeaturesUpdate(test_address, _)).Times(0);
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::DISCONNECTED, test_address));
-
- TestConnect(test_address);
-}
-
-TEST_F(HasClientTest, test_discovery_has_features_ntf) {
- const RawAddress test_address = GetTestAddress(1);
- auto test_conn_id = GetTestConnId(test_address);
- uint8_t has_features;
-
- SetSampleDatabaseHasOnlyFeaturesNtf(
- test_address, bluetooth::has::kFeatureBitHearingAidTypeBanded);
-
- EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _))
- .WillOnce(SaveArg<1>(&has_features));
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address))
- .Times(1);
-
- /* Verify subscription to features */
- EXPECT_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(gatt_interface,
- RegisterForNotifications(gatt_if, test_address,
- HasDbBuilder::kFeaturesValHdl));
-
- /* Verify features CCC was written */
- EXPECT_CALL(gatt_queue, WriteDescriptor(test_conn_id, _, _, _, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(gatt_queue,
- WriteDescriptor(test_conn_id, HasDbBuilder::kFeaturesValHdl + 1,
- std::vector<uint8_t>{0x01, 0x00}, _, _, _));
- TestConnect(test_address);
-
- /* Verify features */
- ASSERT_EQ(has_features, bluetooth::has::kFeatureBitHearingAidTypeBanded);
-
- uint8_t new_features;
-
- /* Verify peer features change notification */
- EXPECT_CALL(*callbacks, OnFeaturesUpdate(test_address, _))
- .WillOnce(SaveArg<1>(&new_features));
- InjectNotificationEvent(test_address, test_conn_id,
- HasDbBuilder::kFeaturesValHdl,
- std::vector<uint8_t>({0x00}));
- ASSERT_NE(has_features, new_features);
-}
-
-TEST_F(HasClientTest, test_discovery_has_features_no_ntf) {
- const RawAddress test_address = GetTestAddress(1);
- auto test_conn_id = GetTestConnId(test_address);
- uint8_t has_features;
-
- SetSampleDatabaseHasOnlyFeaturesNoNtf(
- test_address, bluetooth::has::kFeatureBitHearingAidTypeBanded);
-
- EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _))
- .WillOnce(SaveArg<1>(&has_features));
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address))
- .Times(1);
-
- /* Verify no subscription to features */
- EXPECT_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(gatt_interface,
- RegisterForNotifications(gatt_if, test_address,
- HasDbBuilder::kFeaturesValHdl))
- .Times(0);
-
- /* Verify no features CCC was written */
- EXPECT_CALL(gatt_queue, WriteDescriptor(test_conn_id, _, _, _, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(gatt_queue,
- WriteDescriptor(test_conn_id, HasDbBuilder::kFeaturesValHdl + 1,
- _, _, _, _))
- .Times(0);
- TestConnect(test_address);
-
- /* Verify features */
- ASSERT_EQ(has_features, bluetooth::has::kFeatureBitHearingAidTypeBanded);
-}
-
-TEST_F(HasClientTest, test_discovery_has_multiple_presets_ntf) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(
- test_address, bluetooth::has::kFeatureBitHearingAidTypeBanded);
-
- std::variant<RawAddress, int> addr_or_group = test_address;
- std::vector<PresetInfo> preset_details;
- uint8_t active_preset_index;
- uint8_t has_features;
-
- EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _))
- .WillOnce(SaveArg<1>(&has_features));
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- EXPECT_CALL(*callbacks, OnPresetInfo(_, PresetInfoReason::ALL_PRESET_INFO, _))
- .WillOnce(DoAll(SaveArg<0>(&addr_or_group), SaveArg<2>(&preset_details)));
- EXPECT_CALL(*callbacks, OnActivePresetSelected(_, _))
- .WillOnce(
- DoAll(SaveArg<0>(&addr_or_group), SaveArg<1>(&active_preset_index)));
-
- /* Verify subscription to control point */
- EXPECT_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, _))
- .Times(AnyNumber());
- EXPECT_CALL(gatt_interface,
- RegisterForNotifications(gatt_if, test_address,
- HasDbBuilder::kPresetsCtpValHdl));
-
- /* Verify features CCC was written */
- EXPECT_CALL(gatt_queue, WriteDescriptor(1, _, _, _, _, _)).Times(AnyNumber());
- EXPECT_CALL(gatt_queue,
- WriteDescriptor(1, HasDbBuilder::kPresetsCtpValHdl + 1,
- std::vector<uint8_t>{0x03, 0x00}, _, _, _));
- TestConnect(test_address);
-
- /* Verify features */
- ASSERT_EQ(has_features, bluetooth::has::kFeatureBitHearingAidTypeBanded);
-}
-
-TEST_F(HasClientTest, test_active_preset_change) {
- const RawAddress test_address = GetTestAddress(1);
- auto test_conn_id = GetTestConnId(test_address);
-
- SetSampleDatabaseHasNoOptionalNtf(test_address);
-
- uint8_t active_preset_index;
- EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _));
- EXPECT_CALL(*callbacks,
- OnPresetInfo(_, PresetInfoReason::ALL_PRESET_INFO, _));
- EXPECT_CALL(*callbacks, OnActivePresetSelected(_, _))
- .WillOnce(SaveArg<1>(&active_preset_index));
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- TestConnect(test_address);
-
- uint8_t new_active_preset;
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address), _))
- .WillOnce(SaveArg<1>(&new_active_preset));
- InjectNotificationEvent(test_address, test_conn_id,
- HasDbBuilder::kActivePresetIndexValHdl,
- std::vector<uint8_t>({0x00}));
-
- ASSERT_NE(active_preset_index, new_active_preset);
- ASSERT_EQ(new_active_preset, 0x00);
-}
-
-TEST_F(HasClientTest, test_duplicate_presets) {
- const RawAddress test_address = GetTestAddress(1);
- std::vector<PresetInfo> preset_details;
-
- /* Handle duplicates gracefully */
- SetSampleDatabaseHasPresetsNtf(
- test_address, kFeatureBitWritablePresets,
- {{HasPreset(5,
- HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "YourWritablePreset5"),
- HasPreset(5,
- HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "YourWritablePreset5")}});
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- EXPECT_CALL(*callbacks, OnPresetInfo(_, PresetInfoReason::ALL_PRESET_INFO, _))
- .WillOnce(SaveArg<2>(&preset_details));
- TestConnect(test_address);
-
- /* Verify presets - expect 1, no duplicates */
- ASSERT_EQ(preset_details.size(), 1u);
- auto preset = std::find_if(
- preset_details.begin(), preset_details.end(),
- [](auto const& preset_info) { return preset_info.preset_index == 5; });
- ASSERT_TRUE(preset != preset_details.end());
- ASSERT_EQ("YourWritablePreset5", preset->preset_name);
- ASSERT_TRUE(preset->available);
- ASSERT_TRUE(preset->writable);
-}
-
-TEST_F(HasClientTest, test_preset_set_name_invalid_index) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(test_address);
- TestConnect(test_address);
-
- EXPECT_CALL(*callbacks,
- OnSetPresetNameError(std::variant<RawAddress, int>(test_address),
- 0x40, ErrorCode::INVALID_PRESET_INDEX))
- .Times(1);
- EXPECT_CALL(gatt_queue,
- WriteCharacteristic(1, HasDbBuilder::kPresetsCtpValHdl, _,
- GATT_WRITE, _, _))
- .Times(0);
-
- HasClient::Get()->SetPresetName(test_address, 0x40, "new preset name");
-}
-
-TEST_F(HasClientTest, test_preset_set_name_non_writable) {
- const RawAddress test_address = GetTestAddress(1);
- uint16_t test_conn_id = GetTestConnId(test_address);
-
- SetSampleDatabaseHasPresetsNtf(
- test_address, kFeatureBitWritablePresets,
- {{
- HasPreset(5, HasPreset::kPropertyAvailable, "YourPreset5"),
- HasPreset(
- 55, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "YourWritablePreset55"),
- }});
- TestConnect(test_address);
-
- EXPECT_CALL(*callbacks,
- OnSetPresetNameError(_, _, ErrorCode::SET_NAME_NOT_ALLOWED))
- .Times(1);
- EXPECT_CALL(gatt_queue,
- WriteCharacteristic(1, HasDbBuilder::kPresetsCtpValHdl, _,
- GATT_WRITE, _, _))
- .Times(0);
-
- HasClient::Get()->SetPresetName(
- test_address, current_peer_presets_.at(test_conn_id).begin()->GetIndex(),
- "new preset name");
-}
-
-TEST_F(HasClientTest, test_preset_set_name_to_long) {
- const RawAddress test_address = GetTestAddress(1);
- uint16_t test_conn_id = GetTestConnId(test_address);
-
- SetSampleDatabaseHasPresetsNtf(
- test_address, kFeatureBitWritablePresets,
- {{HasPreset(5,
- HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "YourWritablePreset")}});
- TestConnect(test_address);
-
- EXPECT_CALL(*callbacks,
- OnSetPresetNameError(_, _, ErrorCode::INVALID_PRESET_NAME_LENGTH))
- .Times(1);
- EXPECT_CALL(gatt_queue,
- WriteCharacteristic(test_conn_id, HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(0);
-
- HasClient::Get()->SetPresetName(test_address, 5,
- "this name is more than 40 characters long");
-}
-
-TEST_F(HasClientTest, test_preset_set_name) {
- const RawAddress test_address = GetTestAddress(1);
- uint16_t test_conn_id = GetTestConnId(test_address);
-
- SetSampleDatabaseHasPresetsNtf(
- test_address, kFeatureBitWritablePresets,
- {{HasPreset(5,
- HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "YourWritablePreset5")}});
-
- TestConnect(test_address);
-
- std::vector<uint8_t> value;
- EXPECT_CALL(*callbacks, OnSetPresetNameError(_, _, _)).Times(0);
- EXPECT_CALL(gatt_queue,
- WriteCharacteristic(test_conn_id, HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _));
-
- std::vector<PresetInfo> updated_preset_details;
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::PRESET_INFO_UPDATE, _))
- .WillOnce(SaveArg<2>(&updated_preset_details));
- HasClient::Get()->SetPresetName(test_address, 5, "new preset name");
-
- ASSERT_EQ(1u, updated_preset_details.size());
- ASSERT_EQ(updated_preset_details[0].preset_name, "new preset name");
-}
-
-TEST_F(HasClientTest, test_preset_group_set_name) {
- /* None of these devices support preset syncing */
- const RawAddress test_address1 = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(
- test_address1, bluetooth::has::kFeatureBitHearingAidTypeBinaural |
- bluetooth::has::kFeatureBitWritablePresets);
-
- const RawAddress test_address2 = GetTestAddress(2);
- SetSampleDatabaseHasPresetsNtf(
- test_address2, bluetooth::has::kFeatureBitHearingAidTypeBinaural |
- bluetooth::has::kFeatureBitWritablePresets);
-
- TestConnect(test_address1);
- TestConnect(test_address2);
-
- /* Mock the csis group with two devices */
- uint8_t not_synced_group = 13;
- ON_CALL(mock_csis_client_module_, GetDeviceList(not_synced_group))
- .WillByDefault(
- Return(std::vector<RawAddress>({{test_address1, test_address2}})));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address1, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(not_synced_group));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address2, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(not_synced_group));
-
- std::vector<PresetInfo> preset_details;
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address1), 55))
- .Times(0);
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address2), 55))
- .Times(0);
-
- /* This should be a group callback */
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(not_synced_group),
- PresetInfoReason::PRESET_INFO_UPDATE, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&preset_details));
-
- /* No locally synced opcodes support so expect both devices getting writes */
- EXPECT_CALL(gatt_queue, WriteCharacteristic(GetTestConnId(test_address1),
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(1);
- EXPECT_CALL(gatt_queue, WriteCharacteristic(GetTestConnId(test_address2),
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(1);
-
- HasClient::Get()->SetPresetName(not_synced_group, 55, "new preset name");
- ASSERT_EQ(preset_details.size(), 1u);
- ASSERT_EQ(preset_details[0].preset_name, "new preset name");
- ASSERT_EQ(preset_details[0].preset_index, 55);
-}
-
-TEST_F(HasClientTest, test_multiple_presets_get_name) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(
- test_address, kFeatureBitWritablePresets,
- {{
- HasPreset(
- 5, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "YourWritablePreset5"),
- HasPreset(55, HasPreset::kPropertyAvailable, "YourPreset55"),
- HasPreset(99, 0, "YourPreset99"),
- }});
-
- std::vector<PresetInfo> preset_details;
-
- EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _));
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- EXPECT_CALL(*callbacks, OnPresetInfo(_, PresetInfoReason::ALL_PRESET_INFO, _))
- .WillOnce(SaveArg<2>(&preset_details));
- TestConnect(test_address);
-
- /* Get each preset info individually */
- for (auto const& preset : preset_details) {
- std::vector<PresetInfo> new_preset_details;
-
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::PRESET_INFO_REQUEST_RESPONSE, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&new_preset_details));
- HasClient::Get()->GetPresetInfo(test_address, preset.preset_index);
-
- Mock::VerifyAndClearExpectations(&*callbacks);
- ASSERT_EQ(1u, new_preset_details.size());
- ASSERT_EQ(preset.preset_index, new_preset_details[0].preset_index);
- ASSERT_EQ(preset.preset_name, new_preset_details[0].preset_name);
- ASSERT_EQ(preset.writable, new_preset_details[0].writable);
- ASSERT_EQ(preset.available, new_preset_details[0].available);
- }
-}
-
-TEST_F(HasClientTest, test_presets_get_name_invalid_index) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(test_address);
- TestConnect(test_address);
-
- EXPECT_CALL(*callbacks,
- OnPresetInfoError(std::variant<RawAddress, int>(test_address),
- 128, ErrorCode::INVALID_PRESET_INDEX));
- HasClient::Get()->GetPresetInfo(test_address, 128);
-
- EXPECT_CALL(*callbacks,
- OnPresetInfoError(std::variant<RawAddress, int>(test_address), 0,
- ErrorCode::INVALID_PRESET_INDEX));
- HasClient::Get()->GetPresetInfo(test_address, 0);
-}
-
-TEST_F(HasClientTest, test_presets_changed_generic_update_no_add_or_delete) {
- const RawAddress test_address = GetTestAddress(1);
- uint16_t test_conn_id = GetTestConnId(test_address);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> presets = {{
- HasPreset(1, HasPreset::kPropertyAvailable, "Universal"),
- HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "Preset2"),
- HasPreset(4, HasPreset::kPropertyAvailable, "Preset4"),
- HasPreset(7, HasPreset::kPropertyAvailable, "Preset7"),
- }};
- SetSampleDatabaseHasPresetsNtf(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitDynamicPresets |
- bluetooth::has::kFeatureBitWritablePresets,
- presets);
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- TestConnect(test_address);
-
- std::vector<PresetInfo> preset_details;
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::PRESET_INFO_UPDATE, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&preset_details));
-
- /* Inject generic update on the first preset */
- auto preset_index = 2;
- auto new_test_preset = HasPreset(preset_index, 0, "props new name");
- ASSERT_NE(*current_peer_presets_.at(test_conn_id).find(preset_index),
- new_test_preset);
-
- InjectPresetChanged(test_conn_id, test_address, false, new_test_preset,
- 1 /* prev_index */,
- ::le_audio::has::PresetCtpChangeId::PRESET_GENERIC_UPDATE,
- true /* is_last */);
-
- /* Verify received preset info update on the 2nd preset */
- ASSERT_EQ(1u, preset_details.size());
- ASSERT_EQ(new_test_preset.GetIndex(), preset_details[0].preset_index);
- ASSERT_EQ(new_test_preset.IsAvailable(), preset_details[0].available);
- ASSERT_EQ(new_test_preset.IsWritable(), preset_details[0].writable);
- ASSERT_EQ(new_test_preset.GetName(), preset_details[0].preset_name);
-}
-
-TEST_F(HasClientTest, test_presets_changed_generic_update_add_and_delete) {
- const RawAddress test_address = GetTestAddress(1);
- uint16_t test_conn_id = GetTestConnId(test_address);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> presets = {{
- HasPreset(1, HasPreset::kPropertyAvailable, "Universal"),
- HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "Preset2"),
- HasPreset(4, HasPreset::kPropertyAvailable, "Preset4"),
- HasPreset(5, HasPreset::kPropertyAvailable, "Preset5"),
- HasPreset(32, HasPreset::kPropertyAvailable, "Preset32"),
- }};
- SetSampleDatabaseHasPresetsNtf(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets,
- presets);
-
- std::vector<PresetInfo> preset_details;
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::ALL_PRESET_INFO, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&preset_details));
- TestConnect(test_address);
-
- /* Expect more OnPresetInfo call */
- std::vector<PresetInfo> updated_preset_details;
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::PRESET_INFO_UPDATE, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&updated_preset_details));
-
- /* Expect more OnPresetInfo call */
- std::vector<PresetInfo> deleted_preset_details;
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::PRESET_DELETED, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&deleted_preset_details));
-
- /* Inject generic updates */
- /* First event replaces all the existing presets from 1 to 8 with preset 8
- */
- auto new_test_preset1 =
- HasPreset(8, HasPreset::kPropertyAvailable, "props new name9");
- InjectPresetChanged(test_conn_id, test_address, false, new_test_preset1,
- 1 /* prev_index */,
- ::le_audio::has::PresetCtpChangeId::PRESET_GENERIC_UPDATE,
- false /* is_last */);
-
- /* Second event adds preset 9 to the already existing presets 1 and 8 */
- auto new_test_preset2 =
- HasPreset(9, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "props new name11");
- InjectPresetChanged(test_conn_id, test_address, false, new_test_preset2,
- 8 /* prev_index */,
- ::le_audio::has::PresetCtpChangeId::PRESET_GENERIC_UPDATE,
- true /* is_last */);
-
- /* Verify received preset info - expect presets 1, 32 unchanged, 8, 9
- * updated, and 2, 4, 5 deleted.
- */
- ASSERT_EQ(2u, updated_preset_details.size());
- ASSERT_EQ(new_test_preset1.GetIndex(),
- updated_preset_details[0].preset_index);
- ASSERT_EQ(new_test_preset1.IsAvailable(),
- updated_preset_details[0].available);
- ASSERT_EQ(new_test_preset1.IsWritable(), updated_preset_details[0].writable);
- ASSERT_EQ(new_test_preset1.GetName(), updated_preset_details[0].preset_name);
- ASSERT_EQ(new_test_preset2.GetIndex(),
- updated_preset_details[1].preset_index);
- ASSERT_EQ(new_test_preset2.IsAvailable(),
- updated_preset_details[1].available);
- ASSERT_EQ(new_test_preset2.IsWritable(), updated_preset_details[1].writable);
- ASSERT_EQ(new_test_preset2.GetName(), updated_preset_details[1].preset_name);
-
- ASSERT_EQ(3u, deleted_preset_details.size());
- ASSERT_EQ(2, deleted_preset_details[0].preset_index);
- ASSERT_EQ(4, deleted_preset_details[1].preset_index);
- ASSERT_EQ(5, deleted_preset_details[2].preset_index);
-}
-
-TEST_F(HasClientTest, test_presets_changed_deleted) {
- const RawAddress test_address = GetTestAddress(1);
- uint16_t test_conn_id = GetTestConnId(test_address);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> presets = {{
- HasPreset(1, HasPreset::kPropertyAvailable, "Universal"),
- HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "Preset2"),
- }};
- SetSampleDatabaseHasPresetsNtf(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets,
- presets);
-
- std::vector<PresetInfo> preset_details;
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::ALL_PRESET_INFO, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&preset_details));
- TestConnect(test_address);
-
- /* Expect second OnPresetInfo call */
- std::vector<PresetInfo> deleted_preset_details;
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::PRESET_DELETED, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&deleted_preset_details));
-
- /* Inject preset deletion of index 2 */
- auto deleted_index = preset_details[1].preset_index;
- InjectPresetChanged(test_conn_id, test_address, false,
- *presets.find(deleted_index), 0 /* prev_index */,
- ::le_audio::has::PresetCtpChangeId::PRESET_DELETED,
- true /* is_last */);
-
- ASSERT_EQ(2u, preset_details.size());
- ASSERT_EQ(1u, deleted_preset_details.size());
- ASSERT_EQ(preset_details[1].preset_index,
- deleted_preset_details[0].preset_index);
- ASSERT_EQ(preset_details[1].writable, deleted_preset_details[0].writable);
- ASSERT_EQ(preset_details[1].available, deleted_preset_details[0].available);
- ASSERT_EQ(preset_details[1].preset_name,
- deleted_preset_details[0].preset_name);
-}
-
-TEST_F(HasClientTest, test_presets_changed_available) {
- const RawAddress test_address = GetTestAddress(1);
- uint16_t test_conn_id = GetTestConnId(test_address);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> presets = {{
- HasPreset(1, 0, "Universal"),
- HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "Preset2"),
- }};
- SetSampleDatabaseHasPresetsNtf(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets,
- presets);
-
- std::vector<PresetInfo> preset_details;
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::ALL_PRESET_INFO, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&preset_details));
- TestConnect(test_address);
-
- /* Expect second OnPresetInfo call */
- std::vector<PresetInfo> changed_preset_details;
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::PRESET_AVAILABILITY_CHANGED, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&changed_preset_details));
-
- /* Inject preset deletion of index 2 */
- auto changed_index = preset_details[0].preset_index;
- InjectPresetChanged(test_conn_id, test_address, false,
- *presets.find(changed_index), 0 /* prev_index */,
- ::le_audio::has::PresetCtpChangeId::PRESET_AVAILABLE,
- true /* is_last */);
-
- ASSERT_EQ(2u, preset_details.size());
- ASSERT_EQ(1u, changed_preset_details.size());
- ASSERT_EQ(preset_details[0].preset_index,
- changed_preset_details[0].preset_index);
- ASSERT_EQ(preset_details[0].writable, changed_preset_details[0].writable);
- ASSERT_EQ(preset_details[0].preset_name,
- changed_preset_details[0].preset_name);
- /* This field should have changed */
- ASSERT_NE(preset_details[0].available, changed_preset_details[0].available);
- ASSERT_TRUE(changed_preset_details[0].available);
-}
-
-TEST_F(HasClientTest, test_presets_changed_unavailable) {
- const RawAddress test_address = GetTestAddress(1);
- uint16_t test_conn_id = GetTestConnId(test_address);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> presets = {{
- HasPreset(1, HasPreset::kPropertyAvailable, "Universal"),
- HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "Preset2"),
- }};
- SetSampleDatabaseHasPresetsNtf(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets,
- presets);
-
- std::vector<PresetInfo> preset_details;
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::ALL_PRESET_INFO, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&preset_details));
- TestConnect(test_address);
-
- /* Expect second OnPresetInfo call */
- std::vector<PresetInfo> changed_preset_details;
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::PRESET_AVAILABILITY_CHANGED, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&changed_preset_details));
-
- /* Inject preset deletion of index 2 */
- auto changed_index = preset_details[0].preset_index;
- InjectPresetChanged(test_conn_id, test_address, false,
- *presets.find(changed_index), 0 /* prev_index */,
- ::le_audio::has::PresetCtpChangeId::PRESET_UNAVAILABLE,
- true /* is_last */);
-
- ASSERT_EQ(2u, preset_details.size());
- ASSERT_EQ(1u, changed_preset_details.size());
- ASSERT_EQ(preset_details[0].preset_index,
- changed_preset_details[0].preset_index);
- ASSERT_EQ(preset_details[0].writable, changed_preset_details[0].writable);
- ASSERT_EQ(preset_details[0].preset_name,
- changed_preset_details[0].preset_name);
- /* This field should have changed */
- ASSERT_NE(preset_details[0].available, changed_preset_details[0].available);
- ASSERT_FALSE(changed_preset_details[0].available);
-}
-
-TEST_F(HasClientTest, test_select_preset_valid) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(test_address);
-
- uint8_t active_preset_index = 0;
- std::vector<PresetInfo> preset_details;
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::ALL_PRESET_INFO, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&preset_details));
- EXPECT_CALL(*callbacks, OnActivePresetSelected(_, _))
- .WillOnce(SaveArg<1>(&active_preset_index));
- TestConnect(test_address);
-
- ASSERT_TRUE(preset_details.size() > 1);
- ASSERT_EQ(preset_details.front().preset_index, active_preset_index);
-
- uint8_t new_active_preset_index = 0;
- EXPECT_CALL(*callbacks, OnActivePresetSelected(_, _))
- .WillOnce(SaveArg<1>(&new_active_preset_index));
-
- HasClient::Get()->SelectActivePreset(test_address,
- preset_details.back().preset_index);
- Mock::VerifyAndClearExpectations(&*callbacks);
-
- ASSERT_NE(active_preset_index, new_active_preset_index);
- ASSERT_EQ(preset_details.back().preset_index, new_active_preset_index);
-}
-
-TEST_F(HasClientTest, test_select_group_preset_invalid_group) {
- const RawAddress test_address1 = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(test_address1);
-
- const RawAddress test_address2 = GetTestAddress(2);
- SetSampleDatabaseHasPresetsNtf(test_address2);
-
- TestConnect(test_address1);
- TestConnect(test_address2);
-
- /* Mock the csis group with no devices */
- uint8_t unlucky_group = 13;
- ON_CALL(mock_csis_client_module_, GetDeviceList(unlucky_group))
- .WillByDefault(Return(std::vector<RawAddress>()));
-
- EXPECT_CALL(*callbacks, OnActivePresetSelectError(
- std::variant<RawAddress, int>(unlucky_group),
- ErrorCode::OPERATION_NOT_POSSIBLE))
- .Times(1);
-
- HasClient::Get()->SelectActivePreset(unlucky_group, 6);
-}
-
-TEST_F(HasClientTest, test_select_group_preset_valid_no_preset_sync_supported) {
- /* None of these devices support preset syncing */
- const RawAddress test_address1 = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(
- test_address1, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
-
- const RawAddress test_address2 = GetTestAddress(2);
- SetSampleDatabaseHasPresetsNtf(
- test_address2, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
-
- TestConnect(test_address1);
- TestConnect(test_address2);
-
- /* Mock the csis group with two devices */
- uint8_t not_synced_group = 13;
- ON_CALL(mock_csis_client_module_, GetDeviceList(not_synced_group))
- .WillByDefault(
- Return(std::vector<RawAddress>({{test_address1, test_address2}})));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address1, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(not_synced_group));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address2, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(not_synced_group));
-
- uint8_t group_active_preset_index = 0;
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address1), 55))
- .Times(0);
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address2), 55))
- .Times(0);
- EXPECT_CALL(*callbacks,
- OnActivePresetSelected(
- std::variant<RawAddress, int>(not_synced_group), _))
- .WillOnce(SaveArg<1>(&group_active_preset_index));
-
- /* No locally synced opcodes support so expect both devices getting writes */
- EXPECT_CALL(gatt_queue, WriteCharacteristic(GetTestConnId(test_address1),
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(1);
- EXPECT_CALL(gatt_queue, WriteCharacteristic(GetTestConnId(test_address2),
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(1);
-
- HasClient::Get()->SelectActivePreset(not_synced_group, 55);
- ASSERT_EQ(group_active_preset_index, 55);
-}
-
-TEST_F(HasClientTest, test_select_group_preset_valid_preset_sync_supported) {
- /* Only one of these devices support preset syncing */
- const RawAddress test_address1 = GetTestAddress(1);
- uint16_t test_conn_id1 = GetTestConnId(test_address1);
- SetSampleDatabaseHasPresetsNtf(
- test_address1, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
-
- const RawAddress test_address2 = GetTestAddress(2);
- uint16_t test_conn_id2 = GetTestConnId(test_address2);
- SetSampleDatabaseHasPresetsNtf(
- test_address2,
- bluetooth::has::kFeatureBitHearingAidTypeBinaural |
- bluetooth::has::kFeatureBitPresetSynchronizationSupported);
-
- uint8_t active_preset_index1 = 0;
- uint8_t active_preset_index2 = 0;
-
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address1), _))
- .WillOnce(SaveArg<1>(&active_preset_index1));
- TestConnect(test_address1);
-
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address2), _))
- .WillOnce(SaveArg<1>(&active_preset_index2));
- TestConnect(test_address2);
-
- /* Mock the csis group with two devices */
- uint8_t synced_group = 13;
- ON_CALL(mock_csis_client_module_, GetDeviceList(synced_group))
- .WillByDefault(
- Return(std::vector<RawAddress>({{test_address1, test_address2}})));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address1, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(synced_group));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address2, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(synced_group));
-
- EXPECT_CALL(*callbacks, OnActivePresetSelectError(
- _, ErrorCode::GROUP_OPERATION_NOT_SUPPORTED))
- .Times(0);
-
- /* Expect callback from the group but not from the devices */
- uint8_t group_active_preset_index = 0;
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address1), _))
- .Times(0);
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address2), _))
- .Times(0);
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(synced_group), _))
- .WillOnce(SaveArg<1>(&group_active_preset_index));
-
- /* Expect Ctp write on on this device which forwards operation to the other */
- EXPECT_CALL(gatt_queue, WriteCharacteristic(test_conn_id1,
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(0);
- EXPECT_CALL(gatt_queue, WriteCharacteristic(test_conn_id2,
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(1);
-
- HasClient::Get()->SelectActivePreset(synced_group, 55);
- ASSERT_EQ(group_active_preset_index, 55);
-}
-
-TEST_F(HasClientTest, test_select_preset_invalid) {
- const RawAddress test_address = GetTestAddress(1);
- uint16_t test_conn_id = GetTestConnId(test_address);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> presets = {{
- HasPreset(1, HasPreset::kPropertyAvailable, "Universal"),
- HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "Preset2"),
- }};
- SetSampleDatabaseHasPresetsNtf(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets,
- presets);
-
- uint8_t active_preset_index = 0;
- std::vector<PresetInfo> preset_details;
-
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::ALL_PRESET_INFO, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&preset_details));
- EXPECT_CALL(*callbacks, OnActivePresetSelected(_, _))
- .WillOnce(SaveArg<1>(&active_preset_index));
- TestConnect(test_address);
-
- ASSERT_TRUE(preset_details.size() > 1);
- ASSERT_EQ(preset_details.front().preset_index, active_preset_index);
-
- /* Inject preset deletion of index 2 */
- auto deleted_index = preset_details[1].preset_index;
- InjectPresetChanged(test_conn_id, test_address, false,
- *presets.find(deleted_index), 0 /* prev_index */,
- ::le_audio::has::PresetCtpChangeId::PRESET_DELETED,
- true /* is_last */);
-
- EXPECT_CALL(*callbacks, OnActivePresetSelectError(
- std::variant<RawAddress, int>(test_address),
- ErrorCode::INVALID_PRESET_INDEX))
- .Times(1);
-
- /* Check if preset was actually deleted - try setting it as an active one */
- HasClient::Get()->SelectActivePreset(test_address,
- preset_details[1].preset_index);
-}
-
-TEST_F(HasClientTest, test_select_preset_next) {
- const RawAddress test_address = GetTestAddress(1);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> presets = {{
- HasPreset(1, HasPreset::kPropertyAvailable, "Universal"),
- HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "Preset2"),
- }};
- SetSampleDatabaseHasPresetsNtf(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets,
- presets);
-
- uint8_t active_preset_index = 0;
- std::vector<PresetInfo> preset_details;
-
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::ALL_PRESET_INFO, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&preset_details));
- EXPECT_CALL(*callbacks, OnActivePresetSelected(_, _))
- .WillOnce(SaveArg<1>(&active_preset_index));
- TestConnect(test_address);
-
- ASSERT_TRUE(preset_details.size() > 1);
- ASSERT_EQ(1, active_preset_index);
-
- /* Verify active preset change */
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address), 2));
- HasClient::Get()->NextActivePreset(test_address);
-}
-
-TEST_F(HasClientTest, test_select_group_preset_next_no_preset_sync_supported) {
- /* None of these devices support preset syncing */
- const RawAddress test_address1 = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(
- test_address1, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
-
- const RawAddress test_address2 = GetTestAddress(2);
- SetSampleDatabaseHasPresetsNtf(
- test_address2, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
-
- TestConnect(test_address1);
- TestConnect(test_address2);
-
- /* Mock the csis group with two devices */
- uint8_t not_synced_group = 13;
- ON_CALL(mock_csis_client_module_, GetDeviceList(not_synced_group))
- .WillByDefault(
- Return(std::vector<RawAddress>({{test_address1, test_address2}})));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address1, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(not_synced_group));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address2, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(not_synced_group));
-
- uint8_t group_active_preset_index = 0;
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address1), 55))
- .Times(0);
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address2), 55))
- .Times(0);
- EXPECT_CALL(*callbacks,
- OnActivePresetSelected(
- std::variant<RawAddress, int>(not_synced_group), _))
- .WillOnce(SaveArg<1>(&group_active_preset_index));
-
- /* No locally synced opcodes support so expect both devices getting writes */
- EXPECT_CALL(gatt_queue, WriteCharacteristic(GetTestConnId(test_address1),
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(1);
- EXPECT_CALL(gatt_queue, WriteCharacteristic(GetTestConnId(test_address2),
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(1);
-
- HasClient::Get()->NextActivePreset(not_synced_group);
- ASSERT_EQ(group_active_preset_index, 55);
-}
-
-TEST_F(HasClientTest, test_select_group_preset_next_preset_sync_supported) {
- /* Only one of these devices support preset syncing */
- const RawAddress test_address1 = GetTestAddress(1);
- uint16_t test_conn_id1 = GetTestConnId(test_address1);
- SetSampleDatabaseHasPresetsNtf(
- test_address1, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
-
- const RawAddress test_address2 = GetTestAddress(2);
- uint16_t test_conn_id2 = GetTestConnId(test_address2);
- SetSampleDatabaseHasPresetsNtf(
- test_address2,
- bluetooth::has::kFeatureBitHearingAidTypeBinaural |
- bluetooth::has::kFeatureBitPresetSynchronizationSupported);
-
- uint8_t active_preset_index1 = 0;
- uint8_t active_preset_index2 = 0;
-
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address1), _))
- .WillOnce(SaveArg<1>(&active_preset_index1));
- TestConnect(test_address1);
-
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address2), _))
- .WillOnce(SaveArg<1>(&active_preset_index2));
- TestConnect(test_address2);
-
- /* Mock the csis group with two devices */
- uint8_t synced_group = 13;
- ON_CALL(mock_csis_client_module_, GetDeviceList(synced_group))
- .WillByDefault(
- Return(std::vector<RawAddress>({{test_address1, test_address2}})));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address1, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(synced_group));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address2, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(synced_group));
-
- EXPECT_CALL(*callbacks, OnActivePresetSelectError(
- _, ErrorCode::GROUP_OPERATION_NOT_SUPPORTED))
- .Times(0);
-
- /* Expect callback from the group but not from the devices */
- uint8_t group_active_preset_index = 0;
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address1), _))
- .Times(0);
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address2), _))
- .Times(0);
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(synced_group), _))
- .WillOnce(SaveArg<1>(&group_active_preset_index));
-
- /* Expect Ctp write on on this device which forwards operation to the other */
- EXPECT_CALL(gatt_queue, WriteCharacteristic(test_conn_id1,
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(0);
- EXPECT_CALL(gatt_queue, WriteCharacteristic(test_conn_id2,
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(1);
-
- HasClient::Get()->NextActivePreset(synced_group);
- ASSERT_EQ(group_active_preset_index, 55);
-}
-
-TEST_F(HasClientTest, test_select_preset_prev) {
- const RawAddress test_address = GetTestAddress(1);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> presets = {{
- HasPreset(1, HasPreset::kPropertyAvailable, "Universal"),
- HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "Preset2"),
- }};
- SetSampleDatabaseHasPresetsNtf(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets,
- presets);
-
- uint8_t active_preset_index = 0;
- std::vector<PresetInfo> preset_details;
-
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::ALL_PRESET_INFO, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&preset_details));
- ON_CALL(*callbacks, OnActivePresetSelected(_, _))
- .WillByDefault(SaveArg<1>(&active_preset_index));
- TestConnect(test_address);
-
- HasClient::Get()->SelectActivePreset(test_address, 2);
- ASSERT_TRUE(preset_details.size() > 1);
- ASSERT_EQ(2, active_preset_index);
-
- /* Verify active preset change */
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address), 1));
- HasClient::Get()->PreviousActivePreset(test_address);
-}
-
-TEST_F(HasClientTest, test_select_group_preset_prev_no_preset_sync_supported) {
- /* None of these devices support preset syncing */
- const RawAddress test_address1 = GetTestAddress(1);
- SetSampleDatabaseHasPresetsNtf(
- test_address1, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
-
- const RawAddress test_address2 = GetTestAddress(2);
- SetSampleDatabaseHasPresetsNtf(
- test_address2, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
-
- TestConnect(test_address1);
- TestConnect(test_address2);
-
- /* Mock the csis group with two devices */
- uint8_t not_synced_group = 13;
- ON_CALL(mock_csis_client_module_, GetDeviceList(not_synced_group))
- .WillByDefault(
- Return(std::vector<RawAddress>({{test_address1, test_address2}})));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address1, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(not_synced_group));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address2, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(not_synced_group));
-
- uint8_t group_active_preset_index = 0;
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address1), 55))
- .Times(0);
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address2), 55))
- .Times(0);
- EXPECT_CALL(*callbacks,
- OnActivePresetSelected(
- std::variant<RawAddress, int>(not_synced_group), _))
- .WillOnce(SaveArg<1>(&group_active_preset_index));
-
- /* No locally synced opcodes support so expect both devices getting writes */
- EXPECT_CALL(gatt_queue, WriteCharacteristic(GetTestConnId(test_address1),
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(1);
- EXPECT_CALL(gatt_queue, WriteCharacteristic(GetTestConnId(test_address2),
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(1);
-
- HasClient::Get()->PreviousActivePreset(not_synced_group);
- ASSERT_EQ(group_active_preset_index, 55);
-}
-
-TEST_F(HasClientTest, test_select_group_preset_prev_preset_sync_supported) {
- /* Only one of these devices support preset syncing */
- const RawAddress test_address1 = GetTestAddress(1);
- uint16_t test_conn_id1 = GetTestConnId(test_address1);
- SetSampleDatabaseHasPresetsNtf(
- test_address1, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
-
- const RawAddress test_address2 = GetTestAddress(2);
- uint16_t test_conn_id2 = GetTestConnId(test_address2);
- SetSampleDatabaseHasPresetsNtf(
- test_address2,
- bluetooth::has::kFeatureBitHearingAidTypeBinaural |
- bluetooth::has::kFeatureBitPresetSynchronizationSupported);
-
- uint8_t active_preset_index1 = 0;
- uint8_t active_preset_index2 = 0;
-
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address1), _))
- .WillOnce(SaveArg<1>(&active_preset_index1));
- TestConnect(test_address1);
-
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address2), _))
- .WillOnce(SaveArg<1>(&active_preset_index2));
- TestConnect(test_address2);
-
- /* Mock the csis group with two devices */
- uint8_t synced_group = 13;
- ON_CALL(mock_csis_client_module_, GetDeviceList(synced_group))
- .WillByDefault(
- Return(std::vector<RawAddress>({{test_address1, test_address2}})));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address1, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(synced_group));
- ON_CALL(mock_csis_client_module_,
- GetGroupId(test_address2, ::le_audio::uuid::kCapServiceUuid))
- .WillByDefault(Return(synced_group));
-
- EXPECT_CALL(*callbacks, OnActivePresetSelectError(
- _, ErrorCode::GROUP_OPERATION_NOT_SUPPORTED))
- .Times(0);
-
- /* Expect callback from the group but not from the devices */
- uint8_t group_active_preset_index = 0;
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address1), _))
- .Times(0);
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(test_address2), _))
- .Times(0);
- EXPECT_CALL(*callbacks, OnActivePresetSelected(
- std::variant<RawAddress, int>(synced_group), _))
- .WillOnce(SaveArg<1>(&group_active_preset_index));
-
- /* Expect Ctp write on on this device which forwards operation to the other */
- EXPECT_CALL(gatt_queue, WriteCharacteristic(test_conn_id1,
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(0);
- EXPECT_CALL(gatt_queue, WriteCharacteristic(test_conn_id2,
- HasDbBuilder::kPresetsCtpValHdl,
- _, GATT_WRITE, _, _))
- .Times(1);
-
- HasClient::Get()->PreviousActivePreset(synced_group);
- ASSERT_EQ(group_active_preset_index, 55);
-}
-
-TEST_F(HasClientTest, test_select_has_no_presets) {
- const RawAddress test_address = GetTestAddress(1);
- SetSampleDatabaseHasNoPresetsFlagsOnly(test_address);
-
- EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _)).Times(1);
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address))
- .Times(1);
- TestConnect(test_address);
-
- /* Test this not so useful service */
- EXPECT_CALL(*callbacks,
- OnActivePresetSelectError(_, ErrorCode::OPERATION_NOT_SUPPORTED))
- .Times(3);
-
- HasClient::Get()->SelectActivePreset(test_address, 0x01);
- HasClient::Get()->NextActivePreset(test_address);
- HasClient::Get()->PreviousActivePreset(test_address);
-}
-
-static int GetSocketBufferSize(int sockfd) {
- int socket_buffer_size;
- socklen_t optlen = sizeof(socket_buffer_size);
- getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (void*)&socket_buffer_size,
- &optlen);
- return socket_buffer_size;
-}
-
-bool SimpleJsonValidator(int fd, int* dumpsys_byte_cnt) {
- std::ostringstream ss;
-
- char buf{0};
- bool within_double_quotes{false};
- int left_bracket{0}, right_bracket{0};
- int left_sq_bracket{0}, right_sq_bracket{0};
- while (read(fd, &buf, 1) != -1) {
- switch (buf) {
- (*dumpsys_byte_cnt)++;
- case '"':
- within_double_quotes = !within_double_quotes;
- break;
- case '{':
- if (!within_double_quotes) {
- left_bracket++;
- }
- break;
- case '}':
- if (!within_double_quotes) {
- right_bracket++;
- }
- break;
- case '[':
- if (!within_double_quotes) {
- left_sq_bracket++;
- }
- break;
- case ']':
- if (!within_double_quotes) {
- right_sq_bracket++;
- }
- break;
- default:
- break;
- }
- ss << buf;
- }
- LOG(ERROR) << __func__ << ": " << ss.str();
- return (left_bracket == right_bracket) &&
- (left_sq_bracket == right_sq_bracket);
-}
-
-TEST_F(HasClientTest, test_dumpsys) {
- const RawAddress test_address = GetTestAddress(1);
-
- std::set<HasPreset, HasPreset::ComparatorDesc> presets = {{
- HasPreset(1, HasPreset::kPropertyAvailable, "Universal"),
- HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "Preset2"),
- }};
- SetSampleDatabaseHasPresetsNtf(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets,
- presets);
-
- uint8_t active_preset_index = 0;
- std::vector<PresetInfo> preset_details;
-
- EXPECT_CALL(*callbacks,
- OnPresetInfo(std::variant<RawAddress, int>(test_address),
- PresetInfoReason::ALL_PRESET_INFO, _))
- .Times(1)
- .WillOnce(SaveArg<2>(&preset_details));
- ON_CALL(*callbacks, OnActivePresetSelected(_, _))
- .WillByDefault(SaveArg<1>(&active_preset_index));
- TestConnect(test_address);
-
- int sv[2];
- ASSERT_EQ(0, socketpair(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK, 0, sv));
- int socket_buffer_size = GetSocketBufferSize(sv[0]);
-
- HasClient::Get()->DebugDump(sv[0]);
- int dumpsys_byte_cnt = 0;
- ASSERT_TRUE(dumpsys_byte_cnt < socket_buffer_size);
- ASSERT_TRUE(SimpleJsonValidator(sv[1], &dumpsys_byte_cnt));
-}
-
-TEST_F(HasClientTest, test_connect_database_out_of_sync) {
- osi_property_set_bool("persist.bluetooth.has.always_use_preset_cache", false);
-
- const RawAddress test_address = GetTestAddress(1);
- std::set<HasPreset, HasPreset::ComparatorDesc> has_presets = {{
- HasPreset(1, HasPreset::kPropertyAvailable, "Universal"),
- HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "Preset2"),
- }};
- SetSampleDatabaseHasPresetsNtf(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets,
- has_presets);
-
- EXPECT_CALL(*callbacks, OnDeviceAvailable(
- test_address,
- bluetooth::has::kFeatureBitHearingAidTypeBanded |
- bluetooth::has::kFeatureBitWritablePresets |
- bluetooth::has::kFeatureBitDynamicPresets));
- EXPECT_CALL(*callbacks,
- OnConnectionState(ConnectionState::CONNECTED, test_address));
- TestConnect(test_address);
-
- ON_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _))
- .WillByDefault(
- Invoke([this](uint16_t conn_id, uint16_t handle,
- std::vector<uint8_t> value, tGATT_WRITE_TYPE write_type,
- GATT_WRITE_OP_CB cb, void* cb_data) {
- auto* svc = gatt::FindService(services_map[conn_id], handle);
- if (svc == nullptr) return;
-
- tGATT_STATUS status = GATT_DATABASE_OUT_OF_SYNC;
- if (cb)
- cb(conn_id, status, handle, value.size(), value.data(), cb_data);
- }));
-
- ON_CALL(gatt_interface, ServiceSearchRequest(_, _)).WillByDefault(Return());
- EXPECT_CALL(gatt_interface, ServiceSearchRequest(_, _));
- HasClient::Get()->GetPresetInfo(test_address, 1);
-}
-
-class HasTypesTest : public ::testing::Test {
- protected:
- void SetUp(void) override { mock_function_count_map.clear(); }
-
- void TearDown(void) override {}
-}; // namespace
-
-TEST_F(HasTypesTest, test_has_preset_serialize) {
- HasPreset preset(0x01,
- HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "My Writable Preset01");
-
- auto sp_sz = preset.SerializedSize();
- std::vector<uint8_t> serialized(sp_sz);
-
- ASSERT_EQ(1 + // preset index
- 1 + // properties
- 1 + // name length
- preset.GetName().length(),
- sp_sz);
-
- /* Serialize should move the received buffer pointer by the size of data
- */
- ASSERT_EQ(preset.Serialize(serialized.data(), serialized.size()),
- serialized.data() + serialized.size());
-
- /* Deserialize */
- HasPreset clone;
- ASSERT_EQ(HasPreset::Deserialize(serialized.data(), serialized.size(), clone),
- serialized.data() + serialized.size());
-
- /* Verify */
- ASSERT_EQ(preset.GetIndex(), clone.GetIndex());
- ASSERT_EQ(preset.GetProperties(), clone.GetProperties());
- ASSERT_EQ(preset.GetName(), clone.GetName());
-}
-
-TEST_F(HasTypesTest, test_has_preset_serialize_output_buffer_to_small) {
- HasPreset preset(0x01,
- HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "My Writable Preset01");
-
- /* On failure, the offset should still point on .data() */
- std::vector<uint8_t> serialized(preset.SerializedSize() - 1);
- ASSERT_EQ(preset.Serialize(serialized.data(), serialized.size()),
- serialized.data());
- ASSERT_EQ(preset.Serialize(serialized.data(), 0), serialized.data());
- ASSERT_EQ(preset.Serialize(serialized.data(), 1), serialized.data());
- ASSERT_EQ(preset.Serialize(serialized.data(), 10), serialized.data());
-}
-
-TEST_F(HasTypesTest, test_has_preset_serialize_name_to_long) {
- HasPreset preset(0x01,
- HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "This name is more than 40 characters long");
-
- /* On failure, the offset should still point on .data() */
- std::vector<uint8_t> serialized(preset.SerializedSize());
- EXPECT_EQ(preset.Serialize(serialized.data(), serialized.size()),
- serialized.data());
-}
-
-TEST_F(HasTypesTest, test_has_preset_deserialize_input_buffer_to_small) {
- HasPreset preset(0x01,
- HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "My Writable Preset01");
-
- std::vector<uint8_t> serialized(preset.SerializedSize());
-
- /* Serialize should move the received buffer pointer by the size of data
- */
- ASSERT_EQ(preset.Serialize(serialized.data(), serialized.size()),
- serialized.data() + serialized.size());
-
- /* Deserialize */
- HasPreset clone;
- ASSERT_EQ(HasPreset::Deserialize(serialized.data(), 0, clone),
- serialized.data());
- ASSERT_EQ(HasPreset::Deserialize(serialized.data(), 1, clone),
- serialized.data());
- ASSERT_EQ(HasPreset::Deserialize(serialized.data(), 11, clone),
- serialized.data());
- ASSERT_EQ(
- HasPreset::Deserialize(serialized.data(), serialized.size() - 1, clone),
- serialized.data());
-}
-
-TEST_F(HasTypesTest, test_has_presets_serialize) {
- HasPreset preset(0x01,
- HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable,
- "My Writable Preset01");
-
- HasPreset preset2(0x02, 0, "Nonwritable Unavailable Preset");
-
- HasDevice has_device(GetTestAddress(1));
- has_device.has_presets.insert(preset);
- has_device.has_presets.insert(preset2);
-
- auto out_buf_sz = has_device.SerializedPresetsSize();
- ASSERT_EQ(out_buf_sz, preset.SerializedSize() + preset2.SerializedSize() + 2);
-
- /* Serialize should append to the vector */
- std::vector<uint8_t> serialized;
- ASSERT_TRUE(has_device.SerializePresets(serialized));
- ASSERT_EQ(out_buf_sz, serialized.size());
-
- /* Deserialize */
- HasDevice clone(GetTestAddress(1));
- ASSERT_TRUE(HasDevice::DeserializePresets(serialized.data(),
- serialized.size(), clone));
-
- /* Verify */
- ASSERT_EQ(clone.has_presets.size(), has_device.has_presets.size());
- ASSERT_NE(0u, clone.has_presets.count(0x01));
- ASSERT_NE(0u, clone.has_presets.count(0x02));
-
- ASSERT_EQ(clone.has_presets.find(0x01)->GetIndex(),
- has_device.has_presets.find(0x01)->GetIndex());
- ASSERT_EQ(clone.has_presets.find(0x01)->GetProperties(),
- has_device.has_presets.find(0x01)->GetProperties());
- ASSERT_EQ(clone.has_presets.find(0x01)->GetName(),
- has_device.has_presets.find(0x01)->GetName());
-
- ASSERT_EQ(clone.has_presets.find(0x02)->GetIndex(),
- has_device.has_presets.find(0x02)->GetIndex());
- ASSERT_EQ(clone.has_presets.find(0x02)->GetProperties(),
- has_device.has_presets.find(0x02)->GetProperties());
- ASSERT_EQ(clone.has_presets.find(0x02)->GetName(),
- has_device.has_presets.find(0x02)->GetName());
-}
-
-TEST_F(HasTypesTest, test_group_op_coordinator_init) {
- HasCtpGroupOpCoordinator::Initialize([](void*) {
- /* Do nothing */
- });
- ASSERT_EQ(0u, HasCtpGroupOpCoordinator::ref_cnt);
- auto address1 = GetTestAddress(1);
- auto address2 = GetTestAddress(2);
-
- HasCtpGroupOpCoordinator wrapper(
- {address1, address2},
- HasCtpOp(0x01, ::le_audio::has::PresetCtpOpcode::READ_PRESETS, 6));
- ASSERT_EQ(2u, wrapper.ref_cnt);
-
- HasCtpGroupOpCoordinator::Cleanup();
- ASSERT_EQ(0u, wrapper.ref_cnt);
-
- ASSERT_EQ(1, mock_function_count_map["alarm_free"]);
- ASSERT_EQ(1, mock_function_count_map["alarm_new"]);
-}
-
-TEST_F(HasTypesTest, test_group_op_coordinator_copy) {
- HasCtpGroupOpCoordinator::Initialize([](void*) {
- /* Do nothing */
- });
- ASSERT_EQ(0u, HasCtpGroupOpCoordinator::ref_cnt);
- auto address1 = GetTestAddress(1);
- auto address2 = GetTestAddress(2);
-
- HasCtpGroupOpCoordinator wrapper(
- {address1, address2},
- HasCtpOp(0x01, ::le_audio::has::PresetCtpOpcode::READ_PRESETS, 6));
- HasCtpGroupOpCoordinator wrapper2(
- {address1},
- HasCtpOp(0x01, ::le_audio::has::PresetCtpOpcode::READ_PRESETS, 6));
- ASSERT_EQ(3u, wrapper.ref_cnt);
- HasCtpGroupOpCoordinator wrapper3 = wrapper2;
- auto* wrapper4 =
- new HasCtpGroupOpCoordinator(HasCtpGroupOpCoordinator(wrapper2));
- ASSERT_EQ(5u, wrapper.ref_cnt);
-
- delete wrapper4;
- ASSERT_EQ(4u, wrapper.ref_cnt);
-
- HasCtpGroupOpCoordinator::Cleanup();
- ASSERT_EQ(0u, wrapper.ref_cnt);
-
- ASSERT_EQ(1, mock_function_count_map["alarm_free"]);
- ASSERT_EQ(1, mock_function_count_map["alarm_new"]);
-}
-
-TEST_F(HasTypesTest, test_group_op_coordinator_completion) {
- HasCtpGroupOpCoordinator::Initialize([](void*) {
- /* Do nothing */
- LOG(INFO) << __func__ << " callback call";
- });
- ASSERT_EQ(0u, HasCtpGroupOpCoordinator::ref_cnt);
- auto address1 = GetTestAddress(1);
- auto address2 = GetTestAddress(2);
- auto address3 = GetTestAddress(3);
-
- HasCtpGroupOpCoordinator wrapper(
- {address1, address3},
- HasCtpOp(0x01, ::le_audio::has::PresetCtpOpcode::READ_PRESETS, 6));
- HasCtpGroupOpCoordinator wrapper2(
- {address2},
- HasCtpOp(0x01, ::le_audio::has::PresetCtpOpcode::READ_PRESETS, 6));
- ASSERT_EQ(3u, wrapper.ref_cnt);
-
- ASSERT_FALSE(wrapper.IsFullyCompleted());
-
- wrapper.SetCompleted(address1);
- ASSERT_EQ(2u, wrapper.ref_cnt);
-
- wrapper.SetCompleted(address3);
- ASSERT_EQ(1u, wrapper.ref_cnt);
- ASSERT_FALSE(wrapper.IsFullyCompleted());
- ASSERT_EQ(0, mock_function_count_map["alarm_free"]);
-
- /* Non existing address completion */
- wrapper.SetCompleted(address2);
- ASSERT_EQ(0, mock_function_count_map["alarm_free"]);
- ASSERT_EQ(1u, wrapper.ref_cnt);
-
- /* Last device address completion */
- wrapper2.SetCompleted(address2);
- ASSERT_TRUE(wrapper.IsFullyCompleted());
- ASSERT_EQ(0u, wrapper.ref_cnt);
- ASSERT_EQ(1, mock_function_count_map["alarm_free"]);
- mock_function_count_map["alarm_free"] = 0;
-
- HasCtpGroupOpCoordinator::Cleanup();
-
- ASSERT_EQ(0, mock_function_count_map["alarm_free"]);
- ASSERT_EQ(1, mock_function_count_map["alarm_new"]);
-}
-
-} // namespace
-} // namespace internal
-} // namespace has
-} // namespace bluetooth
diff --git a/bta/has/has_ctp.cc b/bta/has/has_ctp.cc
deleted file mode 100644
index 616752a67..000000000
--- a/bta/has/has_ctp.cc
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#include "has_ctp.h"
-//#include "os/log.h"
-
-namespace le_audio {
-namespace has {
-
-static bool ParsePresetGenericUpdate(uint16_t& len, const uint8_t* value,
- HasCtpNtf& ntf) {
- if (len < sizeof(ntf.prev_index) + HasPreset::kCharValueMinSize) {
- LOG(ERROR) << "Invalid preset value length=" << +len
- << " for generic update.";
- return false;
- }
-
- STREAM_TO_UINT8(ntf.index, value);
- len -= 1;
-
- ntf.preset = HasPreset::FromCharacteristicValue(len, value);
- return true;
-}
-
-static bool ParsePresetIndex(uint16_t& len, const uint8_t* value,
- HasCtpNtf& ntf) {
- if (len < sizeof(ntf.index)) {
- LOG(ERROR) << __func__ << "Invalid preset value length=" << +len
- << " for generic update.";
- return false;
- }
-
- STREAM_TO_UINT8(ntf.index, value);
- len -= 1;
- return true;
-}
-
-static bool ParsePresetReadResponse(uint16_t& len, const uint8_t* value,
- HasCtpNtf& ntf) {
- if (len < sizeof(ntf.is_last) + HasPreset::kCharValueMinSize) {
- LOG(ERROR) << "Invalid preset value length=" << +len;
- return false;
- }
-
- STREAM_TO_UINT8(ntf.is_last, value);
- len -= 1;
-
- ntf.preset = HasPreset::FromCharacteristicValue(len, value);
- return true;
-}
-
-static bool ParsePresetChanged(uint16_t len, const uint8_t* value,
- HasCtpNtf& ntf) {
- if (len < sizeof(ntf.is_last) + sizeof(ntf.change_id)) {
- LOG(ERROR) << __func__ << "Invalid preset value length=" << +len;
- return false;
- }
-
- uint8_t change_id;
- STREAM_TO_UINT8(change_id, value);
- len -= 1;
- if (change_id > static_cast<std::underlying_type_t<PresetCtpChangeId>>(
- PresetCtpChangeId::CHANGE_ID_MAX_)) {
- LOG(ERROR) << __func__ << "Invalid preset chenge_id=" << change_id;
- return false;
- }
- ntf.change_id = PresetCtpChangeId(change_id);
- STREAM_TO_UINT8(ntf.is_last, value);
- len -= 1;
-
- switch (ntf.change_id) {
- case PresetCtpChangeId::PRESET_GENERIC_UPDATE:
- return ParsePresetGenericUpdate(len, value, ntf);
- case PresetCtpChangeId::PRESET_AVAILABLE:
- return ParsePresetIndex(len, value, ntf);
- case PresetCtpChangeId::PRESET_UNAVAILABLE:
- return ParsePresetIndex(len, value, ntf);
- case PresetCtpChangeId::PRESET_DELETED:
- return ParsePresetIndex(len, value, ntf);
- default:
- return false;
- }
-
- return true;
-}
-
-std::optional<HasCtpNtf> HasCtpNtf::FromCharacteristicValue(
- uint16_t len, const uint8_t* value) {
- if (len < 3) {
- LOG(ERROR) << __func__ << " Invalid Cp notification.";
- return std::nullopt;
- }
-
- uint8_t op;
- STREAM_TO_UINT8(op, value);
- --len;
-
- if ((op != static_cast<std::underlying_type_t<PresetCtpOpcode>>(
- PresetCtpOpcode::READ_PRESET_RESPONSE)) &&
- (op != static_cast<std::underlying_type_t<PresetCtpOpcode>>(
- PresetCtpOpcode::PRESET_CHANGED))) {
- LOG(ERROR) << __func__
- << ": Received invalid opcode in control point notification: "
- << ++op;
- return std::nullopt;
- }
-
- HasCtpNtf ntf;
- memset(&ntf, 0, sizeof(HasCtpNtf));
- ntf.opcode = PresetCtpOpcode(op);
- if (ntf.opcode == le_audio::has::PresetCtpOpcode::PRESET_CHANGED) {
- if (!ParsePresetChanged(len, value, ntf)) return std::nullopt;
-
- } else if (ntf.opcode ==
- le_audio::has::PresetCtpOpcode::READ_PRESET_RESPONSE) {
- if (!ParsePresetReadResponse(len, value, ntf)) return std::nullopt;
- }
-
- return ntf;
-}
-
-uint16_t HasCtpOp::last_op_id_ = 0;
-
-std::vector<uint8_t> HasCtpOp::ToCharacteristicValue() const {
- std::vector<uint8_t> value;
- auto* pp = value.data();
-
- switch (opcode) {
- case PresetCtpOpcode::READ_PRESETS:
- value.resize(3);
- pp = value.data();
- UINT8_TO_STREAM(
- pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
- UINT8_TO_STREAM(pp, index);
- UINT8_TO_STREAM(pp, num_of_indices);
- break;
- case PresetCtpOpcode::SET_ACTIVE_PRESET:
- case PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC:
- value.resize(2);
- pp = value.data();
- UINT8_TO_STREAM(
- pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
- UINT8_TO_STREAM(pp, index);
- break;
-
- case PresetCtpOpcode::SET_NEXT_PRESET:
- case PresetCtpOpcode::SET_NEXT_PRESET_SYNC:
- case PresetCtpOpcode::SET_PREV_PRESET:
- case PresetCtpOpcode::SET_PREV_PRESET_SYNC:
- value.resize(1);
- pp = value.data();
- UINT8_TO_STREAM(
- pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
- break;
-
- case PresetCtpOpcode::WRITE_PRESET_NAME: {
- auto name_str = name.value_or("");
- value.resize(2 + name_str.length());
- pp = value.data();
-
- UINT8_TO_STREAM(
- pp, static_cast<std::underlying_type_t<PresetCtpOpcode>>(opcode));
- UINT8_TO_STREAM(pp, index);
- memcpy(pp, name_str.c_str(), name_str.length());
- } break;
-
- default:
- LOG_ASSERT(false) << __func__ << "Bad control point operation!";
- break;
- }
-
- return value;
-}
-
-#define CASE_SET_PTR_TO_TOKEN_STR(en) \
- case (en): \
- ch = #en; \
- break;
-
-std::ostream& operator<<(std::ostream& out, const PresetCtpChangeId value) {
- const char* ch = 0;
- switch (value) {
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_GENERIC_UPDATE);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_DELETED);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_AVAILABLE);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpChangeId::PRESET_UNAVAILABLE);
- default:
- ch = "INVALID_CHANGE_ID";
- break;
- }
- return out << ch;
-}
-
-std::ostream& operator<<(std::ostream& out, const PresetCtpOpcode value) {
- const char* ch = 0;
- switch (value) {
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::READ_PRESETS);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::READ_PRESET_RESPONSE);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::PRESET_CHANGED);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::WRITE_PRESET_NAME);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_ACTIVE_PRESET);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_NEXT_PRESET);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_PREV_PRESET);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_NEXT_PRESET_SYNC);
- CASE_SET_PTR_TO_TOKEN_STR(PresetCtpOpcode::SET_PREV_PRESET_SYNC);
- default:
- ch = "NOT_A_VALID_OPCODE";
- break;
- }
- return out << ch;
-}
-#undef SET_CH_TO_TOKENIZED
-
-std::ostream& operator<<(std::ostream& out, const HasCtpOp& op) {
- out << "\"HasCtpOp\": {";
- if (std::holds_alternative<int>(op.addr_or_group)) {
- out << "\"group_id\": " << std::get<int>(op.addr_or_group);
- } else if (std::holds_alternative<RawAddress>(op.addr_or_group)) {
- //out << "\"address\": \""
- //<< ADDRESS_TO_LOGGABLE_STR(std::get<RawAddress>(op.addr_or_group)) << "\"";
- } else {
- out << "\"bad value\"";
- }
- out << ", \"id\": " << op.op_id << ", \"opcode\": \"" << op.opcode << "\""
- << ", \"index\": " << +op.index << ", \"name\": \""
- << op.name.value_or("<none>") << "\""
- << "}";
- return out;
-}
-
-std::ostream& operator<<(std::ostream& out, const HasCtpNtf& ntf) {
- out << "\"HasCtpNtf\": {";
- out << "\"opcode\": \"" << ntf.opcode << "\"";
-
- if (ntf.opcode == PresetCtpOpcode::READ_PRESET_RESPONSE) {
- out << ", \"is_last\": " << (ntf.is_last ? "\"True\"" : "\"False\"");
- if (ntf.preset.has_value()) {
- out << ", \"preset\": " << ntf.preset.value();
- } else {
- out << ", \"preset\": \"None\"";
- }
-
- } else if (ntf.opcode == PresetCtpOpcode::PRESET_CHANGED) {
- out << ", \"change_id\": " << ntf.change_id;
- out << ", \"is_last\": " << (ntf.is_last ? "\"True\"" : "\"False\"");
- switch (ntf.change_id) {
- case PresetCtpChangeId::PRESET_GENERIC_UPDATE:
- out << ", \"prev_index\": " << +ntf.prev_index;
- if (ntf.preset.has_value()) {
- out << ", \"preset\": {" << ntf.preset.value() << "}";
- } else {
- out << ", \"preset\": \"None\"";
- }
- break;
- case PresetCtpChangeId::PRESET_DELETED:
- FALLTHROUGH;
- case PresetCtpChangeId::PRESET_AVAILABLE:
- FALLTHROUGH;
- case PresetCtpChangeId::PRESET_UNAVAILABLE:
- out << ", \"index\": " << +ntf.index;
- break;
- default:
- break;
- }
- }
- out << "}";
-
- return out;
-}
-
-} // namespace has
-} // namespace le_audio
diff --git a/bta/has/has_ctp.h b/bta/has/has_ctp.h
deleted file mode 100644
index 963a23c7b..000000000
--- a/bta/has/has_ctp.h
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#pragma once
-
-#include <list>
-#include <optional>
-
-#include "hardware/bt_has.h"
-#include "has_preset.h"
-#include "osi/include/alarm.h"
-
-namespace le_audio {
-namespace has {
-/* HAS control point Change Id */
-enum class PresetCtpChangeId : uint8_t {
- PRESET_GENERIC_UPDATE = 0,
- PRESET_DELETED,
- PRESET_AVAILABLE,
- PRESET_UNAVAILABLE,
- /* NOTICE: Values below are for internal use only of this particular
- * implementation, and do not correspond to any bluetooth specification.
- */
- CHANGE_ID_MAX_ = PRESET_UNAVAILABLE,
-};
-std::ostream& operator<<(std::ostream& out, const PresetCtpChangeId value);
-
-/* HAS control point Opcodes */
-enum class PresetCtpOpcode : uint8_t {
- READ_PRESETS = 1,
- READ_PRESET_RESPONSE,
- PRESET_CHANGED,
- WRITE_PRESET_NAME,
- SET_ACTIVE_PRESET,
- SET_NEXT_PRESET,
- SET_PREV_PRESET,
- SET_ACTIVE_PRESET_SYNC,
- SET_NEXT_PRESET_SYNC,
- SET_PREV_PRESET_SYNC,
- /* NOTICE: Values below are for internal use only of this particular
- * implementation, and do not correspond to any bluetooth specification.
- */
- OP_MAX_ = SET_PREV_PRESET_SYNC,
- OP_NONE_ = OP_MAX_ + 1,
-};
-std::ostream& operator<<(std::ostream& out, const PresetCtpOpcode value);
-
-static constexpr uint16_t PresetCtpOpcode2Bitmask(PresetCtpOpcode op) {
- return ((uint16_t)0b1 << static_cast<std::underlying_type_t<PresetCtpOpcode>>(
- op));
-}
-
-/* Mandatory opcodes if control point characteristic exists */
-static constexpr uint16_t kControlPointMandatoryOpcodesBitmask =
- PresetCtpOpcode2Bitmask(PresetCtpOpcode::READ_PRESETS) |
- PresetCtpOpcode2Bitmask(PresetCtpOpcode::SET_ACTIVE_PRESET) |
- PresetCtpOpcode2Bitmask(PresetCtpOpcode::SET_NEXT_PRESET) |
- PresetCtpOpcode2Bitmask(PresetCtpOpcode::SET_PREV_PRESET);
-
-/* Optional coordinated operation opcodes */
-static constexpr uint16_t kControlPointSynchronizedOpcodesBitmask =
- PresetCtpOpcode2Bitmask(PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC) |
- PresetCtpOpcode2Bitmask(PresetCtpOpcode::SET_NEXT_PRESET_SYNC) |
- PresetCtpOpcode2Bitmask(PresetCtpOpcode::SET_PREV_PRESET_SYNC);
-
-/* Represents HAS Control Point value notification */
-struct HasCtpNtf {
- PresetCtpOpcode opcode;
- PresetCtpChangeId change_id;
- bool is_last;
- union {
- uint8_t index;
- uint8_t prev_index;
- };
- std::optional<HasPreset> preset;
-
- static std::optional<HasCtpNtf> FromCharacteristicValue(uint16_t len,
- const uint8_t* value);
-};
-std::ostream& operator<<(std::ostream& out, const HasCtpNtf& value);
-
-/* Represents HAS Control Point operation request */
-struct HasCtpOp {
- std::variant<RawAddress, int> addr_or_group;
- PresetCtpOpcode opcode;
- uint8_t index;
- uint8_t num_of_indices;
- std::optional<std::string> name;
- uint16_t op_id;
-
- HasCtpOp(std::variant<RawAddress, int> addr_or_group_id, PresetCtpOpcode op,
- uint8_t index = bluetooth::has::kHasPresetIndexInvalid,
- uint8_t num_of_indices = 1,
- std::optional<std::string> name = std::nullopt)
- : addr_or_group(addr_or_group_id),
- opcode(op),
- index(index),
- num_of_indices(num_of_indices),
- name(name) {
- /* Skip 0 on roll-over */
- last_op_id_ += 1;
- if (last_op_id_ == 0) last_op_id_ = 1;
- op_id = last_op_id_;
- }
-
- std::vector<uint8_t> ToCharacteristicValue(void) const;
-
- bool IsGroupRequest() const {
- return std::holds_alternative<int>(addr_or_group);
- }
-
- int GetGroupId() const {
- return std::holds_alternative<int>(addr_or_group)
- ? std::get<int>(addr_or_group)
- : -1;
- }
-
- RawAddress GetDeviceAddr() const {
- return std::holds_alternative<RawAddress>(addr_or_group)
- ? std::get<RawAddress>(addr_or_group)
- : RawAddress::kEmpty;
- }
-
- bool IsSyncedOperation() const {
- return (opcode == PresetCtpOpcode::SET_ACTIVE_PRESET_SYNC) ||
- (opcode == PresetCtpOpcode::SET_NEXT_PRESET_SYNC) ||
- (opcode == PresetCtpOpcode::SET_PREV_PRESET_SYNC);
- }
-
- private:
- /* It's fine for this to roll-over eventually */
- static uint16_t last_op_id_;
-};
-std::ostream& operator<<(std::ostream& out, const HasCtpOp& value);
-
-/* Used to track group operations. SetCompleted() allows to mark
- * a single device as operation-completed when notification is received.
- * When all the devices are SetComplete'd, timeout timer is being canceled and
- * a group operation can be considered completed (IsFullyCompleted() == true).
- *
- * NOTICE: A single callback and reference counter is being used for all the
- * coordinator instances, therefore creating more instances result
- * in timeout timer being rescheduled. User should remove all the
- * pending op. coordinators in the timer timeout callback.
- */
-struct HasCtpGroupOpCoordinator {
- std::list<RawAddress> devices;
- HasCtpOp operation;
- std::list<bluetooth::has::PresetInfo> preset_info_verification_list;
-
- static size_t ref_cnt;
- static alarm_t* operation_timeout_timer;
- static constexpr uint16_t kOperationTimeoutMs = 10000u;
- static alarm_callback_t cb;
-
- static void Initialize(alarm_callback_t c = nullptr) {
- operation_timeout_timer = nullptr;
- ref_cnt = 0;
- cb = c;
- }
-
- static void Cleanup() {
- if (operation_timeout_timer != nullptr) {
- if (alarm_is_scheduled(operation_timeout_timer)) {
- DLOG(INFO) << __func__ << +ref_cnt;
- alarm_cancel(operation_timeout_timer);
- }
- alarm_free(operation_timeout_timer);
- operation_timeout_timer = nullptr;
- }
-
- ref_cnt = 0;
- }
-
- static bool IsFullyCompleted() { return ref_cnt == 0; }
- static bool IsPending() { return ref_cnt != 0; }
-
- HasCtpGroupOpCoordinator() = delete;
- HasCtpGroupOpCoordinator& operator=(const HasCtpGroupOpCoordinator&) = delete;
- /* NOTICE: It cannot be non-copyable if we want to put it into the std::map.
- * The default copy constructor and copy assignment operator would break the
- * reference counting, so we must increment ref_cnt for all the temporary
- * copies.
- */
- HasCtpGroupOpCoordinator(const HasCtpGroupOpCoordinator& other)
- : devices(other.devices),
- operation(other.operation),
- preset_info_verification_list(other.preset_info_verification_list) {
- ref_cnt += other.devices.size();
- }
-
- HasCtpGroupOpCoordinator(const std::vector<RawAddress>& targets,
- HasCtpOp operation)
- : operation(operation) {
- LOG_ASSERT(targets.size() != 0) << " Empty device list error.";
- if (targets.size() != 1) {
- LOG_ASSERT(operation.IsGroupRequest()) << " Must be a group operation!";
- LOG_ASSERT(operation.GetGroupId() != -1) << " Must set valid group_id!";
- }
-
- devices = std::list<RawAddress>(targets.cbegin(), targets.cend());
-
- ref_cnt += devices.size();
- if (operation_timeout_timer == nullptr) {
- operation_timeout_timer = alarm_new("GroupOpTimer");
- }
-
- if (alarm_is_scheduled(operation_timeout_timer))
- alarm_cancel(operation_timeout_timer);
-
- LOG_ASSERT(cb != nullptr) << " Timeout timer callback not set!";
- alarm_set_on_mloop(operation_timeout_timer, kOperationTimeoutMs, cb,
- nullptr);
- }
-
- ~HasCtpGroupOpCoordinator() {
- /* Check if cleanup wasn't already called */
- if (ref_cnt != 0) {
- ref_cnt -= devices.size();
- if (ref_cnt == 0) {
- Cleanup();
- }
- }
- }
-
- bool SetCompleted(RawAddress addr) {
- auto result = false;
-
- auto it = std::find(devices.begin(), devices.end(), addr);
- if (it != devices.end()) {
- devices.erase(it);
- --ref_cnt;
- result = true;
- }
-
- if (ref_cnt == 0) {
- alarm_cancel(operation_timeout_timer);
- alarm_free(operation_timeout_timer);
- operation_timeout_timer = nullptr;
- }
-
- return result;
- }
-};
-
-} // namespace has
-} // namespace le_audio
diff --git a/bta/has/has_journal.cc b/bta/has/has_journal.cc
deleted file mode 100644
index 8a79375f8..000000000
--- a/bta/has/has_journal.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#include "has_journal.h"
-
-#include "internal_include/bt_trace.h"
-
-namespace le_audio {
-namespace has {
-
-std::ostream& operator<<(std::ostream& os, const HasJournalRecord& r) {
- os << "{";
-
- char eventtime[20] = "";
- char temptime[20] = "";
- struct tm* tstamp = localtime(&r.timestamp.tv_sec);
- if (tstamp != NULL) {
- strftime(temptime, sizeof(temptime), "%H:%M:%S", tstamp);
- }
- snprintf(eventtime, sizeof(eventtime), "%s.%03ld", temptime,
- r.timestamp.tv_nsec / 1000000);
- os << "\"time\": \"" << eventtime << "\", ";
-
- if (r.is_operation) {
- os << std::get<HasCtpOp>(r.event);
- os << ", \"status\": \"" << loghex(r.op_status) << "\"";
-
- } else if (r.is_notification) {
- os << std::get<HasCtpNtf>(r.event) << ", ";
-
- } else if (r.is_active_preset_change) {
- os << "\"Active preset changed\": {\"active_preset_idx\": "
- << +std::get<uint8_t>(r.event) << "}";
-
- } else {
- os << "\"Features changed\": {\"features\": \""
- << loghex(std::get<uint8_t>(r.event)) << "\"}";
- }
-
- os << "}";
- return os;
-}
-} // namespace has
-} // namespace le_audio
diff --git a/bta/has/has_journal.h b/bta/has/has_journal.h
deleted file mode 100644
index 99bdc4986..000000000
--- a/bta/has/has_journal.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#pragma once
-
-#include <sys/time.h>
-#include <time.h>
-
-#include <list>
-#include <variant>
-
-//#include "common/time_util.h"
-#include "has_ctp.h"
-
-/* Journal and journal entry classes used by the state dumping functionality. */
-namespace le_audio {
-namespace has {
-static constexpr uint8_t kHasJournalNumRecords = 20;
-
-struct HasJournalRecord {
- /* Indicates which value the `event` contains (due to ambiguous uint8_t) */
- bool is_operation : 1, is_notification : 1, is_features_change : 1,
- is_active_preset_change : 1;
- std::variant<HasCtpOp, HasCtpNtf, uint8_t> event;
- struct timespec timestamp;
-
- /* Operation context handle to match on GATT write response */
- void* op_context_handle;
-
- /* Status of the operation to be set once it gets completed */
- uint8_t op_status;
-
- HasJournalRecord(const HasCtpOp& op, void* context)
- : event(op), op_context_handle(context) {
- clock_gettime(CLOCK_REALTIME, &timestamp);
- is_operation = true;
- is_notification = false;
- is_features_change = false;
- is_active_preset_change = false;
- }
-
- HasJournalRecord(const HasCtpNtf& ntf) : event(ntf) {
- clock_gettime(CLOCK_REALTIME, &timestamp);
- is_operation = false;
- is_notification = true;
- is_features_change = false;
- is_active_preset_change = false;
- }
-
- HasJournalRecord(uint8_t value, bool is_feat_change) : event(value) {
- clock_gettime(CLOCK_REALTIME, &timestamp);
- is_operation = false;
- is_notification = false;
- if (is_feat_change) {
- is_active_preset_change = false;
- is_features_change = true;
- } else {
- is_active_preset_change = true;
- is_features_change = false;
- }
- }
-};
-std::ostream& operator<<(std::ostream& os, const HasJournalRecord& r);
-
-template <class valT, size_t cache_max>
-class CacheList {
- public:
- valT& Append(valT data) {
- items_.push_front(std::move(data));
-
- if (items_.size() > cache_max) {
- items_.pop_back();
- }
-
- return items_.front();
- }
-
- using iterator = typename std::list<valT>::iterator;
- iterator begin(void) { return items_.begin(); }
- iterator end(void) { return items_.end(); }
-
- using const_iterator = typename std::list<valT>::const_iterator;
- const_iterator begin(void) const { return items_.begin(); }
- const_iterator end(void) const { return items_.end(); }
-
- void Erase(iterator it) {
- if (it != items_.end()) items_.erase(it);
- }
-
- void Clear(void) { items_.clear(); }
- bool isEmpty(void) { return items_.empty(); }
-
- private:
- typename std::list<valT> items_;
-};
-
-using HasJournal = CacheList<HasJournalRecord, kHasJournalNumRecords>;
-} // namespace has
-} // namespace le_audio
diff --git a/bta/has/has_preset.cc b/bta/has/has_preset.cc
deleted file mode 100644
index b54831150..000000000
--- a/bta/has/has_preset.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#include "has_preset.h"
-
-namespace le_audio {
-namespace has {
-
-std::optional<HasPreset> HasPreset::FromCharacteristicValue(
- uint16_t& len, const uint8_t* value) {
- if ((len < kCharValueMinSize) ||
- (len > kCharValueMinSize + kPresetNameLengthLimit)) {
- LOG(ERROR) << __func__ << " Preset record to long: " << len;
- return std::nullopt;
- }
-
- HasPreset preset;
- STREAM_TO_UINT8(preset.index_, value);
- --len;
- STREAM_TO_UINT8(preset.properties_, value);
- --len;
- preset.name_ = std::string(value, value + len);
-
- return preset;
-}
-
-void HasPreset::ToCharacteristicValue(std::vector<uint8_t>& value) const {
- auto initial_offset = value.size();
-
- value.resize(value.size() + kCharValueMinSize + name_.size());
- auto pp = value.data() + initial_offset;
-
- UINT8_TO_STREAM(pp, index_);
- UINT8_TO_STREAM(pp, properties_);
- ARRAY_TO_STREAM(pp, name_.c_str(), (int)name_.size());
-}
-
-uint8_t* HasPreset::Serialize(uint8_t* p_out, size_t buffer_size) const {
- if (buffer_size < SerializedSize()) {
- LOG(ERROR) << "Invalid output buffer size!";
- return p_out;
- }
-
- uint8_t name_len = name_.length();
- if (name_len > kPresetNameLengthLimit) {
- LOG(ERROR) << __func__
- << " Invalid preset name length. Cannot be serialized!";
- return p_out;
- }
-
- /* Serialized data length */
- UINT8_TO_STREAM(p_out, name_len + 2);
-
- UINT8_TO_STREAM(p_out, index_);
- UINT8_TO_STREAM(p_out, properties_);
- ARRAY_TO_STREAM(p_out, name_.c_str(), (int)name_.size());
- return p_out;
-}
-
-const uint8_t* HasPreset::Deserialize(const uint8_t* p_in, size_t len,
- HasPreset& preset) {
- const uint8_t nonamed_size = HasPreset(0, 0).SerializedSize();
- auto* p_curr = p_in;
-
- if (len < nonamed_size) {
- LOG(ERROR) << "Invalid buffer size " << +len << ". Cannot deserialize.";
- return p_in;
- }
-
- uint8_t serialized_data_len;
- STREAM_TO_UINT8(serialized_data_len, p_curr);
- if (serialized_data_len < 2) {
- LOG(ERROR) << __func__ << " Invalid data size. Cannot be deserialized!";
- return p_in;
- }
-
- auto name_len = serialized_data_len - 2;
- if ((name_len > kPresetNameLengthLimit) ||
- ((size_t)nonamed_size + name_len > len)) {
- LOG(ERROR) << __func__
- << " Invalid preset name length. Cannot be deserialized!";
- return p_in;
- }
-
- STREAM_TO_UINT8(preset.index_, p_curr);
- STREAM_TO_UINT8(preset.properties_, p_curr);
- if (name_len) preset.name_ = std::string((const char*)p_curr, name_len);
-
- return p_curr + name_len;
-}
-
-std::ostream& operator<<(std::ostream& os, const HasPreset& b) {
- os << "{\"index\": " << +b.GetIndex();
- os << ", \"name\": \"" << b.GetName() << "\"";
- os << ", \"is_available\": " << (b.IsAvailable() ? "\"True\"" : "\"False\"");
- os << ", \"is_writable\": " << (b.IsWritable() ? "\"True\"" : "\"False\"");
- os << "}";
- return os;
-}
-
-} // namespace has
-} // namespace le_audio
diff --git a/bta/has/has_preset.h b/bta/has/has_preset.h
deleted file mode 100644
index fae011ade..000000000
--- a/bta/has/has_preset.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#pragma once
-
-#include <optional>
-#include <string>
-
-#include "bt_types.h"
-#include "hardware/bt_has.h"
-
-namespace le_audio {
-namespace has {
-/* Represents preset instance. It stores properties such as preset name,
- * preset index and if it supports renaming. Also stores all the needed
- * GATT characteristics and descriptor informations.
- */
-class HasPreset {
- private:
- mutable std::string name_;
- mutable uint8_t properties_;
- uint8_t index_;
-
- public:
- static constexpr size_t kCharValueMinSize = 1 /*index*/ + 1 /*properties*/;
-
- static constexpr uint8_t kPropertyWritable = 0x01;
- static constexpr uint8_t kPropertyAvailable = 0x02;
-
- static constexpr uint8_t kPresetNameLengthLimit = 40;
-
- HasPreset(uint8_t index, uint8_t props = 0,
- std::optional<std::string> name = std::nullopt)
- : properties_(props), index_(index) {
- name_ = name.value_or("");
- }
- HasPreset()
- : name_(""),
- properties_(0),
- index_(bluetooth::has::kHasPresetIndexInvalid) {}
-
- auto& GetName() const { return name_; }
- decltype(index_) GetIndex() const { return index_; }
- decltype(properties_) GetProperties() const { return properties_; }
- bool IsWritable() const { return properties_ & kPropertyWritable; }
- bool IsAvailable() const { return properties_ & kPropertyAvailable; }
-
- HasPreset& operator=(const HasPreset& other) {
- LOG_ASSERT(index_ == other.GetIndex())
- << "Assigning immutable preset index!";
-
- if ((this != &other) && (*this != other)) {
- index_ = other.GetIndex();
- name_ = other.GetName();
- }
- return *this;
- }
-
- bool operator==(const HasPreset& b) const {
- return (index_ == b.index_) && (properties_ == b.properties_) &&
- (name_ == b.name_);
- }
- bool operator!=(const HasPreset& b) const {
- return (index_ != b.index_) || (properties_ != b.properties_) ||
- (name_ != b.name_);
- }
- bool operator<(const HasPreset& b) const { return index_ < b.index_; }
- friend std::ostream& operator<<(std::ostream& os, const HasPreset& b);
-
- struct ComparatorDesc {
- using is_transparent = void;
- bool operator()(HasPreset const& a, int index) const {
- return a.index_ < index;
- }
- bool operator()(int index, HasPreset const& a) const {
- return index < a.index_;
- }
- bool operator()(HasPreset const& a, HasPreset const& b) const {
- return a.index_ < b.index_;
- }
- };
-
- static std::optional<HasPreset> FromCharacteristicValue(uint16_t& len,
- const uint8_t* value);
- void ToCharacteristicValue(std::vector<uint8_t>& value) const;
-
- /* Calculates buffer space that the preset will use when serialized */
- uint8_t SerializedSize() const {
- return (sizeof(index_) + sizeof(properties_) + 1 /* name length */
- + name_.length());
- }
- /* Serializes into binary blob for the persistent storage */
- uint8_t* Serialize(uint8_t* p_out, size_t buffer_size) const;
- /* Deserializes binary blob read from the persistent storage */
- static const uint8_t* Deserialize(const uint8_t* p_in, size_t len,
- HasPreset& preset);
-};
-
-} // namespace has
-} // namespace le_audio
diff --git a/bta/has/has_types.cc b/bta/has/has_types.cc
deleted file mode 100644
index 6d886687f..000000000
--- a/bta/has/has_types.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#include "has_types.h"
-
-namespace le_audio {
-namespace has {
-
-std::ostream& operator<<(std::ostream& os, const HasDevice& b) {
- os << "HAP device: {"
- << "addr: " << b.addr << ", conn id: " << b.conn_id << "}";
- return os;
-}
-
-} // namespace has
-} // namespace le_audio
diff --git a/bta/has/has_types.h b/bta/has/has_types.h
deleted file mode 100644
index 66d0dd3fb..000000000
--- a/bta/has/has_types.h
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#pragma once
-
-#include <numeric>
-#include <optional>
-#include <set>
-#include <vector>
-
-#include "bta_gatt_api.h"
-#include "gap_api.h"
-#include "hardware/bt_has.h"
-#include "has_ctp.h"
-#include "has_journal.h"
-#include "has_preset.h"
-
-namespace le_audio {
-namespace has {
-
-/* Helper class to pass some minimal context through the GATT operation API. */
-union HasGattOpContext {
- public:
- void* ptr = nullptr;
- struct {
- /* Ctp. Operation ID or 0 if not a control point operation context */
- uint16_t ctp_op_id;
-
- /* Additional user flags */
- uint8_t context_flags;
- };
-
- /* Flags describing operation context */
- static constexpr uint8_t kContextFlagsEnableNotification = 0x01;
- static constexpr uint8_t kIsNotNull = 0x02;
-
- static constexpr uint8_t kStatusCodeNotSet = 0xF0;
-
- HasGattOpContext(const HasCtpOp& ctp_op, uint8_t flags = 0) {
- ctp_op_id = ctp_op.op_id;
- /* Differ from nullptr in at least 1 bit when everything else is 0 */
- context_flags = flags | kIsNotNull;
- }
- HasGattOpContext(uint8_t flags) : ctp_op_id(0) {
- context_flags = flags | kIsNotNull;
- }
- HasGattOpContext(void* pp) {
- ptr = pp;
- /* Differ from nullptr in at least 1 bit when everything else is 0 */
- context_flags |= kIsNotNull;
- }
- operator void*() { return ptr; }
-};
-
-/* Context must be constrained to void* size to pass through the GATT API */
-static_assert(sizeof(HasGattOpContext) <= sizeof(void*));
-
-/* Service UUIDs */
-static const bluetooth::Uuid kUuidHearingAccessService =
- bluetooth::Uuid::From16Bit(0x1854);
-static const bluetooth::Uuid kUuidHearingAidFeatures =
- bluetooth::Uuid::From16Bit(0x2BDA);
-static const bluetooth::Uuid kUuidHearingAidPresetControlPoint =
- bluetooth::Uuid::From16Bit(0x2BDB);
-static const bluetooth::Uuid kUuidActivePresetIndex =
- bluetooth::Uuid::From16Bit(0x2BDC);
-
-static const uint8_t kStartPresetIndex = 1;
-static const uint8_t kMaxNumOfPresets = 255;
-
-/* Base device class for the GATT-based service clients */
-class GattServiceDevice {
- public:
- RawAddress addr;
- uint16_t conn_id = GATT_INVALID_CONN_ID;
- uint16_t service_handle = GAP_INVALID_HANDLE;
- bool is_connecting_actively = false;
-
- uint8_t gatt_svc_validation_steps = 0xFE;
- bool isGattServiceValid() { return gatt_svc_validation_steps == 0; }
-
- GattServiceDevice(const RawAddress& addr, bool connecting_actively = false)
- : addr(addr), is_connecting_actively(connecting_actively) {}
-
- GattServiceDevice() : GattServiceDevice(RawAddress::kEmpty) {}
-
- bool IsConnected() const { return conn_id != GATT_INVALID_CONN_ID; }
-
- class MatchAddress {
- private:
- RawAddress addr;
-
- public:
- MatchAddress(RawAddress addr) : addr(addr) {}
- bool operator()(const GattServiceDevice& other) const {
- return (addr == other.addr);
- }
- };
-
- class MatchConnId {
- private:
- uint16_t conn_id;
-
- public:
- MatchConnId(uint16_t conn_id) : conn_id(conn_id) {}
- bool operator()(const GattServiceDevice& other) const {
- return (conn_id == other.conn_id);
- }
- };
-
- void Dump(std::ostream& os) const {
- os << "\"addr\": \"" << addr << "\"";
- os << ", \"conn_id\": " << conn_id;
- os << ", \"is_gatt_service_valid\": "
- << (gatt_svc_validation_steps == 0 ? "\"True\"" : "\"False\"") << "("
- << +gatt_svc_validation_steps << ")";
- os << ", \"is_connecting_actively\": "
- << (is_connecting_actively ? "\"True\"" : "\"False\"");
- }
-};
-
-/* Build on top of the base GattServiceDevice extends the base device context
- * with service specific informations such as the currently active preset,
- * all available presets, and supported optional operations. It also stores
- * HAS service specific GATT informations such as characteristic handles.
- */
-class HasDevice : public GattServiceDevice {
- uint8_t features = 0x00;
- uint16_t supported_opcodes_bitmask = 0x0000;
-
- void RefreshSupportedOpcodesBitmask(void) {
- supported_opcodes_bitmask = 0;
-
- /* Some opcodes are mandatory but the characteristics aren't - these are
- * conditional then.
- */
- if ((cp_handle != GAP_INVALID_HANDLE) &&
- (active_preset_handle != GAP_INVALID_HANDLE)) {
- supported_opcodes_bitmask |= kControlPointMandatoryOpcodesBitmask;
- }
-
- if (features & bluetooth::has::kFeatureBitPresetSynchronizationSupported) {
- supported_opcodes_bitmask |= kControlPointMandatoryOpcodesBitmask;
- supported_opcodes_bitmask |= kControlPointSynchronizedOpcodesBitmask;
- }
-
- if (features & bluetooth::has::kFeatureBitWritablePresets) {
- supported_opcodes_bitmask |=
- PresetCtpOpcode2Bitmask(PresetCtpOpcode::WRITE_PRESET_NAME);
- }
- }
-
- public:
- /* Char handle and current ccc value */
- uint16_t active_preset_handle = GAP_INVALID_HANDLE;
- uint16_t active_preset_ccc_handle = GAP_INVALID_HANDLE;
- uint16_t cp_handle = GAP_INVALID_HANDLE;
- uint16_t cp_ccc_handle = GAP_INVALID_HANDLE;
- uint8_t cp_ccc_val = 0;
- uint16_t features_handle = GAP_INVALID_HANDLE;
- uint16_t features_ccc_handle = GAP_INVALID_HANDLE;
-
- bool features_notifications_enabled = false;
-
- /* Presets in the ascending order of their indices */
- std::set<HasPreset, HasPreset::ComparatorDesc> has_presets;
- uint8_t currently_active_preset = bluetooth::has::kHasPresetIndexInvalid;
-
- std::list<HasCtpNtf> ctp_notifications_;
- HasJournal has_journal_;
-
- HasDevice(const RawAddress& addr, uint8_t features)
- : GattServiceDevice(addr) {
- UpdateFeatures(features);
- }
-
- void ConnectionCleanUp() {
- conn_id = GATT_INVALID_CONN_ID;
- is_connecting_actively = false;
- ctp_notifications_.clear();
- }
-
- using GattServiceDevice::GattServiceDevice;
-
- uint8_t GetFeatures() const { return features; }
-
- void UpdateFeatures(uint8_t new_features) {
- features = new_features;
- /* Update the dependent supported feature set */
- RefreshSupportedOpcodesBitmask();
- }
-
- void ClearSvcData() {
- GattServiceDevice::service_handle = GAP_INVALID_HANDLE;
- GattServiceDevice::gatt_svc_validation_steps = 0xFE;
-
- active_preset_handle = GAP_INVALID_HANDLE;
- active_preset_ccc_handle = GAP_INVALID_HANDLE;
- cp_handle = GAP_INVALID_HANDLE;
- cp_ccc_handle = GAP_INVALID_HANDLE;
- features_handle = GAP_INVALID_HANDLE;
- features_ccc_handle = GAP_INVALID_HANDLE;
-
- features = 0;
- features_notifications_enabled = false;
-
- supported_opcodes_bitmask = 0x00;
- currently_active_preset = bluetooth::has::kHasPresetIndexInvalid;
-
- has_presets.clear();
- }
-
- inline bool SupportsPresets() const {
- return (active_preset_handle != GAP_INVALID_HANDLE) &&
- (cp_handle != GAP_INVALID_HANDLE);
- }
-
- inline bool SupportsActivePresetNotification() const {
- return active_preset_ccc_handle != GAP_INVALID_HANDLE;
- }
-
- inline bool SupportsFeaturesNotification() const {
- return features_ccc_handle != GAP_INVALID_HANDLE;
- }
-
- inline bool HasFeaturesNotificationEnabled() const {
- return features_notifications_enabled;
- }
-
- inline bool SupportsOperation(PresetCtpOpcode op) {
- auto mask = PresetCtpOpcode2Bitmask(op);
- return (supported_opcodes_bitmask & mask) == mask;
- }
-
- bool IsValidPreset(uint8_t preset_index, bool writable_only = false) const {
- if (has_presets.count(preset_index)) {
- return writable_only ? has_presets.find(preset_index)->IsWritable()
- : true;
- }
- return false;
- }
-
- const HasPreset* GetPreset(uint8_t preset_index,
- bool writable_only = false) const {
- if (has_presets.count(preset_index)) {
- decltype(has_presets)::iterator preset = has_presets.find(preset_index);
- if (writable_only) return preset->IsWritable() ? &*preset : nullptr;
- return &*preset;
- }
- return nullptr;
- }
-
- std::optional<bluetooth::has::PresetInfo> GetPresetInfo(uint8_t index) const {
- if (has_presets.count(index)) {
- auto preset = *has_presets.find(index);
- return bluetooth::has::PresetInfo({.preset_index = preset.GetIndex(),
- .writable = preset.IsWritable(),
- .available = preset.IsAvailable(),
- .preset_name = preset.GetName()});
- }
- return std::nullopt;
- }
-
- std::vector<bluetooth::has::PresetInfo> GetAllPresetInfo() const {
- std::vector<bluetooth::has::PresetInfo> all_info;
- all_info.reserve(has_presets.size());
-
- for (auto const& preset : has_presets) {
- DLOG(INFO) << __func__ << " preset: " << preset;
- all_info.push_back({.preset_index = preset.GetIndex(),
- .writable = preset.IsWritable(),
- .available = preset.IsAvailable(),
- .preset_name = preset.GetName()});
- }
- return all_info;
- }
-
- /* Calculates the buffer space that all the preset will use when serialized */
- uint8_t SerializedPresetsSize() const {
- /* Two additional bytes are for the header and the number of presets */
- return std::accumulate(has_presets.begin(), has_presets.end(), 0,
- [](uint8_t current, auto const& preset) {
- return current + preset.SerializedSize();
- }) +
- 2;
- }
-
- /* Serializes all the presets into a binary blob for persistent storage */
- bool SerializePresets(std::vector<uint8_t>& out) const {
- auto buffer_size = SerializedPresetsSize();
- auto buffer_offset = out.size();
-
- out.resize(out.size() + buffer_size);
- auto p_out = out.data() + buffer_offset;
-
- UINT8_TO_STREAM(p_out, kHasDeviceBinaryBlobHdr);
- UINT8_TO_STREAM(p_out, has_presets.size());
-
- auto* const p_end = p_out + buffer_size;
- for (auto& preset : has_presets) {
- if (p_out + preset.SerializedSize() >= p_end) {
- LOG(ERROR) << "Serialization error.";
- return false;
- }
- p_out = preset.Serialize(p_out, p_end - p_out);
- }
-
- return true;
- }
-
- /* Deserializes all the presets from a binary blob read from the persistent
- * storage.
- */
- static bool DeserializePresets(const uint8_t* p_in, size_t len,
- HasDevice& device) {
- HasPreset preset;
- if (len < 2 + preset.SerializedSize()) {
- LOG(ERROR) << "Deserialization error. Invalid input buffer size length.";
- return false;
- }
- auto* p_end = p_in + len;
-
- uint8_t hdr;
- STREAM_TO_UINT8(hdr, p_in);
- if (hdr != kHasDeviceBinaryBlobHdr) {
- LOG(ERROR) << __func__ << " Deserialization error. Bad header.";
- return false;
- }
-
- uint8_t num_presets;
- STREAM_TO_UINT8(num_presets, p_in);
-
- device.has_presets.clear();
- while (p_in < p_end) {
- auto* p_new = HasPreset::Deserialize(p_in, p_end - p_in, preset);
- if (p_new <= p_in) {
- LOG(ERROR) << "Deserialization error. Invalid preset found.";
- device.has_presets.clear();
- return false;
- }
-
- device.has_presets.insert(preset);
- p_in = p_new;
- }
-
- return device.has_presets.size() == num_presets;
- }
-
- friend std::ostream& operator<<(std::ostream& os, const HasDevice& b);
-
- void Dump(std::ostream& os) const {
- GattServiceDevice::Dump(os);
- os << ", \"features\": \"" << loghex(features) << "\"";
- os << ", \"features_notifications_enabled\": "
- << (features_notifications_enabled ? "\"Enabled\"" : "\"Disabled\"");
- os << ", \"ctp_notifications size\": " << ctp_notifications_.size();
- os << ",\n";
-
- os << " "
- << "\"presets\": [";
- for (auto const& preset : has_presets) {
- os << "\n " << preset << ",";
- }
- os << "\n ],\n";
-
- os << " "
- << "\"Ctp. notifications process queue\": {";
- if (ctp_notifications_.size() != 0) {
- size_t ntf_pos = 0;
- for (auto const& ntf : ctp_notifications_) {
- os << "\n ";
- if (ntf_pos == 0) {
- os << "\"latest\": ";
- } else {
- os << "\"-" << ntf_pos << "\": ";
- }
-
- os << ntf << ",";
- ++ntf_pos;
- }
- }
- os << "\n },\n";
-
- os << " "
- << "\"event history\": {";
- size_t pos = 0;
- for (auto const& record : has_journal_) {
- os << "\n ";
- if (pos == 0) {
- os << "\"latest\": ";
- } else {
- os << "\"-" << pos << "\": ";
- }
-
- os << record << ",";
- ++pos;
- }
- os << "\n }";
- }
-
- private:
- static constexpr int kHasDeviceBinaryBlobHdr = 0x55;
-};
-
-} // namespace has
-} // namespace le_audio
diff --git a/bta/include/bta_has_api.h b/bta/include/bta_has_api.h
deleted file mode 100644
index ae9cc5255..000000000
--- a/bta/include/bta_has_api.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#pragma once
-
-#include <base/callback.h>
-
-#include <string>
-
-#include "hardware/bt_has.h"
-
-namespace le_audio {
-namespace has {
-class HasClient {
- public:
- virtual ~HasClient() = default;
-
- static void Initialize(bluetooth::has::HasClientCallbacks* callbacks,
- base::Closure initCb);
- static void CleanUp();
- static HasClient* Get();
- static void DebugDump(int fd);
- static bool IsHasClientRunning();
- static void AddFromStorage(const RawAddress& addr, uint8_t features,
- uint16_t is_acceptlisted);
- virtual void Connect(const RawAddress& addr) = 0;
- virtual void Disconnect(const RawAddress& addr) = 0;
- virtual void SelectActivePreset(
- std::variant<RawAddress, int> addr_or_group_id, uint8_t preset_index) = 0;
- virtual void NextActivePreset(
- std::variant<RawAddress, int> addr_or_group_id) = 0;
- virtual void PreviousActivePreset(
- std::variant<RawAddress, int> addr_or_group_id) = 0;
- virtual void GetPresetInfo(const RawAddress& addr, uint8_t preset_index) = 0;
- virtual void SetPresetName(std::variant<RawAddress, int> addr_or_group_id,
- uint8_t preset_index, std::string name) = 0;
-};
-
-} // namespace has
-} // namespace le_audio
diff --git a/btif/Android.bp b/btif/Android.bp
index bf86d2902..b28016b99 100644
--- a/btif/Android.bp
+++ b/btif/Android.bp
@@ -109,7 +109,6 @@ cc_library_static {
"src/btif_gatt_test.cc",
"src/btif_gatt_util.cc",
"src/btif_hearing_aid.cc",
- "src/btif_has_client.cc",
"src/btif_hf.cc",
"src/btif_hf_client.cc",
"src/btif_hh.cc",
@@ -163,6 +162,7 @@ cc_library_static {
"libcrypto",
],
whole_static_libs: [
+ "libPlatformProperties",
"libbt-audio-hal-interface-qti",
"libbt-common-qti",
//"libbtif_ext",
diff --git a/btif/include/btif_ahim.h b/btif/include/btif_ahim.h
index 8ecc3516c..9ba613fa1 100644
--- a/btif/include/btif_ahim.h
+++ b/btif/include/btif_ahim.h
@@ -28,8 +28,7 @@
*
******************************************************************************/
/*
- * Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
@@ -193,8 +192,6 @@ void btif_ahim_update_current_profile(uint8_t profile);
void btif_ahim_update_src_metadata(const source_metadata_t& source_metadata);
void btif_ahim_update_sink_metadata(const sink_metadata_t& sink_metadata);
-void btif_ahim_set_latency_mode(bool is_low_latency);
-
bool btif_ahim_init_hal(thread_t *t, uint8_t profile);
void btif_ahim_cleanup_hal(uint8_t profile);
diff --git a/btif/include/btif_storage.h b/btif/include/btif_storage.h
index 667711dbc..0608ce603 100644
--- a/btif/include/btif_storage.h
+++ b/btif/include/btif_storage.h
@@ -294,17 +294,6 @@ bool btif_storage_get_hearing_aid_prop(
/** Store service changed CCCD value for remote client */
void btif_storage_set_svc_chg_cccd(const RawAddress& bd_addr, uint8_t cccd);
-/** Loads information about bonded HAS devices */
-void btif_storage_load_bonded_leaudio_has_devices(void);
-
-/** Deletes the bonded HAS device info from NVRAM */
-void btif_storage_remove_leaudio_has(const RawAddress& address);
-
-/** Set/Unset the HAS device acceptlist flag. */
-void btif_storage_set_leaudio_has_acceptlist(const RawAddress& address,
- bool add_to_acceptlist);
-
-
/** Get service changed CCCD value for remote client */
uint8_t btif_storage_get_svc_chg_cccd(const RawAddress& bda);
diff --git a/btif/src/bluetooth.cc b/btif/src/bluetooth.cc
index b93e15b3f..65b69eb0b 100644
--- a/btif/src/bluetooth.cc
+++ b/btif/src/bluetooth.cc
@@ -46,7 +46,6 @@
#include <hardware/bt_hd.h>
#include <hardware/bt_hf.h>
#include <hardware/bt_hearing_aid.h>
-#include <hardware/bt_has.h>
#include <hardware/bt_hf_client.h>
#ifdef DIR_FINDING_FEATURE
#include <hardware/bt_atp_locator.h>
@@ -67,7 +66,6 @@
#include <hardware/bt_vendor_rc.h>
#include "bt_utils.h"
#include "bta_sys.h"
-#include "bta/include/bta_has_api.h"
#include "bta/include/bta_hearing_aid_api.h"
#include "bta/include/bta_hf_client_api.h"
#include "btif/include/btif_debug_btsnoop.h"
@@ -100,7 +98,6 @@
using base::Bind;
using bluetooth::hearing_aid::HearingAidInterface;
-using bluetooth::has::HasClientInterface;
#ifdef DIR_FINDING_FEATURE
using bluetooth::atp_locator::AtpLocatorInterface;
#endif
@@ -147,9 +144,6 @@ extern btsdp_interface_t* btif_sdp_get_interface();
/*Hearing Aid client*/
extern HearingAidInterface* btif_hearing_aid_get_interface();
-/* Hearing Access client */
-extern HasClientInterface* btif_has_client_get_interface();
-
/* List all test interface here */
/* vendor */
extern btvendor_interface_t *btif_vendor_get_interface();
@@ -438,7 +432,6 @@ static void dump(int fd, const char** arguments) {
osi_allocator_debug_dump(fd);
alarm_debug_dump(fd);
HearingAid::DebugDump(fd);
- le_audio::has::HasClient::DebugDump(fd);
connection_manager::dump(fd);
bluetooth::bqr::DebugDump(fd);
#if (BTSNOOP_MEM == TRUE)
@@ -519,9 +512,6 @@ static const void* get_profile_interface(const char* profile_id) {
if (is_profile(profile_id, BT_PROFILE_HEARING_AID_ID))
return btif_hearing_aid_get_interface();
- if (is_profile(profile_id, BT_PROFILE_HAP_CLIENT_ID))
- return btif_has_client_get_interface();
-
#ifdef DIR_FINDING_FEATURE
if (is_profile(profile_id, BT_PROFILE_ATP_LOCATOR_ID))
return btif_atp_locator_get_interface();
diff --git a/btif/src/btif_a2dp_source.cc b/btif/src/btif_a2dp_source.cc
index df89c6295..6f2bd4224 100644
--- a/btif/src/btif_a2dp_source.cc
+++ b/btif/src/btif_a2dp_source.cc
@@ -72,7 +72,9 @@ using ::bluetooth::audio::a2dp::SessionType;
#include "btif_hf.h"
#include "btif_av.h"
#include "bta_sys.h"
+#ifdef ADV_AUDIO_FEATURE
#include "btif_acm.h"
+#endif
using system_bt_osi::BluetoothMetricsLogger;
using system_bt_osi::A2dpSessionMetrics;
@@ -181,8 +183,11 @@ extern int btif_av_get_tws_pair_idx(int index);
extern void btif_av_clear_pending_start_flag();
extern bool btif_av_is_tws_suspend_triggered(int index);
+#ifdef ADV_AUDIO_FEATURE
extern bool btif_acm_check_in_call_tracker_timer_exist();
extern void stop_stream_acm_initiator_now();
+#endif
+
extern bool btif_av_check_is_reconfig_pending_flag_set(RawAddress address);
extern bool bt_split_a2dp_sink_enabled;
@@ -1849,6 +1854,7 @@ void btif_a2dp_source_process_request(tA2DP_CTRL_CMD cmd) {
status = A2DP_CTRL_ACK_INCALL_FAILURE;
break;
}
+#ifdef ADV_AUDIO_FEATURE
if (btif_ahim_is_aosp_aidl_hal_enabled() &&
(btif_acm_check_in_call_tracker_timer_exist() ||
(!btif_acm_get_is_inCall() && btif_acm_is_call_active()))) {
@@ -1856,6 +1862,7 @@ void btif_a2dp_source_process_request(tA2DP_CTRL_CMD cmd) {
status = A2DP_CTRL_ACK_INCALL_FAILURE;
break;
}
+#endif
if (btif_ba_is_active()) {
ba_send_message(BTIF_BA_AUDIO_START_REQ_EVT, 0, NULL, false);
status = A2DP_CTRL_ACK_PENDING;
diff --git a/btif/src/btif_ahim.cc b/btif/src/btif_ahim.cc
index 9c5e0cbfd..de201bba7 100644
--- a/btif/src/btif_ahim.cc
+++ b/btif/src/btif_ahim.cc
@@ -28,8 +28,7 @@
*
******************************************************************************/
/*
- * Changes from Qualcomm Innovation Center are provided under the following license:
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the
@@ -74,10 +73,11 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
#include "audio_hal_interface/aidl/a2dp_encoding.h"
#include "audio_hal_interface/aidl/le_audio_software.h"
#include <hardware/audio.h>
-#include <hardware/bt_apm.h>
#include <vector>
+#ifdef ADV_AUDIO_FEATURE
#include <hardware/bt_pacs_client.h>
#include <btif_vmcp.h>
+#endif
#include <aidl/vendor/qti/hardware/bluetooth/audio/LeAudioVendorConfiguration.h>
#include <aidl/vendor/qti/hardware/bluetooth/audio/VendorCodecType.h>
@@ -102,7 +102,9 @@ using VendorConfiguration =
::aidl::android::hardware::bluetooth::audio::LeAudioCodecConfiguration::VendorConfiguration;
using ::aidl::android::hardware::bluetooth::audio::UnicastCapability;
using vendor::qti::hardware::bluetooth_audio::V2_1::LC3ChannelMode;
+#ifdef ADV_AUDIO_FEATURE
using bluetooth::bap::pacs::CodecIndex;
+#endif
//using ::bluetooth::audio::BitsPerSample;
LeAudioClientInterface* leAudioClientInterface = nullptr;
LeAudioClientInterface::Sink* unicastSinkClientInterface = nullptr;
@@ -196,10 +198,6 @@ void btif_ahim_update_current_profile(uint8_t profile)
}
void btif_ahim_process_request(tA2DP_CTRL_CMD cmd, uint8_t profile,
uint8_t direction) {
- if (btif_check_dual_mode()) {
- btif_ahim_process_request_DM(cmd, profile, direction);
- return;
- }
std::lock_guard<std::mutex> lock(active_profile_mtx);
if (btif_ahim_is_aosp_aidl_hal_enabled()) {
cur_active_profile = profile;
@@ -236,60 +234,6 @@ void btif_ahim_process_request(tA2DP_CTRL_CMD cmd, uint8_t profile,
}
}
-void btif_ahim_process_request_DM(tA2DP_CTRL_CMD cmd, uint8_t profile,
- uint8_t direction) {
- RawAddress peer_bda;
- std::lock_guard<std::mutex> lock(active_profile_mtx);
- switch(profile) {
- case A2DP:
- btif_av_get_active_peer_addr(&peer_bda);
- LOG(INFO) << __func__ << ": Sending AIDL request to AV, "
- << "Peer BDA: " << peer_bda << ", isEmpty: " << peer_bda.IsEmpty();
- if (!peer_bda.IsEmpty()) {
- cur_active_profile = profile;
- }
- btif_dispatch_sm_event(BTIF_AV_PROCESS_HIDL_REQ_EVT,
- (char*)&cmd, sizeof(cmd));
- break;
- case AUDIO_GROUP_MGR:
- btif_av_get_active_peer_addr(&peer_bda);
- LOG(INFO) << __func__ << ": Sending AIDL request to Audio Group Manager, "
- << "Peer BDA: " << peer_bda << ", isEmpty: " << peer_bda.IsEmpty();
- if (peer_bda.IsEmpty()) {
- cur_active_profile = profile;
- }
- if (pclient_cbs[AUDIO_GROUP_MGR - 1] &&
- pclient_cbs[AUDIO_GROUP_MGR - 1]->client_cb) {
- LOG(INFO) << __func__ << ": Calling call back for Audio Group Manager";
- pclient_cbs[AUDIO_GROUP_MGR - 1]->client_cb(cmd, direction);
- }
- else
- BTIF_TRACE_ERROR("%s, Audio Group Manager is not registered with AHIM",
- __func__);
- break;
- case BROADCAST:
- btif_av_get_active_peer_addr(&peer_bda);
- LOG(INFO) << __func__ << ": Sending AIDL request to BROADCAST, "
- << "Peer BDA: " << peer_bda;
- if (peer_bda.IsEmpty()) {
- cur_active_profile = profile;
- }
- if (pclient_cbs[BROADCAST - 1] &&
- pclient_cbs[BROADCAST - 1]->client_cb) {
- BTIF_TRACE_IMP("%s: calling call back for BROADCAST", __func__);
- pclient_cbs[BROADCAST - 1]->client_cb(cmd, direction);
- }
- else
- BTIF_TRACE_ERROR("%s, BROADCAST is not registered with AHIM", __func__);
- break;
- }
-}
-
-void btif_ahim_set_latency_mode(bool is_low_latency) {
- LOG(INFO) << __func__ << ", is_low_latency: " << is_low_latency;
- btif_apm_set_latency_mode(is_low_latency);
-}
-
void btif_ahim_update_src_metadata (const source_metadata_t& source_metadata) {
auto track_count = source_metadata.track_count;
auto usage = source_metadata.tracks->usage;
@@ -298,32 +242,17 @@ void btif_ahim_update_src_metadata (const source_metadata_t& source_metadata) {
<< ", usage: " << usage;
// pass on the callbacks to ACM only for new vendor
- if (btif_ahim_is_aosp_aidl_hal_enabled()) {
- LOG(INFO) << __func__ << ": Current Active Profile: " << loghex(cur_active_profile);
- if(cur_active_profile == A2DP) {
- if (btif_check_dual_mode()) {
- std::unique_lock<std::mutex> guard(src_metadata_wait_mutex_);
- src_metadata_wait = false;
- btif_report_a2dp_src_metadata_update((source_metadata_t *)&source_metadata);
- src_metadata_wait_cv.wait_for(guard, std::chrono::milliseconds(3200),
- []{return src_metadata_wait;});
- LOG(INFO) << __func__ << ": A2DP Src waiting completed!!";
- } else {
- btif_report_a2dp_src_metadata_update((source_metadata_t *)&source_metadata);
- }
- } else if(cur_active_profile == AUDIO_GROUP_MGR ||
- cur_active_profile == BROADCAST) {
- LOG(INFO) << __func__ << ": Sending AIDL request to Audio Group Manager";
- if (pclient_cbs[AUDIO_GROUP_MGR - 1] &&
- pclient_cbs[AUDIO_GROUP_MGR - 1]->src_meta_update) {
- LOG(INFO) << __func__ << ": Calling call back for Audio Group Manager";
- std::unique_lock<std::mutex> guard(src_metadata_wait_mutex_);
- src_metadata_wait = false;
- pclient_cbs[AUDIO_GROUP_MGR - 1]->src_meta_update(source_metadata);
- src_metadata_wait_cv.wait_for(guard, std::chrono::milliseconds(3200),
- []{return src_metadata_wait;});
- LOG(INFO) << __func__ << ": Src waiting completed";
- }
+ if(btif_ahim_is_aosp_aidl_hal_enabled()) {
+ BTIF_TRACE_IMP("%s: sending AIDL request to Audio Group Manager", __func__);
+ if (pclient_cbs[AUDIO_GROUP_MGR - 1] &&
+ pclient_cbs[AUDIO_GROUP_MGR - 1]->src_meta_update) {
+ BTIF_TRACE_IMP("%s: calling call back for Audio Group Manager", __func__);
+ std::unique_lock<std::mutex> guard(src_metadata_wait_mutex_);
+ src_metadata_wait = false;
+ pclient_cbs[AUDIO_GROUP_MGR - 1]->src_meta_update(source_metadata);
+ src_metadata_wait_cv.wait_for(guard, std::chrono::milliseconds(3200),
+ []{return src_metadata_wait;});
+ BTIF_TRACE_IMP("%s: src waiting completed", __func__);
}
}
}
@@ -367,7 +296,6 @@ bool btif_ahim_init_hal(thread_t *t, uint8_t profile) {
std::lock_guard<std::mutex>lock(session_mtx);
BTIF_TRACE_IMP("%s: AIDL", __func__);
if (profile == A2DP) {
- if (!btif_check_dual_mode()) {
if (unicastSinkClientInterface != nullptr) {
leAudioClientInterface->ReleaseSink(unicastSinkClientInterface);
unicastSinkClientInterface = nullptr;
@@ -380,7 +308,6 @@ bool btif_ahim_init_hal(thread_t *t, uint8_t profile) {
leAudioClientInterface->ReleaseSink(broadcastSinkClientInterface);
broadcastSinkClientInterface = nullptr;
}
- }
return bluetooth::audio::aidl::a2dp::init(t);
} else {
if(leAudioClientInterface == nullptr) {
@@ -613,6 +540,8 @@ LeAudioConfiguration fetch_offload_audio_config(int profile, int direction) {
uint16_t type = pclient_cbs[profile - 1]->get_profile_status_cb();
uint16_t frame_duration = pclient_cbs[profile - 1]->get_frame_length_cb(direction);
+
+#ifdef ADV_AUDIO_FEATURE
bool is_lc3q_supported = false;
CodecIndex codec_type = (CodecIndex) pclient_cbs[profile - 1]->get_codec_type(direction);
if (codec_type == CodecIndex::CODEC_INDEX_SOURCE_APTX_ADAPTIVE_LE ||
@@ -622,7 +551,6 @@ LeAudioConfiguration fetch_offload_audio_config(int profile, int direction) {
<< frame_duration << ", from leaudio_configs.xml";
}
uint8_t encoder_version = 0;
- if(1) {
/*if (codec_type == CodecIndex::CODEC_INDEX_SOURCE_APTX_ADAPTIVE_LE ||
pclient_cbs[profile - 1]->get_is_codec_type_lc3q(direction)) {*/
VendorConfiguration vendor_config;
@@ -740,7 +668,7 @@ LeAudioConfiguration fetch_offload_audio_config(int profile, int direction) {
}
}
return ucast_config;
- } else {
+#else /* ADV_AUDIO_FEATURE */
// TODO to fill the right PD
Lc3Configuration lc3_config = {
.pcmBitDepth = 24,
@@ -776,7 +704,7 @@ LeAudioConfiguration fetch_offload_audio_config(int profile, int direction) {
});
}
return ucast_config;
- }
+#endif /* ADV_AUDIO_FEATURE */
}
bool leAudio_get_selected_hal_codec_config(AudioConfigurationAIDL *lea_config,
@@ -813,6 +741,7 @@ bool btif_ahim_setup_codec(uint8_t profile) {
uint16_t profile_type = btif_ahim_get_lea_active_profile(profile);
BTIF_TRACE_IMP("%s: AIDL, profile_type: %d", __func__, profile_type);
if(profile_type == BAP || profile_type == GCP) { // ToAIr only
+#ifdef ADV_AUDIO_FEATURE
CodecIndex codec_type = (CodecIndex) pclient_cbs[profile - 1]->get_codec_type(TX_ONLY_CONFIG);
if (codec_type == CodecIndex::CODEC_INDEX_SOURCE_APTX_ADAPTIVE_R4) {
if (!leAudio_get_selected_hal_codec_config(&lea_tx_config, profile,
@@ -831,6 +760,7 @@ bool btif_ahim_setup_codec(uint8_t profile) {
if(unicastSourceClientInterface)
unicastSourceClientInterface->UpdateAudioConfigToHal(lea_rx_config);
} else {
+#endif
if (!leAudio_get_selected_hal_codec_config(&lea_tx_config, profile,
TX_ONLY_CONFIG)) {
LOG(ERROR) << __func__ << ": Failed to get CodecConfiguration";
@@ -842,7 +772,9 @@ bool btif_ahim_setup_codec(uint8_t profile) {
// TODO to fill both session/single session configs based on profile
if(unicastSinkClientInterface)
unicastSinkClientInterface->UpdateAudioConfigToHal(lea_tx_config);
+#ifdef ADV_AUDIO_FEATURE
}
+#endif
} else if(profile_type == BAP_CALL ||
profile_type == GCP_RX ||
profile_type == WMCP_TX) { // Toair and FromAir
@@ -900,6 +832,7 @@ void btif_ahim_start_session(uint8_t profile) {
btif_ahim_get_lea_active_profile(profile);
BTIF_TRACE_IMP("%s: AIDL, profile_type: %d", __func__, profile_type);
if(profile_type == BAP || profile_type == GCP) { // ToAIr only
+#ifdef ADV_AUDIO_FEATURE
CodecIndex codec_type = (CodecIndex) pclient_cbs[profile - 1]->get_codec_type(TX_ONLY_CONFIG);
if (codec_type == CodecIndex::CODEC_INDEX_SOURCE_APTX_ADAPTIVE_R4) {
if(unicastSinkClientInterface)
@@ -907,9 +840,12 @@ void btif_ahim_start_session(uint8_t profile) {
if(unicastSourceClientInterface)
unicastSourceClientInterface->StartSession();
} else {
+#endif
if(unicastSinkClientInterface)
unicastSinkClientInterface->StartSession();
+#ifdef ADV_AUDIO_FEATURE
}
+#endif
} else if(profile_type == BAP_CALL ||
profile_type == GCP_RX ||
profile_type == WMCP_TX) { // Toair and FromAir
@@ -1312,6 +1248,7 @@ size_t btif_ahim_read(uint8_t* p_buf, uint32_t len) {
return bluetooth::audio::aidl::a2dp::read(p_buf, len);
}
+#ifdef ADV_AUDIO_FEATURE
void btif_ahim_update_audio_config() {
AudioConfigurationAIDL lea_tx_config, lea_rx_config;
uint16_t profile_type = btif_ahim_get_lea_active_profile(AUDIO_GROUP_MGR);
@@ -1380,6 +1317,7 @@ if(unicastSinkClientInterface)
else
return 0xFFFF;
}
+#endif
void btif_ahim_set_remote_delay(uint16_t delay_report, uint8_t profile) {
BTIF_TRACE_IMP("%s: AIDL, profile: %d, delay_report: %d",
diff --git a/btif/src/btif_dm.cc b/btif/src/btif_dm.cc
index 9eaf52471..87c56f9f3 100644
--- a/btif/src/btif_dm.cc
+++ b/btif/src/btif_dm.cc
@@ -74,6 +74,10 @@
#include "btif_dm.h"
+#ifdef OS_ANDROID
+#include <android/sysprop/BluetoothProperties.sysprop.h>
+#endif
+
#include <base/bind.h>
#include <base/logging.h>
#include <signal.h>
@@ -170,6 +174,9 @@ const Uuid UUID_HEARING_AID = Uuid::FromString("FDF0");
#define NUM_TIMEOUT_RETRIES 2
+#ifndef PROPERTY_DEFAULT_DEVICE_NAME
+#define PROPERTY_DEFAULT_DEVICE_NAME "bluetooth.device.default_name"
+#endif
#define PROPERTY_PRODUCT_MODEL "ro.product.model"
#define DEFAULT_LOCAL_NAME_MAX 31
#if (DEFAULT_LOCAL_NAME_MAX > BTM_MAX_LOC_BD_NAME_LEN)
@@ -4483,9 +4490,23 @@ void btif_dm_read_energy_info() { BTA_DmBleGetEnergyInfo(bta_energy_info_cb); }
static char* btif_get_default_local_name() {
if (btif_default_local_name[0] == '\0') {
int max_len = sizeof(btif_default_local_name) - 1;
- if (BTM_DEF_LOCAL_NAME[0] != '\0') {
- strncpy(btif_default_local_name, BTM_DEF_LOCAL_NAME, max_len);
- } else {
+
+ // Use the stable sysprop on Android devices, otherwise use osi_property_get
+#ifdef OS_ANDROID
+ std::optional<std::string> default_local_name =
+ android::sysprop::BluetoothProperties::getDefaultDeviceName();
+ if (default_local_name.has_value() && default_local_name.value() != "") {
+ strncpy(btif_default_local_name, default_local_name.value().c_str(),
+ max_len);
+ }
+#else
+ char prop_name[PROPERTY_VALUE_MAX];
+ osi_property_get(PROPERTY_DEFAULT_DEVICE_NAME, prop_name, "");
+ strncpy(btif_default_local_name, prop_name, max_len);
+#endif
+
+ // If no value was placed in the btif_default_local_name then use model name
+ if (btif_default_local_name[0] == '\0') {
char prop_model[PROPERTY_VALUE_MAX];
osi_property_get(PROPERTY_PRODUCT_MODEL, prop_model, "");
strncpy(btif_default_local_name, prop_model, max_len);
diff --git a/btif/src/btif_has_client.cc b/btif/src/btif_has_client.cc
deleted file mode 100644
index bbbe172ca..000000000
--- a/btif/src/btif_has_client.cc
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#include <base/bind.h>
-#include <base/bind_helpers.h>
-#include <base/location.h>
-#include <base/logging.h>
-#include <hardware/bluetooth.h>
-#include <hardware/bt_has.h>
-#include "bta_closure_api.h"
-
-#include <bitset>
-#include <string>
-#include <vector>
-
-#include "bta_has_api.h"
-#include "btif_common.h"
-#include "btif_storage.h"
-#include "stack/include/btu.h"
-
-using base::Bind;
-using base::Owned;
-using base::Passed;
-using base::Unretained;
-using bluetooth::has::ConnectionState;
-using bluetooth::has::ErrorCode;
-using bluetooth::has::HasClientCallbacks;
-using bluetooth::has::HasClientInterface;
-using bluetooth::has::PresetInfo;
-using bluetooth::has::PresetInfoReason;
-
-using le_audio::has::HasClient;
-
-namespace {
-std::unique_ptr<HasClientInterface> has_client_instance;
-
-class HearingAaccessClientServiceInterfaceImpl : public HasClientInterface,
- public HasClientCallbacks {
- ~HearingAaccessClientServiceInterfaceImpl() override = default;
-
- void Init(HasClientCallbacks* callbacks) override {
- DVLOG(2) << __func__;
- this->callbacks_ = callbacks;
-
- do_in_bta_thread(
- FROM_HERE,
- Bind(&HasClient::Initialize, this,
- jni_thread_wrapper(
- FROM_HERE,
- Bind(&btif_storage_load_bonded_leaudio_has_devices))));
- }
-
- void Connect(const RawAddress& addr) override {
- DVLOG(2) << __func__ << " addr: " << addr;
- do_in_bta_thread(FROM_HERE, Bind(&HasClient::Connect,
- Unretained(HasClient::Get()), addr));
-
- do_in_jni_thread(
- FROM_HERE, Bind(&btif_storage_set_leaudio_has_acceptlist, addr, true));
- }
-
- void Disconnect(const RawAddress& addr) override {
- DVLOG(2) << __func__ << " addr: " << addr;
- do_in_bta_thread(FROM_HERE, Bind(&HasClient::Disconnect,
- Unretained(HasClient::Get()), addr));
-
- do_in_jni_thread(
- FROM_HERE, Bind(&btif_storage_set_leaudio_has_acceptlist, addr, false));
- }
-
- void SelectActivePreset(std::variant<RawAddress, int> addr_or_group_id,
- uint8_t preset_index) override {
- DVLOG(2) << __func__ << " preset_index: " << preset_index;
-
- do_in_bta_thread(
- FROM_HERE,
- Bind(&HasClient::SelectActivePreset, Unretained(HasClient::Get()),
- std::move(addr_or_group_id), preset_index));
- }
-
- void NextActivePreset(
- std::variant<RawAddress, int> addr_or_group_id) override {
- DVLOG(2) << __func__;
-
- do_in_bta_thread(FROM_HERE, Bind(&HasClient::NextActivePreset,
- Unretained(HasClient::Get()),
- std::move(addr_or_group_id)));
- }
-
- void PreviousActivePreset(
- std::variant<RawAddress, int> addr_or_group_id) override {
- DVLOG(2) << __func__;
-
- do_in_bta_thread(FROM_HERE, Bind(&HasClient::PreviousActivePreset,
- Unretained(HasClient::Get()),
- std::move(addr_or_group_id)));
- }
-
- void GetPresetInfo(const RawAddress& addr, uint8_t preset_index) override {
- DVLOG(2) << __func__ << " addr: " << addr
- << " preset_index: " << preset_index;
-
- do_in_bta_thread(
- FROM_HERE, Bind(&HasClient::GetPresetInfo, Unretained(HasClient::Get()),
- addr, preset_index));
- }
-
- void SetPresetName(std::variant<RawAddress, int> addr_or_group_id,
- uint8_t preset_index, std::string preset_name) override {
- DVLOG(2) << __func__ << " preset_index: " << preset_index
- << " preset_name: " << preset_name;
-
- do_in_bta_thread(
- FROM_HERE, Bind(&HasClient::SetPresetName, Unretained(HasClient::Get()),
- std::move(addr_or_group_id), preset_index,
- std::move(preset_name)));
- }
-
- void RemoveDevice(const RawAddress& addr) override {
- DVLOG(2) << __func__ << " addr: " << addr;
-
- /* RemoveDevice can be called on devices that don't have BAS enabled */
- if (HasClient::IsHasClientRunning()) {
- do_in_bta_thread(FROM_HERE, Bind(&HasClient::Disconnect,
- Unretained(HasClient::Get()), addr));
- }
-
- do_in_jni_thread(FROM_HERE, Bind(&btif_storage_remove_leaudio_has, addr));
- }
-
- void Cleanup(void) override {
- DVLOG(2) << __func__;
- do_in_bta_thread(FROM_HERE, Bind(&HasClient::CleanUp));
- }
-
- void OnConnectionState(ConnectionState state,
- const RawAddress& addr) override {
- DVLOG(2) << __func__ << " addr: " << addr;
- do_in_jni_thread(FROM_HERE, Bind(&HasClientCallbacks::OnConnectionState,
- Unretained(callbacks_), state, addr));
- }
-
- void OnDeviceAvailable(const RawAddress& addr, uint8_t features) override {
- DVLOG(2) << __func__ << " addr: " << addr << " features: " << features;
-
- do_in_jni_thread(FROM_HERE, Bind(&HasClientCallbacks::OnDeviceAvailable,
- Unretained(callbacks_), addr, features));
- }
-
- void OnFeaturesUpdate(const RawAddress& addr, uint8_t features) override {
- DVLOG(2) << __func__ << " addr: " << addr
- << " ha_features: " << std::bitset<8>(features);
-
- do_in_jni_thread(FROM_HERE, Bind(&HasClientCallbacks::OnFeaturesUpdate,
- Unretained(callbacks_), addr, features));
- }
-
- void OnActivePresetSelected(std::variant<RawAddress, int> addr_or_group_id,
- uint8_t preset_index) override {
- DVLOG(2) << __func__ << " preset_index: " << preset_index;
-
- do_in_jni_thread(FROM_HERE,
- Bind(&HasClientCallbacks::OnActivePresetSelected,
- Unretained(callbacks_), std::move(addr_or_group_id),
- preset_index));
- }
-
- void OnActivePresetSelectError(std::variant<RawAddress, int> addr_or_group_id,
- ErrorCode result_code) override {
- DVLOG(2) << __func__ << " result_code: "
- << static_cast<std::underlying_type<ErrorCode>::type>(result_code);
-
- do_in_jni_thread(
- FROM_HERE,
- Bind(&HasClientCallbacks::OnActivePresetSelectError,
- Unretained(callbacks_), std::move(addr_or_group_id), result_code));
- }
-
- void OnPresetInfo(std::variant<RawAddress, int> addr_or_group_id,
- PresetInfoReason change_id,
- std::vector<PresetInfo> detail_records) override {
- DVLOG(2) << __func__;
- for (const auto& rec : detail_records) {
- DVLOG(2) << "\t index: " << +rec.preset_index << ", change_id: "
- << (std::underlying_type<PresetInfoReason>::type)change_id
- << ", writable: " << rec.writable
- << ", available: " << rec.available
- << ", name: " << rec.preset_name;
- }
-
- do_in_jni_thread(FROM_HERE,
- Bind(&HasClientCallbacks::OnPresetInfo,
- Unretained(callbacks_), std::move(addr_or_group_id),
- change_id, std::move(detail_records)));
- }
-
- void OnPresetInfoError(std::variant<RawAddress, int> addr_or_group_id,
- uint8_t preset_index, ErrorCode result_code) override {
- DVLOG(2) << __func__ << " result_code: "
- << static_cast<std::underlying_type<ErrorCode>::type>(result_code);
-
- do_in_jni_thread(
- FROM_HERE,
- Bind(&HasClientCallbacks::OnPresetInfoError, Unretained(callbacks_),
- std::move(addr_or_group_id), preset_index, result_code));
- }
-
- void OnSetPresetNameError(std::variant<RawAddress, int> addr_or_group_id,
- uint8_t preset_index,
- ErrorCode result_code) override {
- DVLOG(2) << __func__ << " result_code: "
- << static_cast<std::underlying_type<ErrorCode>::type>(result_code);
-
- do_in_jni_thread(
- FROM_HERE,
- Bind(&HasClientCallbacks::OnSetPresetNameError, Unretained(callbacks_),
- std::move(addr_or_group_id), preset_index, result_code));
- }
-
- private:
- HasClientCallbacks* callbacks_;
-};
-
-} /* namespace */
-
-HasClientInterface* btif_has_client_get_interface(void) {
- if (!has_client_instance)
- has_client_instance.reset(new HearingAaccessClientServiceInterfaceImpl());
-
- return has_client_instance.get();
-}
diff --git a/btif/src/btif_storage.cc b/btif/src/btif_storage.cc
index 98e58ab73..f2787e9be 100644
--- a/btif/src/btif_storage.cc
+++ b/btif/src/btif_storage.cc
@@ -55,7 +55,6 @@
#include "bta_closure_api.h"
#include "bta_hd_api.h"
#include "bta_hearing_aid_api.h"
-#include "bta_has_api.h"
#include "bta_hh_api.h"
#include "btif_api.h"
#include "btif_config.h"
@@ -2539,142 +2538,6 @@ void btif_storage_remove_svc_chg_cccd(const RawAddress& bd_addr) {
bd_addr));
}
-constexpr char HAS_IS_ACCEPTLISTED[] = "LeAudioHasIsAcceptlisted";
-constexpr char HAS_FEATURES[] = "LeAudioHasFlags";
-constexpr char HAS_ACTIVE_PRESET[] = "LeAudioHasActivePreset";
-constexpr char HAS_SERIALIZED_PRESETS[] = "LeAudioHasSerializedPresets";
-
-void btif_storage_add_leaudio_has_device(const RawAddress& address,
- std::vector<uint8_t> presets_bin,
- uint8_t features,
- uint8_t active_preset) {
- do_in_jni_thread(
- FROM_HERE,
- Bind(
- [](const RawAddress& address, std::vector<uint8_t> presets_bin,
- uint8_t features, uint8_t active_preset) {
- const std::string& name = address.ToString();
-
- btif_config_set_int(name.c_str(), HAS_FEATURES, features);
- btif_config_set_int(name.c_str(), HAS_ACTIVE_PRESET, active_preset);
- btif_config_set_bin(name.c_str(), HAS_SERIALIZED_PRESETS,
- presets_bin.data(), presets_bin.size());
-
- btif_config_set_int(name.c_str(), HAS_IS_ACCEPTLISTED, true);
- btif_config_save();
- },
- address, std::move(presets_bin), features, active_preset));
-}
-
-void btif_storage_set_leaudio_has_active_preset(const RawAddress& address,
- uint8_t active_preset) {
- do_in_jni_thread(FROM_HERE,
- Bind(
- [](const RawAddress& address, uint8_t active_preset) {
- const std::string& name = address.ToString();
-
- btif_config_set_int(name.c_str(), HAS_ACTIVE_PRESET,
- active_preset);
- btif_config_save();
- },
- address, active_preset));
-}
-
-bool btif_storage_get_leaudio_has_features(const RawAddress& address,
- uint8_t& features) {
- std::string name = address.ToString();
-
- int value;
- if (!btif_config_get_int(name.c_str(), HAS_FEATURES, &value)) return false;
-
- features = value;
- return true;
-}
-
-void btif_storage_set_leaudio_has_features(const RawAddress& address,
- uint8_t features) {
- do_in_jni_thread(FROM_HERE,
- Bind(
- [](const RawAddress& address, uint8_t features) {
- const std::string& name = address.ToString();
-
- btif_config_set_int(name.c_str(), HAS_FEATURES, features);
- btif_config_save();
- },
- address, features));
-}
-
-void btif_storage_load_bonded_leaudio_has_devices() {
-/* for (const auto& bd_addr : btif_config_get_paired_devices()) {*/
- for (const auto& bd_addr : btif_config_get_paired_devices()) {
- const std::string& name = bd_addr.ToString();
-
- if (!btif_config_exist(name, HAS_IS_ACCEPTLISTED) &&
- !btif_config_exist(name, HAS_FEATURES))
- continue;
-
- int value;
- uint16_t is_acceptlisted = 0;
- if (btif_config_get_int(name, HAS_IS_ACCEPTLISTED, &value))
- is_acceptlisted = value;
-
- uint8_t features = 0;
- if (btif_config_get_int(name, HAS_FEATURES, &value)) features = value;
- do_in_bta_thread(FROM_HERE, Bind(&le_audio::has::HasClient::AddFromStorage,
- bd_addr, features, is_acceptlisted));
- }
-}
-
-void btif_storage_remove_leaudio_has(const RawAddress& address) {
- std::string addrstr = address.ToString();
- btif_config_remove(addrstr.c_str(), HAS_IS_ACCEPTLISTED);
- btif_config_remove(addrstr.c_str(), HAS_FEATURES);
- btif_config_remove(addrstr.c_str(), HAS_ACTIVE_PRESET);
- btif_config_remove(addrstr.c_str(), HAS_SERIALIZED_PRESETS);
- btif_config_save();
-}
-
-void btif_storage_set_leaudio_has_acceptlist(const RawAddress& address,
- bool add_to_acceptlist) {
- std::string addrstr = address.ToString();
-
- btif_config_set_int(addrstr.c_str(), HAS_IS_ACCEPTLISTED, add_to_acceptlist);
- btif_config_save();
-}
-
-void btif_storage_set_leaudio_has_presets(const RawAddress& address,
- std::vector<uint8_t> presets_bin) {
- do_in_jni_thread(
- FROM_HERE,
- Bind(
- [](const RawAddress& address, std::vector<uint8_t> presets_bin) {
- const std::string& name = address.ToString();
-
- btif_config_set_bin(name.c_str(), HAS_SERIALIZED_PRESETS,
- presets_bin.data(), presets_bin.size());
- btif_config_save();
- },
- address, std::move(presets_bin)));
-}
-
-bool btif_storage_get_leaudio_has_presets(const RawAddress& address,
- std::vector<uint8_t>& presets_bin,
- uint8_t& active_preset) {
- std::string name = address.ToString();
-
- int value;
- if (!btif_config_get_int(name.c_str(), HAS_ACTIVE_PRESET, &value)) return false;
- active_preset = value;
-
- auto bin_sz = btif_config_get_bin_length(name.c_str(), HAS_SERIALIZED_PRESETS);
- presets_bin.resize(bin_sz);
- if (!btif_config_get_bin(name.c_str(), HAS_SERIALIZED_PRESETS, presets_bin.data(),
- &bin_sz))
- return false;
-
- return true;
-}
-
void btif_storage_set_encr_data_cccd(const RawAddress& bd_addr, uint8_t cccd) {
do_in_jni_thread(FROM_HERE, Bind(
[](const RawAddress& bd_addr, uint8_t cccd) {
@@ -2777,4 +2640,3 @@ bt_status_t btif_storage_remove_enc_key_material(const RawAddress* remote_bd_add
btif_config_save();
return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
}
-
diff --git a/include/hardware/bluetooth.h b/include/hardware/bluetooth.h
index b1c85e010..5185cc8d2 100644
--- a/include/hardware/bluetooth.h
+++ b/include/hardware/bluetooth.h
@@ -51,7 +51,6 @@
#define BT_PROFILE_AV_RC_ID "avrcp"
#define BT_PROFILE_AV_RC_CTRL_ID "avrcp_ctrl"
#define BT_PROFILE_HEARING_AID_ID "hearing_aid"
-#define BT_PROFILE_HAP_CLIENT_ID "has_client"
#define BT_KEYSTORE_ID "bluetooth_keystore"
/** Bluetooth Device Name */
diff --git a/include/hardware/bt_has.h b/include/hardware/bt_has.h
deleted file mode 100644
index 739edc041..000000000
--- a/include/hardware/bt_has.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#pragma once
-
-#include <hardware/bluetooth.h>
-
-#include <variant>
-#include <vector>
-
-namespace bluetooth {
-namespace has {
-
-/** Connection State */
-enum class ConnectionState : uint8_t {
- DISCONNECTED = 0,
- CONNECTING,
- CONNECTED,
- DISCONNECTING,
-};
-
-/** Results codes for the failed preset operations */
-enum class ErrorCode : uint8_t {
- NO_ERROR = 0,
- SET_NAME_NOT_ALLOWED, // Preset cannot be written (read only preset)
- OPERATION_NOT_SUPPORTED, // If theres no optional characteristic,
- // or request opcode is invalid or not supported
- OPERATION_NOT_POSSIBLE, // Operation cannot be performed at this time
- INVALID_PRESET_NAME_LENGTH,
- INVALID_PRESET_INDEX,
- GROUP_OPERATION_NOT_SUPPORTED,
- PROCEDURE_ALREADY_IN_PROGRESS,
-};
-
-enum class PresetInfoReason : uint8_t {
- ALL_PRESET_INFO = 0,
- PRESET_INFO_UPDATE,
- PRESET_DELETED,
- PRESET_AVAILABILITY_CHANGED,
- PRESET_INFO_REQUEST_RESPONSE,
-};
-
-struct PresetInfo {
- uint8_t preset_index;
-
- bool writable;
- bool available;
- std::string preset_name;
-};
-
-/** Service supported feature bits */
-static constexpr uint8_t kFeatureBitHearingAidTypeBinaural = 0x00;
-static constexpr uint8_t kFeatureBitHearingAidTypeMonaural = 0x01;
-static constexpr uint8_t kFeatureBitHearingAidTypeBanded = 0x02;
-static constexpr uint8_t kFeatureBitPresetSynchronizationSupported = 0x04;
-static constexpr uint8_t kFeatureBitIndependentPresets = 0x08;
-static constexpr uint8_t kFeatureBitDynamicPresets = 0x10;
-static constexpr uint8_t kFeatureBitWritablePresets = 0x20;
-
-/** Invalid values for the group and preset identifiers */
-static constexpr uint8_t kHasPresetIndexInvalid = 0x00;
-static constexpr int kHasGroupIdInvalid = -1;
-
-class HasClientCallbacks {
- public:
- virtual ~HasClientCallbacks() = default;
-
- /** Callback for profile connection state change */
- virtual void OnConnectionState(ConnectionState state,
- const RawAddress& addr) = 0;
-
- /** Callback for the new available device */
- virtual void OnDeviceAvailable(const RawAddress& addr, uint8_t features) = 0;
-
- /** Callback for getting device HAS flags */
- virtual void OnFeaturesUpdate(const RawAddress& addr, uint8_t features) = 0;
-
- /** Callback for the currently active preset */
- virtual void OnActivePresetSelected(
- std::variant<RawAddress, int> addr_or_group_id, uint8_t preset_index) = 0;
-
- /** Callbacks for the active preset selection error */
- virtual void OnActivePresetSelectError(
- std::variant<RawAddress, int> addr_or_group_id, ErrorCode error_code) = 0;
-
- /** Callbacks for the preset details event */
- virtual void OnPresetInfo(std::variant<RawAddress, int> addr_or_group_id,
- PresetInfoReason change_id,
- std::vector<PresetInfo> info_records) = 0;
-
- /** Callback for the preset details get error */
- virtual void OnPresetInfoError(std::variant<RawAddress, int> addr_or_group_id,
- uint8_t preset_index,
- ErrorCode error_code) = 0;
-
- /** Callback for the preset name set error */
- virtual void OnSetPresetNameError(
- std::variant<RawAddress, int> addr_or_group_id, uint8_t preset_index,
- ErrorCode error_code) = 0;
-};
-
-class HasClientInterface {
- public:
- virtual ~HasClientInterface() = default;
-
- /** Register the Hearing Aid Service Client profile callbacks */
- virtual void Init(HasClientCallbacks* callbacks) = 0;
-
- /** Connect to HAS service */
- virtual void Connect(const RawAddress& addr) = 0;
-
- /** Disconnect from HAS service */
- virtual void Disconnect(const RawAddress& addr) = 0;
-
- /** Select preset by the index as currently active */
- virtual void SelectActivePreset(
- std::variant<RawAddress, int> addr_or_group_id, uint8_t preset_index) = 0;
-
- /** Select next preset as currently active */
- virtual void NextActivePreset(
- std::variant<RawAddress, int> addr_or_group_id) = 0;
-
- /** Select previous preset as currently active */
- virtual void PreviousActivePreset(
- std::variant<RawAddress, int> addr_or_group_id) = 0;
-
- /** Get preset name by the index */
- virtual void GetPresetInfo(const RawAddress& addr, uint8_t preset_index) = 0;
-
- /** Set preset name by the index */
- virtual void SetPresetName(std::variant<RawAddress, int> addr_or_group_id,
- uint8_t preset_index, std::string name) = 0;
-
- /** Called when HAS capable device is unbonded */
- virtual void RemoveDevice(const RawAddress& addr) = 0;
-
- /** Closes the interface */
- virtual void Cleanup(void) = 0;
-};
-
-} // namespace has
-} // namespace bluetooth
diff --git a/internal_include/bt_target.h b/internal_include/bt_target.h
index ac594ff68..ef07f9743 100644
--- a/internal_include/bt_target.h
+++ b/internal_include/bt_target.h
@@ -67,6 +67,7 @@
#include "bdroid_buildcfg.h"
#endif
+#include <hardware/bt_av.h>
#include "bt_types.h" /* This must be defined AFTER buildcfg.h */
//------------------Added from bdroid_buildcfg.h---------------------
@@ -467,13 +468,6 @@
#define BTM_MAX_LOC_BD_NAME_LEN 248
#endif
-/* Fixed Default String. When this is defined as null string, the device's
- * product model name is used as the default local name.
- */
-#ifndef BTM_DEF_LOCAL_NAME
-#define BTM_DEF_LOCAL_NAME ""
-#endif
-
/* Maximum service name stored with security authorization (0 if not needed) */
#ifndef BTM_SEC_SERVICE_NAME_LEN
#define BTM_SEC_SERVICE_NAME_LEN BT_MAX_SERVICE_NAME_LEN