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 "bta/include/bta_av_api.h"
24
25 using namespace bluetooth;
26
27 // Macro to convert BTA AV audio handle to index and vice versa
28 #define BTA_AV_CO_AUDIO_HANDLE_TO_INDEX(bta_av_handle) \
29 (((bta_av_handle) & (~BTA_AV_CHNL_MSK)) - 1)
30 #define BTA_AV_CO_AUDIO_INDEX_TO_HANDLE(index) \
31 (((index) + 1) | BTA_AV_CHNL_AUDIO)
32
BtaAvCoPeer()33 BtaAvCoPeer::BtaAvCoPeer()
34 : addr(RawAddress::kEmpty),
35 num_sinks(0),
36 num_sources(0),
37 num_seps(0),
38 num_rx_sinks(0),
39 num_rx_sources(0),
40 num_sup_sinks(0),
41 num_sup_sources(0),
42 p_sink(nullptr),
43 p_source(nullptr),
44 codec_config{},
45 acceptor(false),
46 reconfig_needed(false),
47 opened(false),
48 mtu(0),
49 uuid_to_connect(0),
50 bta_av_handle_(0),
51 codecs_(nullptr),
52 content_protect_active_(false) {
53 Reset(0);
54 }
55
Init(const std::vector<btav_a2dp_codec_config_t> & codec_priorities)56 void BtaAvCoPeer::Init(
57 const std::vector<btav_a2dp_codec_config_t>& codec_priorities) {
58 Reset(bta_av_handle_);
59 // Reset the current config
60 codecs_ = new A2dpCodecs(codec_priorities);
61 codecs_->init();
62 A2DP_InitDefaultCodec(codec_config);
63 }
64
Reset(tBTA_AV_HNDL bta_av_handle)65 void BtaAvCoPeer::Reset(tBTA_AV_HNDL bta_av_handle) {
66 addr = RawAddress::kEmpty;
67 for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(sinks); i++) {
68 BtaAvCoSep& sink = sinks[i];
69 sink.Reset();
70 }
71 for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(sources); i++) {
72 BtaAvCoSep& source = sources[i];
73 source.Reset();
74 }
75 num_sinks = 0;
76 num_sources = 0;
77 num_seps = 0;
78 num_rx_sinks = 0;
79 num_rx_sources = 0;
80 num_sup_sinks = 0;
81 num_sup_sources = 0;
82 p_sink = nullptr;
83 p_source = nullptr;
84 memset(codec_config, 0, sizeof(codec_config));
85 acceptor = false;
86 reconfig_needed = false;
87 opened = false;
88 mtu = 0;
89 uuid_to_connect = 0;
90
91 bta_av_handle_ = bta_av_handle;
92 delete codecs_;
93 codecs_ = nullptr;
94 content_protect_active_ = false;
95 }
96
Init(const std::vector<btav_a2dp_codec_config_t> & codec_priorities,std::vector<btav_a2dp_codec_info_t> * supported_codecs)97 void BtaAvCoPeerCache::Init(
98 const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
99 std::vector<btav_a2dp_codec_info_t>* supported_codecs) {
100 std::lock_guard<std::recursive_mutex> lock(codec_lock_);
101
102 codec_priorities_ = codec_priorities;
103
104 for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers_); i++) {
105 BtaAvCoPeer* p_peer = &peers_[i];
106 p_peer->Init(codec_priorities);
107 }
108 }
109
Reset()110 void BtaAvCoPeerCache::Reset() {
111 codec_priorities_.clear();
112
113 // Reset the peers and initialize the handles
114 for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers_); i++) {
115 BtaAvCoPeer* p_peer = &peers_[i];
116 p_peer->Reset(BTA_AV_CO_AUDIO_INDEX_TO_HANDLE(i));
117 }
118 }
119
FindPeer(const RawAddress & peer_address)120 BtaAvCoPeer* BtaAvCoPeerCache::FindPeer(const RawAddress& peer_address) {
121 for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peers_); i++) {
122 BtaAvCoPeer* p_peer = &peers_[i];
123 if (p_peer->addr == peer_address) {
124 return p_peer;
125 }
126 }
127 return nullptr;
128 }
129
FindPeerSource(BtaAvCoPeer * p_peer,btav_a2dp_codec_index_t codec_index,const uint8_t content_protect_flag)130 BtaAvCoSep* BtaAvCoPeerCache::FindPeerSource(
131 BtaAvCoPeer* p_peer, btav_a2dp_codec_index_t codec_index,
132 const uint8_t content_protect_flag) {
133 if (codec_index == BTAV_A2DP_CODEC_INDEX_MAX) {
134 log::warn("invalid codec index for peer {}", p_peer->addr);
135 return nullptr;
136 }
137
138 // Find the peer Source for the codec
139 for (size_t index = 0; index < p_peer->num_sup_sources; index++) {
140 BtaAvCoSep* p_source = &p_peer->sources[index];
141 btav_a2dp_codec_index_t peer_codec_index =
142 A2DP_SinkCodecIndex(p_source->codec_caps);
143 if (peer_codec_index != codec_index) {
144 continue;
145 }
146 if (!AudioSepHasContentProtection(p_source, content_protect_flag)) {
147 log::verbose(
148 "peer Source for codec {} does not support Content Protection",
149 A2DP_CodecIndexStr(codec_index));
150 continue;
151 }
152 return p_source;
153 }
154 return nullptr;
155 }
156
FindPeerSink(BtaAvCoPeer * p_peer,btav_a2dp_codec_index_t codec_index,const uint8_t content_protect_flag)157 BtaAvCoSep* BtaAvCoPeerCache::FindPeerSink(BtaAvCoPeer* p_peer,
158 btav_a2dp_codec_index_t codec_index,
159 const uint8_t content_protect_flag) {
160 if (codec_index == BTAV_A2DP_CODEC_INDEX_MAX) {
161 log::warn("invalid codec index for peer {}", p_peer->addr);
162 return nullptr;
163 }
164
165 // Find the peer Sink for the codec
166 for (size_t index = 0; index < p_peer->num_sup_sinks; index++) {
167 BtaAvCoSep* p_sink = &p_peer->sinks[index];
168 btav_a2dp_codec_index_t peer_codec_index =
169 A2DP_SourceCodecIndex(p_sink->codec_caps);
170 if (peer_codec_index != codec_index) {
171 continue;
172 }
173 if (!AudioSepHasContentProtection(p_sink, content_protect_flag)) {
174 log::warn("invalid codec index for peer {}", p_peer->addr);
175 continue;
176 }
177 return p_sink;
178 }
179 return nullptr;
180 }
181
FindPeer(tBTA_AV_HNDL bta_av_handle)182 BtaAvCoPeer* BtaAvCoPeerCache::FindPeer(tBTA_AV_HNDL bta_av_handle) {
183 uint8_t index;
184
185 index = BTA_AV_CO_AUDIO_HANDLE_TO_INDEX(bta_av_handle);
186
187 log::verbose("bta_av_handle = 0x{:x} index = {}", bta_av_handle, index);
188
189 // Sanity check
190 if (index >= BTA_AV_CO_NUM_ELEMENTS(peers_)) {
191 log::error("peer index {} for BTA AV handle 0x{:x} is out of bounds", index,
192 bta_av_handle);
193 return nullptr;
194 }
195
196 return &peers_[index];
197 }
198
FindPeerAndUpdate(tBTA_AV_HNDL bta_av_handle,const RawAddress & peer_address)199 BtaAvCoPeer* BtaAvCoPeerCache::FindPeerAndUpdate(
200 tBTA_AV_HNDL bta_av_handle, const RawAddress& peer_address) {
201 log::verbose("peer {} bta_av_handle = 0x{:x}", peer_address, bta_av_handle);
202
203 BtaAvCoPeer* p_peer = FindPeer(bta_av_handle);
204 if (p_peer == nullptr) {
205 log::error("peer entry for BTA AV handle 0x{:x} peer {} not found",
206 bta_av_handle, peer_address);
207 return nullptr;
208 }
209
210 log::verbose("peer {} bta_av_handle = 0x{:x} previous address {}",
211 peer_address, bta_av_handle, p_peer->addr);
212 p_peer->addr = peer_address;
213 return p_peer;
214 }
215
FindPeerUuid(tBTA_AV_HNDL bta_av_handle)216 uint16_t BtaAvCoPeerCache::FindPeerUuid(tBTA_AV_HNDL bta_av_handle) {
217 BtaAvCoPeer* p_peer = FindPeer(bta_av_handle);
218 if (p_peer == nullptr) {
219 return 0;
220 }
221 return p_peer->uuid_to_connect;
222 }
223
ContentProtectIsScmst(const uint8_t * p_protect_info)224 bool ContentProtectIsScmst(const uint8_t* p_protect_info) {
225 log::verbose("");
226
227 if (*p_protect_info >= AVDT_CP_LOSC) {
228 uint16_t cp_id;
229 p_protect_info++;
230 STREAM_TO_UINT16(cp_id, p_protect_info);
231 if (cp_id == AVDT_CP_SCMS_T_ID) {
232 log::verbose("SCMS-T found");
233 return true;
234 }
235 }
236 return false;
237 }
238
AudioProtectHasScmst(uint8_t num_protect,const uint8_t * p_protect_info)239 bool AudioProtectHasScmst(uint8_t num_protect, const uint8_t* p_protect_info) {
240 log::verbose("");
241 while (num_protect--) {
242 if (ContentProtectIsScmst(p_protect_info)) return true;
243 // Move to the next Content Protect schema
244 p_protect_info += *p_protect_info + 1;
245 }
246 log::verbose("SCMS-T not found");
247 return false;
248 }
249
AudioSepHasContentProtection(const BtaAvCoSep * p_sep,const uint8_t content_protect_flag)250 bool AudioSepHasContentProtection(const BtaAvCoSep* p_sep,
251 const uint8_t content_protect_flag) {
252 log::verbose("");
253
254 // Check if content protection is enabled for this stream
255 if (content_protect_flag != AVDT_CP_SCMS_COPY_FREE) {
256 return AudioProtectHasScmst(p_sep->num_protect, p_sep->protect_info);
257 }
258
259 log::verbose("not required");
260 return true;
261 }
262