| /* |
| * Copyright 2019 The Android Open Source Project |
| * |
| * 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. |
| */ |
| |
| #define LOG_TAG "BTAudioClientHearingAid" |
| |
| #include "hearing_aid_software_encoding.h" |
| #include "client_interface.h" |
| |
| #include "audio_hearing_aid_hw/include/audio_hearing_aid_hw.h" |
| #include "osi/include/log.h" |
| #include "osi/include/properties.h" |
| |
| namespace { |
| |
| using ::android::hardware::bluetooth::audio::V2_0::CodecType; |
| using ::bluetooth::audio::AudioConfiguration; |
| using ::bluetooth::audio::BitsPerSample; |
| using ::bluetooth::audio::BluetoothAudioCtrlAck; |
| using ::bluetooth::audio::ChannelMode; |
| using ::bluetooth::audio::PcmParameters; |
| using ::bluetooth::audio::SampleRate; |
| using ::bluetooth::audio::SessionType; |
| using ::bluetooth::audio::hearing_aid::StreamCallbacks; |
| |
| // Transport implementation for Hearing Aids |
| class HearingAidTransport |
| : public bluetooth::audio::IBluetoothTransportInstance { |
| public: |
| HearingAidTransport(StreamCallbacks stream_cb) |
| : IBluetoothTransportInstance( |
| SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH, {}), |
| stream_cb_(std::move(stream_cb)), |
| remote_delay_report_ms_(0), |
| total_bytes_read_(0), |
| data_position_({}){}; |
| |
| BluetoothAudioCtrlAck StartRequest() override { |
| LOG(INFO) << __func__; |
| if (stream_cb_.on_resume_(true)) { |
| return BluetoothAudioCtrlAck::SUCCESS_FINISHED; |
| } |
| return BluetoothAudioCtrlAck::FAILURE; |
| } |
| |
| BluetoothAudioCtrlAck SuspendRequest() override { |
| LOG(INFO) << __func__; |
| if (stream_cb_.on_suspend_()) { |
| uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2]; |
| ::bluetooth::audio::hearing_aid::read(p_buf, sizeof(p_buf)); |
| return BluetoothAudioCtrlAck::SUCCESS_FINISHED; |
| } else { |
| return BluetoothAudioCtrlAck::FAILURE; |
| } |
| } |
| |
| void StopRequest() override { |
| LOG(INFO) << __func__; |
| if (stream_cb_.on_suspend_()) { |
| // flush |
| uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2]; |
| ::bluetooth::audio::hearing_aid::read(p_buf, sizeof(p_buf)); |
| } |
| } |
| |
| bool GetPresentationPosition(uint64_t* remote_delay_report_ns, |
| uint64_t* total_bytes_read, |
| timespec* data_position) override { |
| VLOG(2) << __func__ << ": data=" << total_bytes_read_ |
| << " byte(s), timestamp=" << data_position_.tv_sec << "." |
| << data_position_.tv_nsec |
| << "s, delay report=" << remote_delay_report_ms_ << " msec."; |
| if (remote_delay_report_ns != nullptr) { |
| *remote_delay_report_ns = remote_delay_report_ms_ * 1000000u; |
| } |
| if (total_bytes_read != nullptr) *total_bytes_read = total_bytes_read_; |
| if (data_position != nullptr) *data_position = data_position_; |
| |
| return true; |
| } |
| |
| void MetadataChanged(const source_metadata_t& source_metadata) override { |
| auto track_count = source_metadata.track_count; |
| auto tracks = source_metadata.tracks; |
| LOG(INFO) << __func__ << ": " << track_count << " track(s) received"; |
| while (track_count) { |
| VLOG(1) << __func__ << ": usage=" << tracks->usage |
| << ", content_type=" << tracks->content_type |
| << ", gain=" << tracks->gain; |
| --track_count; |
| ++tracks; |
| } |
| } |
| |
| void ResetPresentationPosition() override { |
| VLOG(2) << __func__ << ": called."; |
| remote_delay_report_ms_ = 0; |
| total_bytes_read_ = 0; |
| data_position_ = {}; |
| } |
| |
| void LogBytesRead(size_t bytes_read) override { |
| if (bytes_read) { |
| total_bytes_read_ += bytes_read; |
| clock_gettime(CLOCK_MONOTONIC, &data_position_); |
| } |
| } |
| |
| void SetRemoteDelay(uint16_t delay_report_ms) { |
| LOG(INFO) << __func__ << ": delay_report=" << delay_report_ms << " msec"; |
| remote_delay_report_ms_ = delay_report_ms; |
| } |
| |
| private: |
| StreamCallbacks stream_cb_; |
| uint16_t remote_delay_report_ms_; |
| uint64_t total_bytes_read_; |
| timespec data_position_; |
| }; |
| |
| bool HearingAidGetSelectedHalPcmConfig(PcmParameters* hal_pcm_config) { |
| if (hal_pcm_config == nullptr) return false; |
| // TODO: we only support one config for now! |
| hal_pcm_config->sampleRate = SampleRate::RATE_16000; |
| hal_pcm_config->bitsPerSample = BitsPerSample::BITS_16; |
| hal_pcm_config->channelMode = ChannelMode::STEREO; |
| return true; |
| } |
| |
| // Sink instance of Hearing Aids to provide call-in APIs for Bluetooth Audio Hal |
| HearingAidTransport* hearing_aid_sink = nullptr; |
| // Common interface to call-out into Bluetooth Audio Hal |
| bluetooth::audio::BluetoothAudioClientInterface* |
| hearing_aid_hal_clientinterface = nullptr; |
| bool btaudio_hearing_aid_disabled = false; |
| bool is_configured = false; |
| |
| // Save the value if the remote reports its delay before hearing_aid_sink is |
| // initialized |
| uint16_t remote_delay_ms = 0; |
| |
| bool is_hal_2_0_force_disabled() { |
| if (!is_configured) { |
| btaudio_hearing_aid_disabled = osi_property_get_bool(BLUETOOTH_AUDIO_HAL_PROP_DISABLED, false); |
| is_configured = true; |
| } |
| return btaudio_hearing_aid_disabled; |
| } |
| |
| } // namespace |
| |
| namespace bluetooth { |
| namespace audio { |
| namespace hearing_aid { |
| |
| bool is_hal_2_0_enabled() { return hearing_aid_hal_clientinterface != nullptr; } |
| |
| bool init(StreamCallbacks stream_cb, |
| bluetooth::common::MessageLoopThread* message_loop) { |
| LOG(INFO) << __func__; |
| |
| if (is_hal_2_0_force_disabled()) { |
| LOG(ERROR) << __func__ << ": BluetoothAudio HAL is disabled"; |
| return false; |
| } |
| |
| hearing_aid_sink = new HearingAidTransport(std::move(stream_cb)); |
| hearing_aid_hal_clientinterface = |
| new bluetooth::audio::BluetoothAudioClientInterface(hearing_aid_sink, |
| message_loop); |
| if (!hearing_aid_hal_clientinterface->IsValid()) { |
| LOG(WARNING) << __func__ << ": BluetoothAudio HAL for Hearing Aid is invalid?!"; |
| delete hearing_aid_hal_clientinterface; |
| hearing_aid_hal_clientinterface = nullptr; |
| delete hearing_aid_sink; |
| hearing_aid_sink = nullptr; |
| return false; |
| } |
| |
| if (remote_delay_ms != 0) { |
| LOG(INFO) << __func__ << ": restore DELAY " << remote_delay_ms << " ms"; |
| hearing_aid_sink->SetRemoteDelay(remote_delay_ms); |
| remote_delay_ms = 0; |
| } |
| |
| return true; |
| } |
| |
| void cleanup() { |
| LOG(INFO) << __func__; |
| if (!is_hal_2_0_enabled()) return; |
| end_session(); |
| delete hearing_aid_hal_clientinterface; |
| hearing_aid_hal_clientinterface = nullptr; |
| delete hearing_aid_sink; |
| hearing_aid_sink = nullptr; |
| remote_delay_ms = 0; |
| } |
| |
| void start_session() { |
| LOG(INFO) << __func__; |
| if (!is_hal_2_0_enabled()) return; |
| AudioConfiguration audio_config; |
| PcmParameters pcm_config{}; |
| if (!HearingAidGetSelectedHalPcmConfig(&pcm_config)) { |
| LOG(ERROR) << __func__ << ": cannot get PCM config"; |
| return; |
| } |
| audio_config.pcmConfig(pcm_config); |
| if (!hearing_aid_hal_clientinterface->UpdateAudioConfig(audio_config)) { |
| LOG(ERROR) << __func__ << ": cannot update audio config to HAL"; |
| return; |
| } |
| hearing_aid_hal_clientinterface->StartSession(); |
| } |
| |
| void end_session() { |
| LOG(INFO) << __func__; |
| if (!is_hal_2_0_enabled()) return; |
| hearing_aid_hal_clientinterface->EndSession(); |
| } |
| |
| size_t read(uint8_t* p_buf, uint32_t len) { |
| if (!is_hal_2_0_enabled()) return 0; |
| return hearing_aid_hal_clientinterface->ReadAudioData(p_buf, len); |
| } |
| |
| // Update Hearing Aids delay report to BluetoothAudio HAL |
| void set_remote_delay(uint16_t delay_report_ms) { |
| if (!is_hal_2_0_enabled()) { |
| LOG(INFO) << __func__ << ": not ready for DelayReport " << delay_report_ms |
| << " ms"; |
| remote_delay_ms = delay_report_ms; |
| return; |
| } |
| LOG(INFO) << __func__ << ": delay_report_ms=" << delay_report_ms << " ms"; |
| hearing_aid_sink->SetRemoteDelay(delay_report_ms); |
| } |
| |
| } // namespace hearing_aid |
| } // namespace audio |
| } // namespace bluetooth |