| /* |
| * Copyright 2021 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. |
| */ |
| |
| #include "a2dp_encoding_host.h" |
| |
| #include <errno.h> |
| #include <grp.h> |
| #include <sys/stat.h> |
| |
| #include <memory> |
| |
| #include "a2dp_encoding.h" |
| #include "a2dp_sbc_constants.h" |
| #include "btif_a2dp_source.h" |
| #include "btif_av.h" |
| #include "btif_av_co.h" |
| #include "btif_hf.h" |
| #include "osi/include/log.h" |
| #include "osi/include/properties.h" |
| #include "types/raw_address.h" |
| #include "udrv/include/uipc.h" |
| |
| #include <base/logging.h> |
| |
| #define A2DP_DATA_READ_POLL_MS 10 |
| #define A2DP_HOST_DATA_PATH "/var/run/bluetooth/audio/.a2dp_data" |
| // TODO(b/198260375): Make A2DP data owner group configurable. |
| #define A2DP_HOST_DATA_GROUP "bluetooth-audio" |
| |
| namespace { |
| |
| std::unique_ptr<tUIPC_STATE> a2dp_uipc = nullptr; |
| |
| static void btif_a2dp_data_cb([[maybe_unused]] tUIPC_CH_ID ch_id, |
| tUIPC_EVENT event) { |
| APPL_TRACE_WARNING("%s: BTIF MEDIA (A2DP-DATA) EVENT %s", __func__, |
| dump_uipc_event(event)); |
| |
| switch (event) { |
| case UIPC_OPEN_EVT: |
| /* |
| * Read directly from media task from here on (keep callback for |
| * connection events. |
| */ |
| UIPC_Ioctl(*a2dp_uipc, UIPC_CH_ID_AV_AUDIO, |
| UIPC_REG_REMOVE_ACTIVE_READSET, NULL); |
| UIPC_Ioctl(*a2dp_uipc, UIPC_CH_ID_AV_AUDIO, UIPC_SET_READ_POLL_TMO, |
| reinterpret_cast<void*>(A2DP_DATA_READ_POLL_MS)); |
| |
| // Will start audio on btif_a2dp_on_started |
| |
| /* ACK back when media task is fully started */ |
| break; |
| |
| case UIPC_CLOSE_EVT: |
| /* |
| * Send stop request only if we are actively streaming and haven't |
| * received a stop request. Potentially, the audioflinger detached |
| * abnormally. |
| */ |
| if (btif_a2dp_source_is_streaming()) { |
| /* Post stop event and wait for audio path to stop */ |
| btif_av_stream_stop(RawAddress::kEmpty); |
| } |
| break; |
| |
| default: |
| APPL_TRACE_ERROR("%s: ### A2DP-DATA EVENT %d NOT HANDLED ###", __func__, |
| event); |
| break; |
| } |
| } |
| |
| // If A2DP_HOST_DATA_GROUP exists we expect audio server and BT both are |
| // in this group therefore have access to A2DP socket. Otherwise audio |
| // server should be in the same group that BT stack runs with to access |
| // A2DP socket. |
| static void a2dp_data_path_open() { |
| UIPC_Open(*a2dp_uipc, UIPC_CH_ID_AV_AUDIO, btif_a2dp_data_cb, |
| A2DP_HOST_DATA_PATH); |
| struct group* grp = getgrnam(A2DP_HOST_DATA_GROUP); |
| chmod(A2DP_HOST_DATA_PATH, 0770); |
| if (grp) { |
| int res = chown(A2DP_HOST_DATA_PATH, -1, grp->gr_gid); |
| if (res == -1) { |
| LOG(ERROR) << __func__ << " failed: " << strerror(errno); |
| } |
| } |
| } |
| |
| tA2DP_CTRL_CMD a2dp_pending_cmd_ = A2DP_CTRL_CMD_NONE; |
| uint64_t total_bytes_read_; |
| timespec data_position_; |
| uint16_t remote_delay_report_; |
| |
| } // namespace |
| |
| namespace bluetooth { |
| namespace audio { |
| namespace a2dp { |
| |
| // Invoked by audio server to set audio config (PCM for now) |
| bool SetAudioConfig(AudioConfig config) { |
| btav_a2dp_codec_config_t codec_config; |
| codec_config.sample_rate = config.sample_rate; |
| codec_config.bits_per_sample = config.bits_per_sample; |
| codec_config.channel_mode = config.channel_mode; |
| btif_a2dp_source_feeding_update_req(codec_config); |
| return true; |
| } |
| |
| // Invoked by audio server when it has audio data to stream. |
| bool StartRequest() { |
| // Check if a previous request is not finished |
| if (a2dp_pending_cmd_ == A2DP_CTRL_CMD_START) { |
| LOG(INFO) << __func__ << ": A2DP_CTRL_CMD_START in progress"; |
| return false; |
| } else if (a2dp_pending_cmd_ != A2DP_CTRL_CMD_NONE) { |
| LOG(WARNING) << __func__ << ": busy in pending_cmd=" << a2dp_pending_cmd_; |
| return false; |
| } |
| |
| // Don't send START request to stack while we are in a call |
| if (!bluetooth::headset::IsCallIdle()) { |
| LOG(ERROR) << __func__ << ": call state is busy"; |
| return false; |
| } |
| |
| if (btif_av_stream_started_ready()) { |
| // Already started, ACK back immediately. |
| a2dp_data_path_open(); |
| return true; |
| } |
| if (btif_av_stream_ready()) { |
| a2dp_data_path_open(); |
| /* |
| * 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. |
| */ |
| a2dp_pending_cmd_ = A2DP_CTRL_CMD_START; |
| btif_av_stream_start(); |
| if (btif_av_get_peer_sep() != AVDT_TSEP_SRC) { |
| LOG(INFO) << __func__ << ": accepted"; |
| return false; // TODO: should be pending |
| } |
| a2dp_pending_cmd_ = A2DP_CTRL_CMD_NONE; |
| return true; |
| } |
| LOG(ERROR) << __func__ << ": AV stream is not ready to start"; |
| return false; |
| } |
| |
| // Invoked by audio server when audio streaming is done. |
| bool StopRequest() { |
| if (btif_av_get_peer_sep() == AVDT_TSEP_SNK && |
| !btif_av_stream_started_ready()) { |
| btif_av_clear_remote_suspend_flag(); |
| return true; |
| } |
| LOG(INFO) << __func__ << ": handling"; |
| a2dp_pending_cmd_ = A2DP_CTRL_CMD_STOP; |
| btif_av_stream_stop(RawAddress::kEmpty); |
| return true; |
| } |
| |
| // Invoked by audio server to check audio presentation position periodically. |
| PresentationPosition GetPresentationPosition() { |
| PresentationPosition presentation_position{ |
| .remote_delay_report_ns = remote_delay_report_ * 100000u, |
| .total_bytes_read = total_bytes_read_, |
| .data_position = data_position_, |
| }; |
| return presentation_position; |
| } |
| |
| // delay reports from AVDTP is based on 1/10 ms (100us) |
| void set_remote_delay(uint16_t delay_report) { |
| remote_delay_report_ = delay_report; |
| } |
| |
| // Inform audio server about offloading codec; not used for now |
| bool update_codec_offloading_capabilities( |
| const std::vector<btav_a2dp_codec_config_t>& framework_preference) { |
| return false; |
| } |
| |
| // Checking if new bluetooth_audio is enabled |
| bool is_hal_2_0_enabled() { return true; } |
| |
| // Check if new bluetooth_audio is running with offloading encoders |
| bool is_hal_2_0_offloading() { return false; } |
| |
| // Initialize BluetoothAudio HAL: openProvider |
| bool init(bluetooth::common::MessageLoopThread* message_loop) { |
| a2dp_uipc = UIPC_Init(); |
| total_bytes_read_ = 0; |
| data_position_ = {}; |
| remote_delay_report_ = 0; |
| |
| return true; |
| } |
| |
| // Clean up BluetoothAudio HAL |
| void cleanup() { |
| end_session(); |
| |
| if (a2dp_uipc != nullptr) { |
| UIPC_Close(*a2dp_uipc, UIPC_CH_ID_ALL); |
| } |
| } |
| |
| // Set up the codec into BluetoothAudio HAL |
| bool setup_codec() { |
| // TODO: setup codec |
| return true; |
| } |
| |
| void start_session() { |
| // TODO: Notify server; or do we handle it during connected? |
| } |
| |
| void end_session() { |
| // TODO: Notify server; or do we handle it during disconnected? |
| } |
| |
| void ack_stream_started(const tA2DP_CTRL_ACK& ack) { |
| a2dp_pending_cmd_ = A2DP_CTRL_CMD_NONE; |
| // TODO: Notify server |
| } |
| |
| void ack_stream_suspended(const tA2DP_CTRL_ACK& ack) { |
| a2dp_pending_cmd_ = A2DP_CTRL_CMD_NONE; |
| // TODO: Notify server |
| } |
| |
| // Read from the FMQ of BluetoothAudio HAL |
| size_t read(uint8_t* p_buf, uint32_t len) { |
| if (a2dp_uipc == nullptr) { |
| return 0; |
| } |
| return UIPC_Read(*a2dp_uipc, UIPC_CH_ID_AV_AUDIO, p_buf, len); |
| } |
| |
| } // namespace a2dp |
| } // namespace audio |
| } // namespace bluetooth |