/****************************************************************************** * * Copyright (C) 2017 Google, Inc. * * 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 "btif_a2dp_audio_interface" #include "btif_a2dp_audio_interface.h" #include #include #include #include #include #include #include #include #include #include "a2dp_sbc.h" #include "bt_common.h" #include "bta/av/bta_av_int.h" #include "btif_a2dp.h" #include "btif_a2dp_control.h" #include "btif_a2dp_sink.h" #include "btif_a2dp_source.h" #include "btif_av.h" #include "btif_av_co.h" #include "btif_hf.h" #include "common/metrics.h" #include "common/time_util.h" #include "osi/include/osi.h" #include "stack/include/btu.h" using bluetooth::common::A2dpSessionMetrics; using bluetooth::common::BluetoothMetricsLogger; using android::hardware::bluetooth::a2dp::V1_0::IBluetoothAudioOffload; using android::hardware::bluetooth::a2dp::V1_0::IBluetoothAudioHost; using android::hardware::bluetooth::a2dp::V1_0::Status; using android::hardware::bluetooth::a2dp::V1_0::CodecConfiguration; using android::hardware::bluetooth::a2dp::V1_0::CodecType; using android::hardware::bluetooth::a2dp::V1_0::SampleRate; using android::hardware::bluetooth::a2dp::V1_0::BitsPerSample; using android::hardware::bluetooth::a2dp::V1_0::ChannelMode; using android::hardware::ProcessState; using ::android::hardware::Return; using ::android::hardware::Void; using ::android::hardware::hidl_death_recipient; using ::android::hardware::hidl_vec; using ::android::sp; using ::android::wp; android::sp btAudio; #define CASE_RETURN_STR(const) \ case const: \ return #const; static uint8_t a2dp_cmd_pending = A2DP_CTRL_CMD_NONE; static Status mapToStatus(uint8_t resp); uint8_t btif_a2dp_audio_process_request(uint8_t cmd); static void btif_a2dp_audio_send_start_req(); static void btif_a2dp_audio_send_suspend_req(); static void btif_a2dp_audio_send_stop_req(); static void btif_a2dp_audio_interface_init(); static void btif_a2dp_audio_interface_deinit(); static void btif_a2dp_audio_interface_restart_session(); // Delay reporting // static void btif_a2dp_audio_send_sink_latency(); class A2dpOffloadAudioStats { public: A2dpOffloadAudioStats() { Reset(); } void Reset() { std::lock_guard lock(lock_); ResetPreserveSession(); codec_index_ = -1; } void ResetPreserveSession() { std::lock_guard lock(lock_); audio_start_time_ms_ = -1; audio_stop_time_ms_ = -1; } void StoreMetrics() { std::lock_guard lock(lock_); if (audio_start_time_ms_ < 0 || audio_stop_time_ms_ < 0) { return; } A2dpSessionMetrics metrics; metrics.codec_index = codec_index_; metrics.is_a2dp_offload = true; if (audio_stop_time_ms_ > audio_start_time_ms_) { metrics.audio_duration_ms = audio_stop_time_ms_ - audio_start_time_ms_; } BluetoothMetricsLogger::GetInstance()->LogA2dpSession(metrics); } void LogAudioStart() { std::lock_guard lock(lock_); audio_start_time_ms_ = bluetooth::common::time_get_os_boottime_ms(); } void LogAudioStop() { std::lock_guard lock(lock_); audio_stop_time_ms_ = bluetooth::common::time_get_os_boottime_ms(); } void LogAudioStopMetricsAndReset() { std::lock_guard lock(lock_); LogAudioStop(); StoreMetrics(); ResetPreserveSession(); } void SetCodecIndex(int64_t codec_index) { std::lock_guard lock(lock_); codec_index_ = codec_index; } private: std::recursive_mutex lock_; int64_t audio_start_time_ms_ = -1; int64_t audio_stop_time_ms_ = -1; int64_t codec_index_ = -1; }; static A2dpOffloadAudioStats a2dp_offload_audio_stats; class BluetoothAudioHost : public IBluetoothAudioHost { public: Return startStream() { btif_a2dp_audio_send_start_req(); return Void(); } Return suspendStream() { btif_a2dp_audio_send_suspend_req(); return Void(); } Return stopStream() { btif_a2dp_audio_send_stop_req(); return Void(); } // TODO : Delay reporting /* Return a2dp_get_sink_latency() { LOG_INFO(LOG_TAG,"%s:start ", __func__); btif_a2dp_audio_send_sink_latency(); return Void(); }*/ }; class BluetoothAudioDeathRecipient : public hidl_death_recipient { public: virtual void serviceDied( uint64_t /*cookie*/, const wp<::android::hidl::base::V1_0::IBase>& /*who*/) { LOG_ERROR(LOG_TAG, "%s", __func__); // Restart the session on the correct thread do_in_main_thread(FROM_HERE, base::Bind(&btif_a2dp_audio_interface_restart_session)); } }; sp bluetoothAudioDeathRecipient = new BluetoothAudioDeathRecipient(); static Status mapToStatus(uint8_t resp) { switch (resp) { case A2DP_CTRL_ACK_SUCCESS: return Status::SUCCESS; break; case A2DP_CTRL_ACK_PENDING: return Status::PENDING; break; case A2DP_CTRL_ACK_FAILURE: case A2DP_CTRL_ACK_INCALL_FAILURE: case A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS: return Status::FAILURE; default: APPL_TRACE_WARNING("%s: unknown status recevied :%d", __func__, resp); return Status::FAILURE; break; } } static void btif_a2dp_get_codec_configuration( CodecConfiguration* p_codec_info) { LOG_INFO(LOG_TAG, "%s", __func__); tBT_A2DP_OFFLOAD a2dp_offload; A2dpCodecConfig* a2dpCodecConfig = bta_av_get_a2dp_current_codec(); a2dpCodecConfig->getCodecSpecificConfig(&a2dp_offload); btav_a2dp_codec_config_t codec_config; codec_config = a2dpCodecConfig->getCodecConfig(); a2dp_offload_audio_stats.SetCodecIndex(a2dpCodecConfig->codecIndex()); switch (codec_config.codec_type) { case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC: p_codec_info->codecType = (::android::hardware::bluetooth::a2dp::V1_0::CodecType) BTA_AV_CODEC_TYPE_SBC; p_codec_info->codecSpecific.sbcData.codecParameters = a2dp_offload.codec_info[0]; LOG_INFO(LOG_TAG, " %s: codec parameters =%d", __func__, a2dp_offload.codec_info[0]); p_codec_info->codecSpecific.sbcData.minBitpool = a2dp_offload.codec_info[1]; p_codec_info->codecSpecific.sbcData.maxBitpool = a2dp_offload.codec_info[2]; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC: p_codec_info->codecType = (::android::hardware::bluetooth::a2dp::V1_0::CodecType) BTA_AV_CODEC_TYPE_AAC; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX: p_codec_info->codecType = (::android::hardware::bluetooth::a2dp::V1_0::CodecType) BTA_AV_CODEC_TYPE_APTX; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: p_codec_info->codecType = (::android::hardware::bluetooth::a2dp::V1_0::CodecType) BTA_AV_CODEC_TYPE_APTXHD; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: p_codec_info->codecType = (::android::hardware::bluetooth::a2dp::V1_0::CodecType) BTA_AV_CODEC_TYPE_LDAC; p_codec_info->codecSpecific.ldacData.bitrateIndex = a2dp_offload.codec_info[6]; break; default: APPL_TRACE_ERROR("%s: Unknown Codec type :%d ", __func__, codec_config.codec_type); } // Obtain the MTU RawAddress peer_addr = btif_av_source_active_peer(); tA2DP_ENCODER_INIT_PEER_PARAMS peer_param; bta_av_co_get_peer_params(peer_addr, &peer_param); int effectiveMtu = a2dpCodecConfig->getEffectiveMtu(); if (effectiveMtu > 0 && effectiveMtu < peer_param.peer_mtu) { p_codec_info->peerMtu = effectiveMtu; } else { p_codec_info->peerMtu = peer_param.peer_mtu; } LOG_INFO(LOG_TAG, "%s: peer MTU: %d effective MTU: %d result MTU: %d", __func__, peer_param.peer_mtu, effectiveMtu, p_codec_info->peerMtu); p_codec_info->sampleRate = (::android::hardware::bluetooth::a2dp::V1_0::SampleRate) codec_config.sample_rate; p_codec_info->bitsPerSample = (::android::hardware::bluetooth::a2dp::V1_0::BitsPerSample) codec_config.bits_per_sample; p_codec_info->channelMode = (::android::hardware::bluetooth::a2dp::V1_0::ChannelMode) codec_config.channel_mode; p_codec_info->encodedAudioBitrate = a2dpCodecConfig->getTrackBitRate(); } static void btif_a2dp_audio_interface_init() { LOG_INFO(LOG_TAG, "%s", __func__); btAudio = IBluetoothAudioOffload::getService(); CHECK(btAudio != nullptr); auto death_link = btAudio->linkToDeath(bluetoothAudioDeathRecipient, 0); if (!death_link.isOk()) { LOG_ERROR(LOG_TAG, "%s: Cannot observe the Bluetooth Audio HAL's death", __func__); } LOG_DEBUG( LOG_TAG, "%s: IBluetoothAudioOffload::getService() returned %p (%s)", __func__, btAudio.get(), (btAudio->isRemote() ? "remote" : "local")); LOG_INFO(LOG_TAG, "%s:Init returned", __func__); } static void btif_a2dp_audio_interface_deinit() { LOG_INFO(LOG_TAG, "%s: start", __func__); if (btAudio != nullptr) { auto death_unlink = btAudio->unlinkToDeath(bluetoothAudioDeathRecipient); if (!death_unlink.isOk()) { LOG_ERROR(LOG_TAG, "%s: Error unlinking death observer from Bluetooth Audio HAL", __func__); } } btAudio = nullptr; } void btif_a2dp_audio_interface_start_session() { LOG_INFO(LOG_TAG, "%s", __func__); BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart( bluetooth::common::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0); a2dp_offload_audio_stats.Reset(); btif_a2dp_audio_interface_init(); CHECK(btAudio != nullptr); CodecConfiguration codec_info; btif_a2dp_get_codec_configuration(&codec_info); android::sp host_if = new BluetoothAudioHost(); btAudio->startSession(host_if, codec_info); } void btif_a2dp_audio_interface_end_session() { LOG_INFO(LOG_TAG, "%s", __func__); a2dp_offload_audio_stats.LogAudioStopMetricsAndReset(); BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd( bluetooth::common::DISCONNECT_REASON_UNKNOWN, 0); a2dp_offload_audio_stats.Reset(); if (btAudio == nullptr) return; auto ret = btAudio->endSession(); if (!ret.isOk()) { LOG_ERROR(LOG_TAG, "HAL server is dead"); } btif_a2dp_audio_interface_deinit(); } // Conditionally restart the session only if it was started before static void btif_a2dp_audio_interface_restart_session() { LOG_INFO(LOG_TAG, "%s", __func__); if (btAudio == nullptr) { LOG_INFO(LOG_TAG, "%s: nothing to restart - session was not started", __func__); return; } btAudio = nullptr; btif_a2dp_audio_interface_start_session(); } void btif_a2dp_audio_on_started(tBTA_AV_STATUS status) { LOG_INFO(LOG_TAG, "%s: status = %d", __func__, status); if (btAudio != nullptr) { if (a2dp_cmd_pending == A2DP_CTRL_CMD_START) { if (status != A2DP_CTRL_ACK_PENDING) { a2dp_cmd_pending = A2DP_CTRL_CMD_NONE; } LOG_INFO(LOG_TAG, "%s: calling method onStarted", __func__); auto hal_status = mapToStatus(status); btAudio->streamStarted(hal_status); if (hal_status == Status::SUCCESS) { a2dp_offload_audio_stats.LogAudioStart(); } } } } void btif_a2dp_audio_on_suspended(tBTA_AV_STATUS status) { LOG_INFO(LOG_TAG, "%s: status = %d", __func__, status); if (btAudio != nullptr) { if (a2dp_cmd_pending == A2DP_CTRL_CMD_SUSPEND) { if (status != A2DP_CTRL_ACK_PENDING) { a2dp_cmd_pending = A2DP_CTRL_CMD_NONE; } LOG_INFO(LOG_TAG, "calling method onSuspended"); auto hal_status = mapToStatus(status); btAudio->streamSuspended(hal_status); if (hal_status == Status::SUCCESS) { a2dp_offload_audio_stats.LogAudioStopMetricsAndReset(); } } } } void btif_a2dp_audio_on_stopped(tBTA_AV_STATUS status) { LOG_INFO(LOG_TAG, "%s: status = %d", __func__, status); if (btAudio != nullptr && a2dp_cmd_pending == A2DP_CTRL_CMD_START) { a2dp_cmd_pending = A2DP_CTRL_CMD_NONE; LOG_INFO(LOG_TAG, "%s: Remote disconnected when start under progress", __func__); btAudio->streamStarted(mapToStatus(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS)); a2dp_offload_audio_stats.LogAudioStopMetricsAndReset(); } } void btif_a2dp_audio_send_start_req() { LOG_INFO(LOG_TAG, "%s", __func__); uint8_t resp; resp = btif_a2dp_audio_process_request(A2DP_CTRL_CMD_START); if (btAudio != nullptr) { auto status = mapToStatus(resp); auto ret = btAudio->streamStarted(status); if (status == Status::SUCCESS) { a2dp_offload_audio_stats.LogAudioStart(); } if (!ret.isOk()) LOG_ERROR(LOG_TAG, "HAL server died"); } } void btif_a2dp_audio_send_suspend_req() { LOG_INFO(LOG_TAG, "%s", __func__); uint8_t resp; resp = btif_a2dp_audio_process_request(A2DP_CTRL_CMD_SUSPEND); if (btAudio != nullptr) { auto status = mapToStatus(resp); auto ret = btAudio->streamSuspended(status); if (status == Status::SUCCESS) { a2dp_offload_audio_stats.LogAudioStopMetricsAndReset(); } if (!ret.isOk()) LOG_ERROR(LOG_TAG, "HAL server died"); } } void btif_a2dp_audio_send_stop_req() { LOG_INFO(LOG_TAG, "%s", __func__); btif_a2dp_audio_process_request(A2DP_CTRL_CMD_STOP); a2dp_offload_audio_stats.LogAudioStopMetricsAndReset(); } /*void btif_a2dp_audio_send_sink_latency() { LOG_INFO(LOG_TAG, "%s", __func__); uint16_t sink_latency = btif_av_get_sink_latency(); if (btAudio != nullptr) { auto ret = btAudio->a2dp_on_get_sink_latency(sink_latency); if (!ret.isOk()) LOG_ERROR(LOG_TAG, "server died"); } }*/ uint8_t btif_a2dp_audio_process_request(uint8_t cmd) { LOG_INFO(LOG_TAG, "%s: cmd: %s", __func__, audio_a2dp_hw_dump_ctrl_event((tA2DP_CTRL_CMD)cmd)); uint8_t status; switch (cmd) { case A2DP_CTRL_CMD_START: /* * Don't send START request to stack while we are in a call. * Some headsets such as "Sony MW600", don't allow AVDTP START * while in a call, and respond with BAD_STATE. */ if (!bluetooth::headset::IsCallIdle()) { APPL_TRACE_WARNING("%s: A2DP command %s failed as call state is busy", __func__, audio_a2dp_hw_dump_ctrl_event((tA2DP_CTRL_CMD)cmd)); status = A2DP_CTRL_ACK_INCALL_FAILURE; break; } if (btif_av_stream_started_ready()) { /* * Already started, setup audio data channel listener and ACK * back immediately. */ status = A2DP_CTRL_ACK_SUCCESS; break; } if (btif_av_stream_ready()) { /* * Post start event and wait for audio path to open. * If we are the source, the ACK will be sent after the start * procedure is completed, othewise send it now. */ btif_av_stream_start(); if (btif_av_get_peer_sep() == AVDT_TSEP_SRC) { status = A2DP_CTRL_ACK_SUCCESS; break; } /*Return pending and ack when start stream cfm received from remote*/ status = A2DP_CTRL_ACK_PENDING; break; } APPL_TRACE_WARNING("%s: A2DP command %s while AV stream is not ready", __func__, audio_a2dp_hw_dump_ctrl_event((tA2DP_CTRL_CMD)cmd)); status = A2DP_CTRL_ACK_FAILURE; break; case A2DP_CTRL_CMD_STOP: if (btif_av_get_peer_sep() == AVDT_TSEP_SNK && !btif_a2dp_source_is_streaming()) { /* We are already stopped, just ack back */ status = A2DP_CTRL_ACK_SUCCESS; break; } btif_av_stream_stop(RawAddress::kEmpty); status = A2DP_CTRL_ACK_SUCCESS; break; case A2DP_CTRL_CMD_SUSPEND: /* Local suspend */ if (btif_av_stream_started_ready()) { btif_av_stream_suspend(); status = A2DP_CTRL_ACK_PENDING; break; } /* If we are not in started state, just ack back ok and let * audioflinger close the channel. This can happen if we are * remotely suspended, clear REMOTE SUSPEND flag. */ btif_av_clear_remote_suspend_flag(); status = A2DP_CTRL_ACK_SUCCESS; break; case A2DP_CTRL_CMD_OFFLOAD_START: btif_av_stream_start_offload(); status = A2DP_CTRL_ACK_PENDING; break; default: APPL_TRACE_ERROR("UNSUPPORTED CMD (%d)", cmd); status = A2DP_CTRL_ACK_FAILURE; break; } LOG_INFO(LOG_TAG, "a2dp-ctrl-cmd : %s DONE returning status %d", audio_a2dp_hw_dump_ctrl_event((tA2DP_CTRL_CMD)cmd), status); if (status == A2DP_CTRL_ACK_PENDING) { a2dp_cmd_pending = cmd; } else { a2dp_cmd_pending = A2DP_CTRL_CMD_NONE; } return status; }