| /* |
| * 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 <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_main_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_main_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_main_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_main_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_main_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_main_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_main_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_main_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_main_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_main_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(); |
| } |