1 /******************************************************************************
2 *
3 * Copyright 1999-2012 Broadcom Corporation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18
19 /******************************************************************************
20 *
21 * This file contains SDP utility functions
22 *
23 ******************************************************************************/
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <utility>
28 #include <vector>
29
30 #include "bt_common.h"
31 #include "bt_types.h"
32 #include "btif_config.h"
33
34 #include "avrc_defs.h"
35 #include "sdp_api.h"
36 #include "sdpint.h"
37
38 #include "common/metrics.h"
39
40 using bluetooth::Uuid;
41 static const uint8_t sdp_base_uuid[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
42 0x10, 0x00, 0x80, 0x00, 0x00, 0x80,
43 0x5F, 0x9B, 0x34, 0xFB};
44
45 template <typename T>
to_little_endian_array(T x)46 static std::array<char, sizeof(T)> to_little_endian_array(T x) {
47 static_assert(std::is_integral<T>::value,
48 "to_little_endian_array parameter must be integral.");
49 std::array<char, sizeof(T)> array = {};
50 for (size_t i = 0; i < array.size(); i++) {
51 array[i] = static_cast<char>((x >> (8 * i)) & 0xFF);
52 }
53 return array;
54 }
55
56 /**
57 * Find the list of profile versions from Bluetooth Profile Descriptor list
58 * attribute in a SDP record
59 *
60 * @param p_rec SDP record to search
61 * @return a vector of <UUID, VERSION> pairs, empty if not found
62 */
sdpu_find_profile_version(tSDP_DISC_REC * p_rec)63 static std::vector<std::pair<uint16_t, uint16_t>> sdpu_find_profile_version(
64 tSDP_DISC_REC* p_rec) {
65 std::vector<std::pair<uint16_t, uint16_t>> result;
66 for (tSDP_DISC_ATTR* p_attr = p_rec->p_first_attr; p_attr != nullptr;
67 p_attr = p_attr->p_next_attr) {
68 // Find the profile descriptor list */
69 if (p_attr->attr_id != ATTR_ID_BT_PROFILE_DESC_LIST ||
70 SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) {
71 continue;
72 }
73 // Walk through the protocol descriptor list
74 for (tSDP_DISC_ATTR* p_sattr = p_attr->attr_value.v.p_sub_attr;
75 p_sattr != nullptr; p_sattr = p_sattr->p_next_attr) {
76 // Safety check - each entry should itself be a sequence
77 if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) !=
78 DATA_ELE_SEQ_DESC_TYPE) {
79 LOG(WARNING) << __func__ << ": Descriptor type is not sequence: "
80 << loghex(SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type));
81 return std::vector<std::pair<uint16_t, uint16_t>>();
82 }
83 // Now, see if the entry contains the profile UUID we are interested in
84 for (tSDP_DISC_ATTR* p_ssattr = p_sattr->attr_value.v.p_sub_attr;
85 p_ssattr != nullptr; p_ssattr = p_ssattr->p_next_attr) {
86 if (SDP_DISC_ATTR_TYPE(p_ssattr->attr_len_type) != UUID_DESC_TYPE ||
87 SDP_DISC_ATTR_LEN(p_ssattr->attr_len_type) != 2) {
88 continue;
89 }
90 uint16_t uuid = p_ssattr->attr_value.v.u16;
91 // Next attribute should be the version attribute
92 tSDP_DISC_ATTR* version_attr = p_ssattr->p_next_attr;
93 if (SDP_DISC_ATTR_TYPE(version_attr->attr_len_type) != UINT_DESC_TYPE ||
94 SDP_DISC_ATTR_LEN(version_attr->attr_len_type) != 2) {
95 LOG(WARNING) << __func__ << ": Bad version type "
96 << loghex(
97 SDP_DISC_ATTR_TYPE(version_attr->attr_len_type))
98 << ", or length "
99 << SDP_DISC_ATTR_LEN(version_attr->attr_len_type);
100 return std::vector<std::pair<uint16_t, uint16_t>>();
101 }
102 // High order 8 bits is the major number, low order is the
103 // minor number (big endian)
104 uint16_t version = version_attr->attr_value.v.u16;
105 result.emplace_back(uuid, version);
106 }
107 }
108 }
109 return result;
110 }
111
112 /**
113 * Find the most specific 16-bit service uuid represented by a SDP record
114 *
115 * @param p_rec pointer to a SDP record
116 * @return most specific 16-bit service uuid, 0 if not found
117 */
sdpu_find_most_specific_service_uuid(tSDP_DISC_REC * p_rec)118 static uint16_t sdpu_find_most_specific_service_uuid(tSDP_DISC_REC* p_rec) {
119 for (tSDP_DISC_ATTR* p_attr = p_rec->p_first_attr; p_attr != nullptr;
120 p_attr = p_attr->p_next_attr) {
121 if (p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST &&
122 SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE) {
123 tSDP_DISC_ATTR* p_first_attr = p_attr->attr_value.v.p_sub_attr;
124 if (SDP_DISC_ATTR_TYPE(p_first_attr->attr_len_type) == UUID_DESC_TYPE &&
125 SDP_DISC_ATTR_LEN(p_first_attr->attr_len_type) == 2) {
126 return p_first_attr->attr_value.v.u16;
127 } else if (SDP_DISC_ATTR_TYPE(p_first_attr->attr_len_type) ==
128 DATA_ELE_SEQ_DESC_TYPE) {
129 // Workaround for Toyota G Block car kit:
130 // It incorrectly puts an extra data element sequence in this attribute
131 for (tSDP_DISC_ATTR* p_extra_sattr =
132 p_first_attr->attr_value.v.p_sub_attr;
133 p_extra_sattr != nullptr;
134 p_extra_sattr = p_extra_sattr->p_next_attr) {
135 // Return the first UUID data element
136 if (SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) ==
137 UUID_DESC_TYPE &&
138 SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2) {
139 return p_extra_sattr->attr_value.v.u16;
140 }
141 }
142 } else {
143 LOG(WARNING) << __func__ << ": Bad Service Class ID list attribute";
144 return 0;
145 }
146 } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) {
147 if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE &&
148 SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2) {
149 return p_attr->attr_value.v.u16;
150 }
151 }
152 }
153 return 0;
154 }
155
sdpu_log_attribute_metrics(const RawAddress & bda,tSDP_DISCOVERY_DB * p_db)156 void sdpu_log_attribute_metrics(const RawAddress& bda,
157 tSDP_DISCOVERY_DB* p_db) {
158 CHECK_NE(p_db, nullptr);
159 bool has_di_record = false;
160 for (tSDP_DISC_REC* p_rec = p_db->p_first_rec; p_rec != nullptr;
161 p_rec = p_rec->p_next_rec) {
162 uint16_t service_uuid = sdpu_find_most_specific_service_uuid(p_rec);
163 if (service_uuid == 0) {
164 LOG(INFO) << __func__ << ": skipping record without service uuid " << bda;
165 continue;
166 }
167 // Log the existence of a profile role
168 // This can be different from Bluetooth Profile Descriptor List
169 bluetooth::common::LogSdpAttribute(bda, service_uuid, 0, 0, nullptr);
170 // Log profile version from Bluetooth Profile Descriptor List
171 auto uuid_version_array = sdpu_find_profile_version(p_rec);
172 for (const auto& uuid_version_pair : uuid_version_array) {
173 uint16_t profile_uuid = uuid_version_pair.first;
174 uint16_t version = uuid_version_pair.second;
175 auto version_array = to_little_endian_array(version);
176 bluetooth::common::LogSdpAttribute(
177 bda, profile_uuid, ATTR_ID_BT_PROFILE_DESC_LIST, version_array.size(),
178 version_array.data());
179 }
180 // Log protocol version from Protocol Descriptor List
181 uint16_t protocol_uuid = 0;
182 switch (service_uuid) {
183 case UUID_SERVCLASS_AUDIO_SOURCE:
184 case UUID_SERVCLASS_AUDIO_SINK:
185 protocol_uuid = UUID_PROTOCOL_AVDTP;
186 break;
187 case UUID_SERVCLASS_AV_REMOTE_CONTROL:
188 case UUID_SERVCLASS_AV_REM_CTRL_CONTROL:
189 case UUID_SERVCLASS_AV_REM_CTRL_TARGET:
190 protocol_uuid = UUID_PROTOCOL_AVCTP;
191 break;
192 case UUID_SERVCLASS_PANU:
193 case UUID_SERVCLASS_GN:
194 protocol_uuid = UUID_PROTOCOL_BNEP;
195 break;
196 }
197 if (protocol_uuid != 0) {
198 tSDP_PROTOCOL_ELEM protocol_elements = {};
199 if (SDP_FindProtocolListElemInRec(p_rec, protocol_uuid,
200 &protocol_elements)) {
201 if (protocol_elements.num_params >= 1) {
202 uint16_t version = protocol_elements.params[0];
203 auto version_array = to_little_endian_array(version);
204 bluetooth::common::LogSdpAttribute(
205 bda, protocol_uuid, ATTR_ID_PROTOCOL_DESC_LIST,
206 version_array.size(), version_array.data());
207 }
208 }
209 }
210 // Log profile supported features from various supported feature attributes
211 switch (service_uuid) {
212 case UUID_SERVCLASS_AG_HANDSFREE:
213 case UUID_SERVCLASS_HF_HANDSFREE:
214 case UUID_SERVCLASS_AV_REMOTE_CONTROL:
215 case UUID_SERVCLASS_AV_REM_CTRL_CONTROL:
216 case UUID_SERVCLASS_AV_REM_CTRL_TARGET:
217 case UUID_SERVCLASS_AUDIO_SOURCE:
218 case UUID_SERVCLASS_AUDIO_SINK: {
219 tSDP_DISC_ATTR* p_attr =
220 SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES);
221 if (p_attr == nullptr) {
222 break;
223 }
224 uint16_t supported_features = p_attr->attr_value.v.u16;
225 auto version_array = to_little_endian_array(supported_features);
226 bluetooth::common::LogSdpAttribute(
227 bda, service_uuid, ATTR_ID_SUPPORTED_FEATURES, version_array.size(),
228 version_array.data());
229 break;
230 }
231 case UUID_SERVCLASS_MESSAGE_NOTIFICATION:
232 case UUID_SERVCLASS_MESSAGE_ACCESS: {
233 tSDP_DISC_ATTR* p_attr =
234 SDP_FindAttributeInRec(p_rec, ATTR_ID_MAP_SUPPORTED_FEATURES);
235 if (p_attr == nullptr) {
236 break;
237 }
238 uint32_t map_supported_features = p_attr->attr_value.v.u32;
239 auto features_array = to_little_endian_array(map_supported_features);
240 bluetooth::common::LogSdpAttribute(
241 bda, service_uuid, ATTR_ID_MAP_SUPPORTED_FEATURES,
242 features_array.size(), features_array.data());
243 break;
244 }
245 case UUID_SERVCLASS_PBAP_PCE:
246 case UUID_SERVCLASS_PBAP_PSE: {
247 tSDP_DISC_ATTR* p_attr =
248 SDP_FindAttributeInRec(p_rec, ATTR_ID_PBAP_SUPPORTED_FEATURES);
249 if (p_attr == nullptr) {
250 break;
251 }
252 uint32_t pbap_supported_features = p_attr->attr_value.v.u32;
253 auto features_array = to_little_endian_array(pbap_supported_features);
254 bluetooth::common::LogSdpAttribute(
255 bda, service_uuid, ATTR_ID_PBAP_SUPPORTED_FEATURES,
256 features_array.size(), features_array.data());
257 break;
258 }
259 }
260 if (service_uuid == UUID_SERVCLASS_PNP_INFORMATION) {
261 has_di_record = true;
262 }
263 }
264 // Log the first DI record if there is one
265 if (has_di_record) {
266 tSDP_DI_GET_RECORD di_record = {};
267 if (SDP_GetDiRecord(1, &di_record, p_db) == SDP_SUCCESS) {
268 auto version_array = to_little_endian_array(di_record.spec_id);
269 bluetooth::common::LogSdpAttribute(
270 bda, UUID_SERVCLASS_PNP_INFORMATION, ATTR_ID_SPECIFICATION_ID,
271 version_array.size(), version_array.data());
272 std::stringstream ss;
273 // [N - native]::SDP::[DIP - Device ID Profile]
274 ss << "N:SDP::DIP::" << loghex(di_record.rec.vendor_id_source);
275 bluetooth::common::LogManufacturerInfo(
276 bda, android::bluetooth::DeviceInfoSrcEnum::DEVICE_INFO_INTERNAL,
277 ss.str(), loghex(di_record.rec.vendor), loghex(di_record.rec.product),
278 loghex(di_record.rec.version), "");
279
280 std::string bda_string = bda.ToString();
281 // write manufacturer, model, HW version to config
282 btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_MANUFACTURER,
283 di_record.rec.vendor);
284 btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_MODEL,
285 di_record.rec.product);
286 btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_HW_VERSION,
287 di_record.rec.version);
288 btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_VENDOR_ID_SRC,
289 di_record.rec.vendor_id_source);
290 }
291 }
292 }
293
294 /*******************************************************************************
295 *
296 * Function sdpu_find_ccb_by_cid
297 *
298 * Description This function searches the CCB table for an entry with the
299 * passed CID.
300 *
301 * Returns the CCB address, or NULL if not found.
302 *
303 ******************************************************************************/
sdpu_find_ccb_by_cid(uint16_t cid)304 tCONN_CB* sdpu_find_ccb_by_cid(uint16_t cid) {
305 uint16_t xx;
306 tCONN_CB* p_ccb;
307
308 /* Look through each connection control block */
309 for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++) {
310 if ((p_ccb->con_state != SDP_STATE_IDLE) && (p_ccb->connection_id == cid))
311 return (p_ccb);
312 }
313
314 /* If here, not found */
315 return (NULL);
316 }
317
318 /*******************************************************************************
319 *
320 * Function sdpu_find_ccb_by_db
321 *
322 * Description This function searches the CCB table for an entry with the
323 * passed discovery db.
324 *
325 * Returns the CCB address, or NULL if not found.
326 *
327 ******************************************************************************/
sdpu_find_ccb_by_db(tSDP_DISCOVERY_DB * p_db)328 tCONN_CB* sdpu_find_ccb_by_db(tSDP_DISCOVERY_DB* p_db) {
329 uint16_t xx;
330 tCONN_CB* p_ccb;
331
332 if (p_db) {
333 /* Look through each connection control block */
334 for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++) {
335 if ((p_ccb->con_state != SDP_STATE_IDLE) && (p_ccb->p_db == p_db))
336 return (p_ccb);
337 }
338 }
339 /* If here, not found */
340 return (NULL);
341 }
342
343 /*******************************************************************************
344 *
345 * Function sdpu_allocate_ccb
346 *
347 * Description This function allocates a new CCB.
348 *
349 * Returns CCB address, or NULL if none available.
350 *
351 ******************************************************************************/
sdpu_allocate_ccb(void)352 tCONN_CB* sdpu_allocate_ccb(void) {
353 uint16_t xx;
354 tCONN_CB* p_ccb;
355
356 /* Look through each connection control block for a free one */
357 for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++) {
358 if (p_ccb->con_state == SDP_STATE_IDLE) {
359 alarm_t* alarm = p_ccb->sdp_conn_timer;
360 memset(p_ccb, 0, sizeof(tCONN_CB));
361 p_ccb->sdp_conn_timer = alarm;
362 return (p_ccb);
363 }
364 }
365
366 /* If here, no free CCB found */
367 return (NULL);
368 }
369
370 /*******************************************************************************
371 *
372 * Function sdpu_release_ccb
373 *
374 * Description This function releases a CCB.
375 *
376 * Returns void
377 *
378 ******************************************************************************/
sdpu_release_ccb(tCONN_CB * p_ccb)379 void sdpu_release_ccb(tCONN_CB* p_ccb) {
380 /* Ensure timer is stopped */
381 alarm_cancel(p_ccb->sdp_conn_timer);
382
383 /* Drop any response pointer we may be holding */
384 p_ccb->con_state = SDP_STATE_IDLE;
385 p_ccb->is_attr_search = false;
386
387 /* Free the response buffer */
388 if (p_ccb->rsp_list) SDP_TRACE_DEBUG("releasing SDP rsp_list");
389 osi_free_and_reset((void**)&p_ccb->rsp_list);
390 }
391
392 /*******************************************************************************
393 *
394 * Function sdpu_build_attrib_seq
395 *
396 * Description This function builds an attribute sequence from the list of
397 * passed attributes. It is also passed the address of the
398 * output buffer.
399 *
400 * Returns Pointer to next byte in the output buffer.
401 *
402 ******************************************************************************/
sdpu_build_attrib_seq(uint8_t * p_out,uint16_t * p_attr,uint16_t num_attrs)403 uint8_t* sdpu_build_attrib_seq(uint8_t* p_out, uint16_t* p_attr,
404 uint16_t num_attrs) {
405 uint16_t xx;
406
407 /* First thing is the data element header. See if the length fits 1 byte */
408 /* If no attributes, assume a 4-byte wildcard */
409 if (!p_attr)
410 xx = 5;
411 else
412 xx = num_attrs * 3;
413
414 if (xx > 255) {
415 UINT8_TO_BE_STREAM(p_out,
416 (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
417 UINT16_TO_BE_STREAM(p_out, xx);
418 } else {
419 UINT8_TO_BE_STREAM(p_out,
420 (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
421 UINT8_TO_BE_STREAM(p_out, xx);
422 }
423
424 /* If there are no attributes specified, assume caller wants wildcard */
425 if (!p_attr) {
426 UINT8_TO_BE_STREAM(p_out, (UINT_DESC_TYPE << 3) | SIZE_FOUR_BYTES);
427 UINT16_TO_BE_STREAM(p_out, 0);
428 UINT16_TO_BE_STREAM(p_out, 0xFFFF);
429 } else {
430 /* Loop through and put in all the attributes(s) */
431 for (xx = 0; xx < num_attrs; xx++, p_attr++) {
432 UINT8_TO_BE_STREAM(p_out, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
433 UINT16_TO_BE_STREAM(p_out, *p_attr);
434 }
435 }
436
437 return (p_out);
438 }
439
440 /*******************************************************************************
441 *
442 * Function sdpu_build_attrib_entry
443 *
444 * Description This function builds an attribute entry from the passed
445 * attribute record. It is also passed the address of the
446 * output buffer.
447 *
448 * Returns Pointer to next byte in the output buffer.
449 *
450 ******************************************************************************/
sdpu_build_attrib_entry(uint8_t * p_out,tSDP_ATTRIBUTE * p_attr)451 uint8_t* sdpu_build_attrib_entry(uint8_t* p_out, tSDP_ATTRIBUTE* p_attr) {
452 /* First, store the attribute ID. Goes as a UINT */
453 UINT8_TO_BE_STREAM(p_out, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
454 UINT16_TO_BE_STREAM(p_out, p_attr->id);
455
456 /* the attribute is in the db record.
457 * assuming the attribute len is less than SDP_MAX_ATTR_LEN */
458 switch (p_attr->type) {
459 case TEXT_STR_DESC_TYPE: /* 4 */
460 case DATA_ELE_SEQ_DESC_TYPE: /* 6 */
461 case DATA_ELE_ALT_DESC_TYPE: /* 7 */
462 case URL_DESC_TYPE: /* 8 */
463 #if (SDP_MAX_ATTR_LEN > 0xFFFF)
464 if (p_attr->len > 0xFFFF) {
465 UINT8_TO_BE_STREAM(p_out, (p_attr->type << 3) | SIZE_IN_NEXT_LONG);
466 UINT32_TO_BE_STREAM(p_out, p_attr->len);
467 } else
468 #endif /* 0xFFFF - 0xFF */
469 #if (SDP_MAX_ATTR_LEN > 0xFF)
470 if (p_attr->len > 0xFF) {
471 UINT8_TO_BE_STREAM(p_out, (p_attr->type << 3) | SIZE_IN_NEXT_WORD);
472 UINT16_TO_BE_STREAM(p_out, p_attr->len);
473 } else
474 #endif /* 0xFF and less*/
475 {
476 UINT8_TO_BE_STREAM(p_out, (p_attr->type << 3) | SIZE_IN_NEXT_BYTE);
477 UINT8_TO_BE_STREAM(p_out, p_attr->len);
478 }
479
480 if (p_attr->value_ptr != NULL) {
481 ARRAY_TO_BE_STREAM(p_out, p_attr->value_ptr, (int)p_attr->len);
482 }
483
484 return (p_out);
485 }
486
487 /* Now, store the attribute value */
488 switch (p_attr->len) {
489 case 1:
490 UINT8_TO_BE_STREAM(p_out, (p_attr->type << 3) | SIZE_ONE_BYTE);
491 break;
492 case 2:
493 UINT8_TO_BE_STREAM(p_out, (p_attr->type << 3) | SIZE_TWO_BYTES);
494 break;
495 case 4:
496 UINT8_TO_BE_STREAM(p_out, (p_attr->type << 3) | SIZE_FOUR_BYTES);
497 break;
498 case 8:
499 UINT8_TO_BE_STREAM(p_out, (p_attr->type << 3) | SIZE_EIGHT_BYTES);
500 break;
501 case 16:
502 UINT8_TO_BE_STREAM(p_out, (p_attr->type << 3) | SIZE_SIXTEEN_BYTES);
503 break;
504 default:
505 UINT8_TO_BE_STREAM(p_out, (p_attr->type << 3) | SIZE_IN_NEXT_BYTE);
506 UINT8_TO_BE_STREAM(p_out, p_attr->len);
507 break;
508 }
509
510 if (p_attr->value_ptr != NULL) {
511 ARRAY_TO_BE_STREAM(p_out, p_attr->value_ptr, (int)p_attr->len);
512 }
513
514 return (p_out);
515 }
516
517 /*******************************************************************************
518 *
519 * Function sdpu_build_n_send_error
520 *
521 * Description This function builds and sends an error packet.
522 *
523 * Returns void
524 *
525 ******************************************************************************/
sdpu_build_n_send_error(tCONN_CB * p_ccb,uint16_t trans_num,uint16_t error_code,char * p_error_text)526 void sdpu_build_n_send_error(tCONN_CB* p_ccb, uint16_t trans_num,
527 uint16_t error_code, char* p_error_text) {
528 uint8_t *p_rsp, *p_rsp_start, *p_rsp_param_len;
529 uint16_t rsp_param_len;
530 BT_HDR* p_buf = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE);
531
532 SDP_TRACE_WARNING("SDP - sdpu_build_n_send_error code: 0x%x CID: 0x%x",
533 error_code, p_ccb->connection_id);
534
535 /* Send the packet to L2CAP */
536 p_buf->offset = L2CAP_MIN_OFFSET;
537 p_rsp = p_rsp_start = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
538
539 UINT8_TO_BE_STREAM(p_rsp, SDP_PDU_ERROR_RESPONSE);
540 UINT16_TO_BE_STREAM(p_rsp, trans_num);
541
542 /* Skip the parameter length, we need to add it at the end */
543 p_rsp_param_len = p_rsp;
544 p_rsp += 2;
545
546 UINT16_TO_BE_STREAM(p_rsp, error_code);
547
548 /* Unplugfest example traces do not have any error text */
549 if (p_error_text)
550 ARRAY_TO_BE_STREAM(p_rsp, p_error_text, (int)strlen(p_error_text));
551
552 /* Go back and put the parameter length into the buffer */
553 rsp_param_len = p_rsp - p_rsp_param_len - 2;
554 UINT16_TO_BE_STREAM(p_rsp_param_len, rsp_param_len);
555
556 /* Set the length of the SDP data in the buffer */
557 p_buf->len = p_rsp - p_rsp_start;
558
559 /* Send the buffer through L2CAP */
560 L2CA_DataWrite(p_ccb->connection_id, p_buf);
561 }
562
563 /*******************************************************************************
564 *
565 * Function sdpu_extract_uid_seq
566 *
567 * Description This function extracts a UUID sequence from the passed input
568 * buffer, and puts it into the passed output list.
569 *
570 * Returns Pointer to next byte in the input buffer after the sequence.
571 *
572 ******************************************************************************/
sdpu_extract_uid_seq(uint8_t * p,uint16_t param_len,tSDP_UUID_SEQ * p_seq)573 uint8_t* sdpu_extract_uid_seq(uint8_t* p, uint16_t param_len,
574 tSDP_UUID_SEQ* p_seq) {
575 uint8_t* p_seq_end;
576 uint8_t descr, type, size;
577 uint32_t seq_len, uuid_len;
578
579 /* Assume none found */
580 p_seq->num_uids = 0;
581
582 /* A UID sequence is composed of a bunch of UIDs. */
583 if (sizeof(descr) > param_len) return (NULL);
584 param_len -= sizeof(descr);
585
586 BE_STREAM_TO_UINT8(descr, p);
587 type = descr >> 3;
588 size = descr & 7;
589
590 if (type != DATA_ELE_SEQ_DESC_TYPE) return (NULL);
591
592 switch (size) {
593 case SIZE_TWO_BYTES:
594 seq_len = 2;
595 break;
596 case SIZE_FOUR_BYTES:
597 seq_len = 4;
598 break;
599 case SIZE_SIXTEEN_BYTES:
600 seq_len = 16;
601 break;
602 case SIZE_IN_NEXT_BYTE:
603 if (sizeof(uint8_t) > param_len) return (NULL);
604 param_len -= sizeof(uint8_t);
605 BE_STREAM_TO_UINT8(seq_len, p);
606 break;
607 case SIZE_IN_NEXT_WORD:
608 if (sizeof(uint16_t) > param_len) return (NULL);
609 param_len -= sizeof(uint16_t);
610 BE_STREAM_TO_UINT16(seq_len, p);
611 break;
612 case SIZE_IN_NEXT_LONG:
613 if (sizeof(uint32_t) > param_len) return (NULL);
614 param_len -= sizeof(uint32_t);
615 BE_STREAM_TO_UINT32(seq_len, p);
616 break;
617 default:
618 return (NULL);
619 }
620
621 if (seq_len > param_len) return (NULL);
622
623 p_seq_end = p + seq_len;
624
625 /* Loop through, extracting the UIDs */
626 for (; p < p_seq_end;) {
627 BE_STREAM_TO_UINT8(descr, p);
628 type = descr >> 3;
629 size = descr & 7;
630
631 if (type != UUID_DESC_TYPE) return (NULL);
632
633 switch (size) {
634 case SIZE_TWO_BYTES:
635 uuid_len = 2;
636 break;
637 case SIZE_FOUR_BYTES:
638 uuid_len = 4;
639 break;
640 case SIZE_SIXTEEN_BYTES:
641 uuid_len = 16;
642 break;
643 case SIZE_IN_NEXT_BYTE:
644 if (p + sizeof(uint8_t) > p_seq_end) return NULL;
645 BE_STREAM_TO_UINT8(uuid_len, p);
646 break;
647 case SIZE_IN_NEXT_WORD:
648 if (p + sizeof(uint16_t) > p_seq_end) return NULL;
649 BE_STREAM_TO_UINT16(uuid_len, p);
650 break;
651 case SIZE_IN_NEXT_LONG:
652 if (p + sizeof(uint32_t) > p_seq_end) return NULL;
653 BE_STREAM_TO_UINT32(uuid_len, p);
654 break;
655 default:
656 return (NULL);
657 }
658
659 /* If UUID length is valid, copy it across */
660 if (((uuid_len == 2) || (uuid_len == 4) || (uuid_len == 16)) &&
661 (p + uuid_len <= p_seq_end)) {
662 p_seq->uuid_entry[p_seq->num_uids].len = (uint16_t)uuid_len;
663 BE_STREAM_TO_ARRAY(p, p_seq->uuid_entry[p_seq->num_uids].value,
664 (int)uuid_len);
665 p_seq->num_uids++;
666 } else
667 return (NULL);
668
669 /* We can only do so many */
670 if (p_seq->num_uids >= MAX_UUIDS_PER_SEQ) return (NULL);
671 }
672
673 if (p != p_seq_end) return (NULL);
674
675 return (p);
676 }
677
678 /*******************************************************************************
679 *
680 * Function sdpu_extract_attr_seq
681 *
682 * Description This function extracts an attribute sequence from the passed
683 * input buffer, and puts it into the passed output list.
684 *
685 * Returns Pointer to next byte in the input buffer after the sequence.
686 *
687 ******************************************************************************/
sdpu_extract_attr_seq(uint8_t * p,uint16_t param_len,tSDP_ATTR_SEQ * p_seq)688 uint8_t* sdpu_extract_attr_seq(uint8_t* p, uint16_t param_len,
689 tSDP_ATTR_SEQ* p_seq) {
690 uint8_t* p_end_list;
691 uint8_t descr, type, size;
692 uint32_t list_len, attr_len;
693
694 /* Assume none found */
695 p_seq->num_attr = 0;
696
697 /* Get attribute sequence info */
698 if (param_len < sizeof(descr)) return NULL;
699 param_len -= sizeof(descr);
700 BE_STREAM_TO_UINT8(descr, p);
701 type = descr >> 3;
702 size = descr & 7;
703
704 if (type != DATA_ELE_SEQ_DESC_TYPE) return NULL;
705
706 switch (size) {
707 case SIZE_IN_NEXT_BYTE:
708 if (param_len < sizeof(uint8_t)) return NULL;
709 param_len -= sizeof(uint8_t);
710 BE_STREAM_TO_UINT8(list_len, p);
711 break;
712
713 case SIZE_IN_NEXT_WORD:
714 if (param_len < sizeof(uint16_t)) return NULL;
715 param_len -= sizeof(uint16_t);
716 BE_STREAM_TO_UINT16(list_len, p);
717 break;
718
719 case SIZE_IN_NEXT_LONG:
720 if (param_len < sizeof(uint32_t)) return NULL;
721 param_len -= sizeof(uint32_t);
722 BE_STREAM_TO_UINT32(list_len, p);
723 break;
724
725 default:
726 return NULL;
727 }
728
729 if (list_len > param_len) return NULL;
730
731 p_end_list = p + list_len;
732
733 /* Loop through, extracting the attribute IDs */
734 for (; p < p_end_list;) {
735 BE_STREAM_TO_UINT8(descr, p);
736 type = descr >> 3;
737 size = descr & 7;
738
739 if (type != UINT_DESC_TYPE) return NULL;
740
741 switch (size) {
742 case SIZE_TWO_BYTES:
743 attr_len = 2;
744 break;
745 case SIZE_FOUR_BYTES:
746 attr_len = 4;
747 break;
748 case SIZE_IN_NEXT_BYTE:
749 if (p + sizeof(uint8_t) > p_end_list) return NULL;
750 BE_STREAM_TO_UINT8(attr_len, p);
751 break;
752 case SIZE_IN_NEXT_WORD:
753 if (p + sizeof(uint16_t) > p_end_list) return NULL;
754 BE_STREAM_TO_UINT16(attr_len, p);
755 break;
756 case SIZE_IN_NEXT_LONG:
757 if (p + sizeof(uint32_t) > p_end_list) return NULL;
758 BE_STREAM_TO_UINT32(attr_len, p);
759 break;
760 default:
761 return NULL;
762 break;
763 }
764
765 /* Attribute length must be 2-bytes or 4-bytes for a paired entry. */
766 if (p + attr_len > p_end_list) return NULL;
767 if (attr_len == 2) {
768 BE_STREAM_TO_UINT16(p_seq->attr_entry[p_seq->num_attr].start, p);
769 p_seq->attr_entry[p_seq->num_attr].end =
770 p_seq->attr_entry[p_seq->num_attr].start;
771 } else if (attr_len == 4) {
772 BE_STREAM_TO_UINT16(p_seq->attr_entry[p_seq->num_attr].start, p);
773 BE_STREAM_TO_UINT16(p_seq->attr_entry[p_seq->num_attr].end, p);
774 } else
775 return (NULL);
776
777 /* We can only do so many */
778 if (++p_seq->num_attr >= MAX_ATTR_PER_SEQ) return (NULL);
779 }
780
781 return (p);
782 }
783
784 /*******************************************************************************
785 *
786 * Function sdpu_get_len_from_type
787 *
788 * Description This function gets the length
789 *
790 * Returns void
791 *
792 ******************************************************************************/
sdpu_get_len_from_type(uint8_t * p,uint8_t * p_end,uint8_t type,uint32_t * p_len)793 uint8_t* sdpu_get_len_from_type(uint8_t* p, uint8_t* p_end, uint8_t type,
794 uint32_t* p_len) {
795 uint8_t u8;
796 uint16_t u16;
797 uint32_t u32;
798
799 switch (type & 7) {
800 case SIZE_ONE_BYTE:
801 *p_len = 1;
802 break;
803 case SIZE_TWO_BYTES:
804 *p_len = 2;
805 break;
806 case SIZE_FOUR_BYTES:
807 *p_len = 4;
808 break;
809 case SIZE_EIGHT_BYTES:
810 *p_len = 8;
811 break;
812 case SIZE_SIXTEEN_BYTES:
813 *p_len = 16;
814 break;
815 case SIZE_IN_NEXT_BYTE:
816 if (p + 1 > p_end) {
817 *p_len = 0;
818 return NULL;
819 }
820 BE_STREAM_TO_UINT8(u8, p);
821 *p_len = u8;
822 break;
823 case SIZE_IN_NEXT_WORD:
824 if (p + 2 > p_end) {
825 *p_len = 0;
826 return NULL;
827 }
828 BE_STREAM_TO_UINT16(u16, p);
829 *p_len = u16;
830 break;
831 case SIZE_IN_NEXT_LONG:
832 if (p + 4 > p_end) {
833 *p_len = 0;
834 return NULL;
835 }
836 BE_STREAM_TO_UINT32(u32, p);
837 *p_len = (uint16_t)u32;
838 break;
839 }
840
841 return (p);
842 }
843
844 /*******************************************************************************
845 *
846 * Function sdpu_is_base_uuid
847 *
848 * Description This function checks a 128-bit UUID with the base to see if
849 * it matches. Only the last 12 bytes are compared.
850 *
851 * Returns true if matched, else false
852 *
853 ******************************************************************************/
sdpu_is_base_uuid(uint8_t * p_uuid)854 bool sdpu_is_base_uuid(uint8_t* p_uuid) {
855 uint16_t xx;
856
857 for (xx = 4; xx < Uuid::kNumBytes128; xx++)
858 if (p_uuid[xx] != sdp_base_uuid[xx]) return (false);
859
860 /* If here, matched */
861 return (true);
862 }
863
864 /*******************************************************************************
865 *
866 * Function sdpu_compare_uuid_arrays
867 *
868 * Description This function compares 2 BE UUIDs. If needed, they are
869 * expanded to 128-bit UUIDs, then compared.
870 *
871 * NOTE it is assumed that the arrays are in Big Endian format
872 *
873 * Returns true if matched, else false
874 *
875 ******************************************************************************/
sdpu_compare_uuid_arrays(uint8_t * p_uuid1,uint32_t len1,uint8_t * p_uuid2,uint16_t len2)876 bool sdpu_compare_uuid_arrays(uint8_t* p_uuid1, uint32_t len1, uint8_t* p_uuid2,
877 uint16_t len2) {
878 uint8_t nu1[Uuid::kNumBytes128];
879 uint8_t nu2[Uuid::kNumBytes128];
880
881 if (((len1 != 2) && (len1 != 4) && (len1 != 16)) ||
882 ((len2 != 2) && (len2 != 4) && (len2 != 16))) {
883 SDP_TRACE_ERROR("%s: invalid length", __func__);
884 return false;
885 }
886
887 /* If lengths match, do a straight compare */
888 if (len1 == len2) {
889 if (len1 == 2)
890 return ((p_uuid1[0] == p_uuid2[0]) && (p_uuid1[1] == p_uuid2[1]));
891 if (len1 == 4)
892 return ((p_uuid1[0] == p_uuid2[0]) && (p_uuid1[1] == p_uuid2[1]) &&
893 (p_uuid1[2] == p_uuid2[2]) && (p_uuid1[3] == p_uuid2[3]));
894 else
895 return (memcmp(p_uuid1, p_uuid2, (size_t)len1) == 0);
896 } else if (len1 > len2) {
897 /* If the len1 was 4-byte, (so len2 is 2-byte), compare on the fly */
898 if (len1 == 4) {
899 return ((p_uuid1[0] == 0) && (p_uuid1[1] == 0) &&
900 (p_uuid1[2] == p_uuid2[0]) && (p_uuid1[3] == p_uuid2[1]));
901 } else {
902 /* Normalize UUIDs to 16-byte form, then compare. Len1 must be 16 */
903 memcpy(nu1, p_uuid1, Uuid::kNumBytes128);
904 memcpy(nu2, sdp_base_uuid, Uuid::kNumBytes128);
905
906 if (len2 == 4)
907 memcpy(nu2, p_uuid2, len2);
908 else if (len2 == 2)
909 memcpy(nu2 + 2, p_uuid2, len2);
910
911 return (memcmp(nu1, nu2, Uuid::kNumBytes128) == 0);
912 }
913 } else {
914 /* len2 is greater than len1 */
915 /* If the len2 was 4-byte, (so len1 is 2-byte), compare on the fly */
916 if (len2 == 4) {
917 return ((p_uuid2[0] == 0) && (p_uuid2[1] == 0) &&
918 (p_uuid2[2] == p_uuid1[0]) && (p_uuid2[3] == p_uuid1[1]));
919 } else {
920 /* Normalize UUIDs to 16-byte form, then compare. Len1 must be 16 */
921 memcpy(nu2, p_uuid2, Uuid::kNumBytes128);
922 memcpy(nu1, sdp_base_uuid, Uuid::kNumBytes128);
923
924 if (len1 == 4)
925 memcpy(nu1, p_uuid1, (size_t)len1);
926 else if (len1 == 2)
927 memcpy(nu1 + 2, p_uuid1, (size_t)len1);
928
929 return (memcmp(nu1, nu2, Uuid::kNumBytes128) == 0);
930 }
931 }
932 }
933
934 /*******************************************************************************
935 *
936 * Function sdpu_compare_uuid_with_attr
937 *
938 * Description This function compares a BT UUID structure with the UUID in
939 * an SDP attribute record. If needed, they are expanded to
940 * 128-bit UUIDs, then compared.
941 *
942 * NOTE - it is assumed that BT UUID structures are compressed to the
943 * smallest possible UUIDs (by removing the base SDP UUID).
944 * - it is also assumed that the discovery atribute is compressed
945 * to the smallest possible
946 *
947 * Returns true if matched, else false
948 *
949 ******************************************************************************/
sdpu_compare_uuid_with_attr(const Uuid & uuid,tSDP_DISC_ATTR * p_attr)950 bool sdpu_compare_uuid_with_attr(const Uuid& uuid, tSDP_DISC_ATTR* p_attr) {
951 int len = uuid.GetShortestRepresentationSize();
952 if (len == 2) return uuid.As16Bit() == p_attr->attr_value.v.u16;
953 if (len == 4) return uuid.As32Bit() == p_attr->attr_value.v.u32;
954 if (memcmp(uuid.To128BitBE().data(), (void*)p_attr->attr_value.v.array,
955 Uuid::kNumBytes128) == 0)
956 return (true);
957
958 return (false);
959 }
960
961 /*******************************************************************************
962 *
963 * Function sdpu_sort_attr_list
964 *
965 * Description sorts a list of attributes in numeric order from lowest to
966 * highest to conform to SDP specification
967 *
968 * Returns void
969 *
970 ******************************************************************************/
sdpu_sort_attr_list(uint16_t num_attr,tSDP_DISCOVERY_DB * p_db)971 void sdpu_sort_attr_list(uint16_t num_attr, tSDP_DISCOVERY_DB* p_db) {
972 uint16_t i;
973 uint16_t x;
974
975 /* Done if no attributes to sort */
976 if (num_attr <= 1) {
977 return;
978 } else if (num_attr > SDP_MAX_ATTR_FILTERS) {
979 num_attr = SDP_MAX_ATTR_FILTERS;
980 }
981
982 num_attr--; /* for the for-loop */
983 for (i = 0; i < num_attr;) {
984 if (p_db->attr_filters[i] > p_db->attr_filters[i + 1]) {
985 /* swap the attribute IDs and start from the beginning */
986 x = p_db->attr_filters[i];
987 p_db->attr_filters[i] = p_db->attr_filters[i + 1];
988 p_db->attr_filters[i + 1] = x;
989
990 i = 0;
991 } else
992 i++;
993 }
994 }
995
996 /*******************************************************************************
997 *
998 * Function sdpu_get_list_len
999 *
1000 * Description gets the total list length in the sdp database for a given
1001 * uid sequence and attr sequence
1002 *
1003 * Returns void
1004 *
1005 ******************************************************************************/
sdpu_get_list_len(tSDP_UUID_SEQ * uid_seq,tSDP_ATTR_SEQ * attr_seq)1006 uint16_t sdpu_get_list_len(tSDP_UUID_SEQ* uid_seq, tSDP_ATTR_SEQ* attr_seq) {
1007 tSDP_RECORD* p_rec;
1008 uint16_t len = 0;
1009 uint16_t len1;
1010
1011 for (p_rec = sdp_db_service_search(NULL, uid_seq); p_rec;
1012 p_rec = sdp_db_service_search(p_rec, uid_seq)) {
1013 len += 3;
1014
1015 len1 = sdpu_get_attrib_seq_len(p_rec, attr_seq);
1016
1017 if (len1 != 0)
1018 len += len1;
1019 else
1020 len -= 3;
1021 }
1022 return len;
1023 }
1024
1025 /*******************************************************************************
1026 *
1027 * Function sdpu_get_attrib_seq_len
1028 *
1029 * Description gets the length of the specific attributes in a given
1030 * sdp record
1031 *
1032 * Returns void
1033 *
1034 ******************************************************************************/
sdpu_get_attrib_seq_len(tSDP_RECORD * p_rec,tSDP_ATTR_SEQ * attr_seq)1035 uint16_t sdpu_get_attrib_seq_len(tSDP_RECORD* p_rec, tSDP_ATTR_SEQ* attr_seq) {
1036 tSDP_ATTRIBUTE* p_attr;
1037 uint16_t len1 = 0;
1038 uint16_t xx;
1039 bool is_range = false;
1040 uint16_t start_id = 0, end_id = 0;
1041
1042 for (xx = 0; xx < attr_seq->num_attr; xx++) {
1043 if (!is_range) {
1044 start_id = attr_seq->attr_entry[xx].start;
1045 end_id = attr_seq->attr_entry[xx].end;
1046 }
1047 p_attr = sdp_db_find_attr_in_rec(p_rec, start_id, end_id);
1048 if (p_attr) {
1049 len1 += sdpu_get_attrib_entry_len(p_attr);
1050
1051 /* If doing a range, stick with this one till no more attributes found */
1052 if (start_id != end_id) {
1053 /* Update for next time through */
1054 start_id = p_attr->id + 1;
1055 xx--;
1056 is_range = true;
1057 } else
1058 is_range = false;
1059 } else
1060 is_range = false;
1061 }
1062 return len1;
1063 }
1064
1065 /*******************************************************************************
1066 *
1067 * Function sdpu_get_attrib_entry_len
1068 *
1069 * Description gets the length of a specific attribute
1070 *
1071 * Returns void
1072 *
1073 ******************************************************************************/
sdpu_get_attrib_entry_len(tSDP_ATTRIBUTE * p_attr)1074 uint16_t sdpu_get_attrib_entry_len(tSDP_ATTRIBUTE* p_attr) {
1075 uint16_t len = 3;
1076
1077 /* the attribute is in the db record.
1078 * assuming the attribute len is less than SDP_MAX_ATTR_LEN */
1079 switch (p_attr->type) {
1080 case TEXT_STR_DESC_TYPE: /* 4 */
1081 case DATA_ELE_SEQ_DESC_TYPE: /* 6 */
1082 case DATA_ELE_ALT_DESC_TYPE: /* 7 */
1083 case URL_DESC_TYPE: /* 8 */
1084 #if (SDP_MAX_ATTR_LEN > 0xFFFF)
1085 if (p_attr->len > 0xFFFF) {
1086 len += 5;
1087 } else
1088 #endif /* 0xFFFF - 0xFF */
1089 #if (SDP_MAX_ATTR_LEN > 0xFF)
1090 if (p_attr->len > 0xFF) {
1091 len += 3;
1092 } else
1093 #endif /* 0xFF and less*/
1094 {
1095 len += 2;
1096 }
1097 len += p_attr->len;
1098 return len;
1099 }
1100
1101 /* Now, the attribute value */
1102 switch (p_attr->len) {
1103 case 1:
1104 case 2:
1105 case 4:
1106 case 8:
1107 case 16:
1108 len += 1;
1109 break;
1110 default:
1111 len += 2;
1112 break;
1113 }
1114
1115 len += p_attr->len;
1116 return len;
1117 }
1118
1119 /*******************************************************************************
1120 *
1121 * Function sdpu_build_partial_attrib_entry
1122 *
1123 * Description This function fills a buffer with partial attribute. It is
1124 * assumed that the maximum size of any attribute is 256 bytes.
1125 *
1126 * p_out: output buffer
1127 * p_attr: attribute to be copied partially into p_out
1128 * rem_len: num bytes to copy into p_out
1129 * offset: current start offset within the attr that needs to
1130 * be copied
1131 *
1132 * Returns Pointer to next byte in the output buffer.
1133 * offset is also updated
1134 *
1135 ******************************************************************************/
sdpu_build_partial_attrib_entry(uint8_t * p_out,tSDP_ATTRIBUTE * p_attr,uint16_t len,uint16_t * offset)1136 uint8_t* sdpu_build_partial_attrib_entry(uint8_t* p_out, tSDP_ATTRIBUTE* p_attr,
1137 uint16_t len, uint16_t* offset) {
1138 uint8_t* p_attr_buff =
1139 (uint8_t*)osi_malloc(sizeof(uint8_t) * SDP_MAX_ATTR_LEN);
1140 sdpu_build_attrib_entry(p_attr_buff, p_attr);
1141
1142 uint16_t attr_len = sdpu_get_attrib_entry_len(p_attr);
1143
1144 if (len > SDP_MAX_ATTR_LEN) {
1145 SDP_TRACE_ERROR("%s len %d exceeds SDP_MAX_ATTR_LEN", __func__, len);
1146 len = SDP_MAX_ATTR_LEN;
1147 }
1148
1149 size_t len_to_copy =
1150 ((attr_len - *offset) < len) ? (attr_len - *offset) : len;
1151 memcpy(p_out, &p_attr_buff[*offset], len_to_copy);
1152
1153 p_out = &p_out[len_to_copy];
1154 *offset += len_to_copy;
1155
1156 osi_free(p_attr_buff);
1157 return p_out;
1158 }
1159 /*******************************************************************************
1160 *
1161 * Function sdpu_is_avrcp_profile_description_list
1162 *
1163 * Description This function is to check if attirbute contain AVRCP profile
1164 * description list
1165 *
1166 * p_attr: attibute to be check
1167 *
1168 * Returns AVRCP profile version if matched, else 0
1169 *
1170 ******************************************************************************/
sdpu_is_avrcp_profile_description_list(tSDP_ATTRIBUTE * p_attr)1171 uint16_t sdpu_is_avrcp_profile_description_list(tSDP_ATTRIBUTE* p_attr) {
1172 if (p_attr->id != ATTR_ID_BT_PROFILE_DESC_LIST || p_attr->len != 8) {
1173 return 0;
1174 }
1175
1176 uint8_t* p_uuid = p_attr->value_ptr + 3;
1177 // Check if AVRCP profile UUID
1178 if (p_uuid[0] != 0x11 || p_uuid[1] != 0xe) {
1179 return 0;
1180 }
1181 uint8_t p_version = *(p_uuid + 4);
1182 switch (p_version) {
1183 case 0x0:
1184 return AVRC_REV_1_0;
1185 case 0x3:
1186 return AVRC_REV_1_3;
1187 case 0x4:
1188 return AVRC_REV_1_4;
1189 case 0x5:
1190 return AVRC_REV_1_5;
1191 case 0x6:
1192 return AVRC_REV_1_6;
1193 default:
1194 return 0;
1195 }
1196 }
1197