1 /*
2 * Copyright 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "bluetooth-a2dp"
18
19 #include "btif/include/bta_av_co_peer.h"
20
21 #include <bluetooth/log.h>
22
23 #include <cstddef>
24 #include <cstdint>
25 #include <cstring>
26 #include <mutex>
27 #include <vector>
28
29 #include "bta/include/bta_av_api.h"
30 #include "hardware/bt_av.h"
31 #include "stack/include/a2dp_codec_api.h"
32 #include "stack/include/avdt_api.h"
33 #include "stack/include/bt_types.h"
34 #include "types/raw_address.h"
35
36 using namespace bluetooth;
37
38 // Macro to convert BTA AV audio handle to index and vice versa
39 #define BTA_AV_CO_AUDIO_HANDLE_TO_INDEX(bta_av_handle) (((bta_av_handle) & (~BTA_AV_CHNL_MSK)) - 1)
40 #define BTA_AV_CO_AUDIO_INDEX_TO_HANDLE(index) (((index) + 1) | BTA_AV_CHNL_AUDIO)
41
BtaAvCoPeer()42 BtaAvCoPeer::BtaAvCoPeer()
43 : addr(RawAddress::kEmpty),
44 num_sinks(0),
45 num_sources(0),
46 num_seps(0),
47 num_rx_sinks(0),
48 num_rx_sources(0),
49 num_sup_sinks(0),
50 num_sup_sources(0),
51 p_sink(nullptr),
52 p_source(nullptr),
53 codec_config{},
54 acceptor(false),
55 reconfig_needed(false),
56 opened(false),
57 mtu(0),
58 uuid_to_connect(0),
59 bta_av_handle_(0),
60 codecs_(nullptr),
61 content_protect_active_(false) {
62 Reset(0);
63 }
64
Init(const std::vector<btav_a2dp_codec_config_t> & codec_priorities)65 void BtaAvCoPeer::Init(const std::vector<btav_a2dp_codec_config_t>& codec_priorities) {
66 Reset(bta_av_handle_);
67 // Reset the current config
68 codecs_ = new A2dpCodecs(codec_priorities);
69 codecs_->init();
70 A2DP_InitDefaultCodec(codec_config);
71 }
72
Reset(tBTA_AV_HNDL bta_av_handle)73 void BtaAvCoPeer::Reset(tBTA_AV_HNDL bta_av_handle) {
74 addr = RawAddress::kEmpty;
75 for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(sinks); i++) {
76 BtaAvCoSep& sink = sinks[i];
77 sink.Reset();
78 }
79 for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(sources); i++) {
80 BtaAvCoSep& source = sources[i];
81 source.Reset();
82 }
83 num_sinks = 0;
84 num_sources = 0;
85 num_seps = 0;
86 num_rx_sinks = 0;
87 num_rx_sources = 0;
88 num_sup_sinks = 0;
89 num_sup_sources = 0;
90 p_sink = nullptr;
91 p_source = nullptr;
92 memset(codec_config, 0, sizeof(codec_config));
93 acceptor = false;
94 reconfig_needed = false;
95 opened = false;
96 mtu = 0;
97 uuid_to_connect = 0;
98
99 bta_av_handle_ = bta_av_handle;
100 delete codecs_;
101 codecs_ = nullptr;
102 content_protect_active_ = false;
103 }
104
getCodecConfig()105 uint8_t* BtaAvCoPeer::getCodecConfig() { return codec_config; }
106
setCodecConfig(const uint8_t * new_codec_config)107 void BtaAvCoPeer::setCodecConfig(const uint8_t* new_codec_config) {
108 memcpy(codec_config, new_codec_config, AVDT_CODEC_SIZE);
109 }
110
Init(const std::vector<btav_a2dp_codec_config_t> & codec_priorities)111 void BtaAvCoPeerCache::Init(const std::vector<btav_a2dp_codec_config_t>& codec_priorities) {
112 std::lock_guard<std::recursive_mutex> lock(codec_lock_);
113
114 codec_priorities_ = codec_priorities;
115
116 for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers_); i++) {
117 BtaAvCoPeer* p_peer = &peers_[i];
118 p_peer->Init(codec_priorities);
119 }
120 }
121
Reset()122 void BtaAvCoPeerCache::Reset() {
123 codec_priorities_.clear();
124
125 // Reset the peers and initialize the handles
126 for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers_); i++) {
127 BtaAvCoPeer* p_peer = &peers_[i];
128 p_peer->Reset(BTA_AV_CO_AUDIO_INDEX_TO_HANDLE(i));
129 }
130 }
131
FindPeer(const RawAddress & peer_address)132 BtaAvCoPeer* BtaAvCoPeerCache::FindPeer(const RawAddress& peer_address) {
133 for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers_); i++) {
134 BtaAvCoPeer* p_peer = &peers_[i];
135 if (p_peer->addr == peer_address) {
136 return p_peer;
137 }
138 }
139 return nullptr;
140 }
141
FindPeerSource(BtaAvCoPeer * p_peer,btav_a2dp_codec_index_t codec_index,const uint8_t content_protect_flag)142 BtaAvCoSep* BtaAvCoPeerCache::FindPeerSource(BtaAvCoPeer* p_peer,
143 btav_a2dp_codec_index_t codec_index,
144 const uint8_t content_protect_flag) {
145 if (codec_index == BTAV_A2DP_CODEC_INDEX_MAX) {
146 log::warn("invalid codec index for peer {}", p_peer->addr);
147 return nullptr;
148 }
149
150 // Find the peer Source for the codec
151 for (size_t index = 0; index < p_peer->num_sup_sources; index++) {
152 BtaAvCoSep* p_source = &p_peer->sources[index];
153 btav_a2dp_codec_index_t peer_codec_index = A2DP_SinkCodecIndex(p_source->codec_caps);
154 if (peer_codec_index != codec_index) {
155 continue;
156 }
157 if (!AudioSepHasContentProtection(p_source, content_protect_flag)) {
158 log::verbose("peer Source for codec {} does not support Content Protection",
159 A2DP_CodecIndexStr(codec_index));
160 continue;
161 }
162 return p_source;
163 }
164 return nullptr;
165 }
166
FindPeerSink(BtaAvCoPeer * p_peer,btav_a2dp_codec_index_t codec_index,const uint8_t content_protect_flag)167 BtaAvCoSep* BtaAvCoPeerCache::FindPeerSink(BtaAvCoPeer* p_peer, btav_a2dp_codec_index_t codec_index,
168 const uint8_t content_protect_flag) {
169 if (codec_index == BTAV_A2DP_CODEC_INDEX_MAX) {
170 log::warn("invalid codec index for peer {}", p_peer->addr);
171 return nullptr;
172 }
173
174 // Find the peer Sink for the codec
175 for (size_t index = 0; index < p_peer->num_sup_sinks; index++) {
176 BtaAvCoSep* p_sink = &p_peer->sinks[index];
177 btav_a2dp_codec_index_t peer_codec_index = A2DP_SourceCodecIndex(p_sink->codec_caps);
178 if (peer_codec_index != codec_index) {
179 continue;
180 }
181 if (!AudioSepHasContentProtection(p_sink, content_protect_flag)) {
182 log::warn("invalid codec index for peer {}", p_peer->addr);
183 continue;
184 }
185 return p_sink;
186 }
187 return nullptr;
188 }
189
FindPeer(tBTA_AV_HNDL bta_av_handle)190 BtaAvCoPeer* BtaAvCoPeerCache::FindPeer(tBTA_AV_HNDL bta_av_handle) {
191 uint8_t index;
192
193 index = BTA_AV_CO_AUDIO_HANDLE_TO_INDEX(bta_av_handle);
194
195 log::verbose("bta_av_handle = 0x{:x} index = {}", bta_av_handle, index);
196
197 // Sanity check
198 if (index >= BTA_AV_CO_NUM_ELEMENTS(peers_)) {
199 log::error("peer index {} for BTA AV handle 0x{:x} is out of bounds", index, bta_av_handle);
200 return nullptr;
201 }
202
203 return &peers_[index];
204 }
205
FindPeerAndUpdate(tBTA_AV_HNDL bta_av_handle,const RawAddress & peer_address)206 BtaAvCoPeer* BtaAvCoPeerCache::FindPeerAndUpdate(tBTA_AV_HNDL bta_av_handle,
207 const RawAddress& peer_address) {
208 log::verbose("peer {} bta_av_handle = 0x{:x}", peer_address, bta_av_handle);
209
210 BtaAvCoPeer* p_peer = FindPeer(bta_av_handle);
211 if (p_peer == nullptr) {
212 log::error("peer entry for BTA AV handle 0x{:x} peer {} not found", bta_av_handle,
213 peer_address);
214 return nullptr;
215 }
216
217 log::verbose("peer {} bta_av_handle = 0x{:x} previous address {}", peer_address, bta_av_handle,
218 p_peer->addr);
219 p_peer->addr = peer_address;
220 return p_peer;
221 }
222
FindPeerUuid(tBTA_AV_HNDL bta_av_handle)223 uint16_t BtaAvCoPeerCache::FindPeerUuid(tBTA_AV_HNDL bta_av_handle) {
224 BtaAvCoPeer* p_peer = FindPeer(bta_av_handle);
225 if (p_peer == nullptr) {
226 return 0;
227 }
228 return p_peer->uuid_to_connect;
229 }
230
ContentProtectIsScmst(const uint8_t * p_protect_info)231 bool ContentProtectIsScmst(const uint8_t* p_protect_info) {
232 log::verbose("");
233
234 if (*p_protect_info >= AVDT_CP_LOSC) {
235 uint16_t cp_id;
236 p_protect_info++;
237 STREAM_TO_UINT16(cp_id, p_protect_info);
238 if (cp_id == AVDT_CP_SCMS_T_ID) {
239 log::verbose("SCMS-T found");
240 return true;
241 }
242 }
243 return false;
244 }
245
AudioProtectHasScmst(uint8_t num_protect,const uint8_t * p_protect_info)246 bool AudioProtectHasScmst(uint8_t num_protect, const uint8_t* p_protect_info) {
247 log::verbose("");
248 while (num_protect--) {
249 if (ContentProtectIsScmst(p_protect_info)) {
250 return true;
251 }
252 // Move to the next Content Protect schema
253 p_protect_info += *p_protect_info + 1;
254 }
255 log::verbose("SCMS-T not found");
256 return false;
257 }
258
AudioSepHasContentProtection(const BtaAvCoSep * p_sep,const uint8_t content_protect_flag)259 bool AudioSepHasContentProtection(const BtaAvCoSep* p_sep, const uint8_t content_protect_flag) {
260 log::verbose("");
261
262 // Check if content protection is enabled for this stream
263 if (content_protect_flag != AVDT_CP_SCMS_COPY_FREE) {
264 return AudioProtectHasScmst(p_sep->num_protect, p_sep->protect_info);
265 }
266
267 log::verbose("not required");
268 return true;
269 }
270