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