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 functions that handle the SDP server functions.
22 * This is mainly dealing with client requests
23 *
24 ******************************************************************************/
25 #define LOG_TAG "stack::sdp"
26
27 #include <bluetooth/log.h>
28 #include <string.h> // memcpy
29
30 #include <cstdint>
31
32 #include "btif/include/btif_storage.h"
33 #include "device/include/interop.h"
34 #include "device/include/interop_config.h"
35 #include "internal_include/bt_target.h"
36 #include "osi/include/allocator.h"
37 #include "osi/include/properties.h"
38 #include "stack/btm/btm_sco_hfp_hal.h"
39 #include "stack/include/bt_hdr.h"
40 #include "stack/include/bt_types.h"
41 #include "stack/include/bt_uuid16.h"
42 #include "stack/include/sdpdefs.h"
43 #include "stack/sdp/sdpint.h"
44
45 /* Maximum number of bytes to reserve out of SDP MTU for response data */
46 #define SDP_MAX_SERVICE_RSPHDR_LEN 12
47 #define SDP_MAX_SERVATTR_RSPHDR_LEN 10
48 #define SDP_MAX_ATTR_RSPHDR_LEN 10
49 #define PROFILE_VERSION_POSITION 7
50 #define SDP_PROFILE_DESC_LENGTH 8
51 #define HFP_PROFILE_MINOR_VERSION_6 0x06
52 #define HFP_PROFILE_MINOR_VERSION_7 0x07
53 #define HFP_PROFILE_MINOR_VERSION_9 0x09
54 #define PBAP_GOEP_L2CAP_PSM_LEN 0x06
55 #define PBAP_SUPP_FEA_LEN 0x08
56
57 #ifndef SDP_ENABLE_PTS_PBAP
58 #define SDP_ENABLE_PTS_PBAP "bluetooth.pts.pbap"
59 #endif
60
61 #define PBAP_1_2 0x0102
62 #define PBAP_1_2_BL_LEN 14
63
64 using namespace bluetooth;
65
66 /* Used to set PBAP local SDP device record for PBAP 1.2 upgrade */
67 struct tSDP_PSE_LOCAL_RECORD {
68 int32_t rfcomm_channel_number;
69 int32_t l2cap_psm;
70 int32_t profile_version;
71 uint32_t supported_features;
72 uint32_t supported_repositories;
73 };
74
75 static tSDP_PSE_LOCAL_RECORD sdpPseLocalRecord;
76
77 /******************************************************************************/
78 /* E R R O R T E X T S T R I N G S */
79 /* */
80 /* The default is to have no text string, but we allow the strings to be */
81 /* configured in target.h if people want them. */
82 /******************************************************************************/
83 #ifndef SDP_TEXT_BAD_HEADER
84 #define SDP_TEXT_BAD_HEADER NULL
85 #endif
86
87 #ifndef SDP_TEXT_BAD_PDU
88 #define SDP_TEXT_BAD_PDU NULL
89 #endif
90
91 #ifndef SDP_TEXT_BAD_UUID_LIST
92 #define SDP_TEXT_BAD_UUID_LIST NULL
93 #endif
94
95 #ifndef SDP_TEXT_BAD_HANDLE
96 #define SDP_TEXT_BAD_HANDLE NULL
97 #endif
98
99 #ifndef SDP_TEXT_BAD_ATTR_LIST
100 #define SDP_TEXT_BAD_ATTR_LIST NULL
101 #endif
102
103 #ifndef SDP_TEXT_BAD_CONT_LEN
104 #define SDP_TEXT_BAD_CONT_LEN NULL
105 #endif
106
107 #ifndef SDP_TEXT_BAD_CONT_INX
108 #define SDP_TEXT_BAD_CONT_INX NULL
109 #endif
110
111 #ifndef SDP_TEXT_BAD_MAX_RECORDS_LIST
112 #define SDP_TEXT_BAD_MAX_RECORDS_LIST NULL
113 #endif
114
115 /*************************************************************************************
116 **
117 ** Function sdp_dynamic_change_hfp_version
118 **
119 ** Description Checks if UUID is AG_HANDSFREE, attribute id
120 ** is Profile descriptor list and remote BD address
121 ** matches device Allow list, change hfp version to 1.7
122 **
123 ** Returns BOOLEAN
124 **
125 +***************************************************************************************/
sdp_dynamic_change_hfp_version(const tSDP_ATTRIBUTE * p_attr,const RawAddress & remote_address)126 bool sdp_dynamic_change_hfp_version(const tSDP_ATTRIBUTE* p_attr,
127 const RawAddress& remote_address) {
128 if ((p_attr->id != ATTR_ID_BT_PROFILE_DESC_LIST) || (p_attr->len < SDP_PROFILE_DESC_LENGTH)) {
129 return false;
130 }
131 /* As per current DB implementation UUID is condidered as 16 bit */
132 if (((p_attr->value_ptr[3] << SDP_PROFILE_DESC_LENGTH) | (p_attr->value_ptr[4])) !=
133 UUID_SERVCLASS_HF_HANDSFREE) {
134 return false;
135 }
136 bool is_allowlisted_1_7 = interop_match_addr_or_name(INTEROP_HFP_1_7_ALLOWLIST, &remote_address,
137 &btif_storage_get_remote_device_property);
138 bool is_allowlisted_1_9 = interop_match_addr_or_name(INTEROP_HFP_1_9_ALLOWLIST, &remote_address,
139 &btif_storage_get_remote_device_property);
140 /* For PTS we should update AG's HFP version as 1.7 */
141 if (!(is_allowlisted_1_7) && !(is_allowlisted_1_9) &&
142 !(osi_property_get_bool("vendor.bt.pts.certification", false))) {
143 return false;
144 }
145 if (hfp_hal_interface::get_swb_supported() && is_allowlisted_1_9) {
146 p_attr->value_ptr[PROFILE_VERSION_POSITION] = HFP_PROFILE_MINOR_VERSION_9;
147 } else {
148 p_attr->value_ptr[PROFILE_VERSION_POSITION] = HFP_PROFILE_MINOR_VERSION_7;
149 }
150 log::verbose("SDP Change HFP Version = {} for {}", p_attr->value_ptr[PROFILE_VERSION_POSITION],
151 remote_address);
152 return true;
153 }
154 /******************************************************************************
155 *
156 * Function hfp_fallback
157 *
158 * Description Update HFP version back to 1.6
159 *
160 * Returns void
161 *
162 *****************************************************************************/
hfp_fallback(bool & is_hfp_fallback,const tSDP_ATTRIBUTE * p_attr)163 static void hfp_fallback(bool& is_hfp_fallback, const tSDP_ATTRIBUTE* p_attr) {
164 /* Update HFP version back to 1.6 */
165 p_attr->value_ptr[PROFILE_VERSION_POSITION] = HFP_PROFILE_MINOR_VERSION_6;
166 log::verbose("Restore HFP version to 1.6");
167 is_hfp_fallback = false;
168 }
169
170 /*******************************************************************************
171 *
172 * Function process_service_search
173 *
174 * Description This function handles a service search request from the
175 * client. It builds a reply message with info from the
176 * database, and sends the reply back to the client.
177 *
178 * Returns void
179 *
180 ******************************************************************************/
process_service_search(tCONN_CB * p_ccb,uint16_t trans_num,uint16_t param_len,uint8_t * p_req,uint8_t * p_req_end)181 static void process_service_search(tCONN_CB* p_ccb, uint16_t trans_num, uint16_t param_len,
182 uint8_t* p_req, uint8_t* p_req_end) {
183 uint16_t max_replies, cur_handles, rem_handles, cont_offset;
184 tSDP_UUID_SEQ uid_seq;
185 uint8_t *p_rsp, *p_rsp_start, *p_rsp_param_len;
186 uint16_t rsp_param_len, num_rsp_handles, xx;
187 uint32_t rsp_handles[SDP_MAX_RECORDS] = {0};
188 const tSDP_RECORD* p_rec = NULL;
189 bool is_cont = false;
190
191 p_req = sdpu_extract_uid_seq(p_req, param_len, &uid_seq);
192
193 if ((!p_req) || (!uid_seq.num_uids)) {
194 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_REQ_SYNTAX,
195 SDP_TEXT_BAD_UUID_LIST);
196 return;
197 }
198
199 /* Get the max replies we can send. Cap it at our max anyways. */
200 if (p_req + sizeof(max_replies) + sizeof(uint8_t) > p_req_end) {
201 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_REQ_SYNTAX,
202 SDP_TEXT_BAD_MAX_RECORDS_LIST);
203 return;
204 }
205 BE_STREAM_TO_UINT16(max_replies, p_req);
206
207 if (max_replies > SDP_MAX_RECORDS) {
208 max_replies = SDP_MAX_RECORDS;
209 }
210
211 /* Get a list of handles that match the UUIDs given to us */
212 for (num_rsp_handles = 0; num_rsp_handles < max_replies;) {
213 p_rec = sdp_db_service_search(p_rec, &uid_seq);
214
215 if (p_rec) {
216 rsp_handles[num_rsp_handles++] = p_rec->record_handle;
217 } else {
218 break;
219 }
220 }
221
222 /* Check if this is a continuation request */
223 if (p_req + 1 > p_req_end) {
224 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE,
225 SDP_TEXT_BAD_CONT_LEN);
226 return;
227 }
228 if (*p_req) {
229 if (*p_req++ != SDP_CONTINUATION_LEN || (p_req + sizeof(cont_offset) > p_req_end)) {
230 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE,
231 SDP_TEXT_BAD_CONT_LEN);
232 return;
233 }
234 BE_STREAM_TO_UINT16(cont_offset, p_req);
235
236 if (cont_offset != p_ccb->cont_offset || num_rsp_handles < cont_offset) {
237 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE,
238 SDP_TEXT_BAD_CONT_INX);
239 return;
240 }
241
242 rem_handles = num_rsp_handles - cont_offset; /* extract the remaining handles */
243 } else {
244 rem_handles = num_rsp_handles;
245 cont_offset = 0;
246 p_ccb->cont_offset = 0;
247 }
248
249 /* Calculate how many handles will fit in one PDU */
250 cur_handles = (uint16_t)((p_ccb->rem_mtu_size - SDP_MAX_SERVICE_RSPHDR_LEN) / 4);
251
252 if (rem_handles <= cur_handles) {
253 cur_handles = rem_handles;
254 } else /* Continuation is set */
255 {
256 p_ccb->cont_offset += cur_handles;
257 is_cont = true;
258 }
259
260 /* Get a buffer to use to build the response */
261 BT_HDR* p_buf = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE);
262 p_buf->offset = L2CAP_MIN_OFFSET;
263 p_rsp = p_rsp_start = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
264
265 /* Start building a rsponse */
266 UINT8_TO_BE_STREAM(p_rsp, SDP_PDU_SERVICE_SEARCH_RSP);
267 UINT16_TO_BE_STREAM(p_rsp, trans_num);
268
269 /* Skip the length, we need to add it at the end */
270 p_rsp_param_len = p_rsp;
271 p_rsp += 2;
272
273 /* Put in total and current number of handles, and handles themselves */
274 UINT16_TO_BE_STREAM(p_rsp, num_rsp_handles);
275 UINT16_TO_BE_STREAM(p_rsp, cur_handles);
276
277 /*
278 log::verbose("SDP Service Rsp: tothdl {}, curhdlr {}, start {}, end {}, cont
279 {}", num_rsp_handles, cur_handles, cont_offset, cont_offset + cur_handles-1,
280 is_cont); */
281 for (xx = cont_offset; xx < cont_offset + cur_handles; xx++) {
282 UINT32_TO_BE_STREAM(p_rsp, rsp_handles[xx]);
283 }
284
285 if (is_cont) {
286 UINT8_TO_BE_STREAM(p_rsp, SDP_CONTINUATION_LEN);
287 UINT16_TO_BE_STREAM(p_rsp, p_ccb->cont_offset);
288 } else {
289 UINT8_TO_BE_STREAM(p_rsp, 0);
290 }
291
292 /* Go back and put the parameter length into the buffer */
293 rsp_param_len = p_rsp - p_rsp_param_len - 2;
294 UINT16_TO_BE_STREAM(p_rsp_param_len, rsp_param_len);
295
296 /* Set the length of the SDP data in the buffer */
297 p_buf->len = p_rsp - p_rsp_start;
298
299 /* Send the buffer through L2CAP */
300 if (stack::l2cap::get_interface().L2CA_DataWrite(p_ccb->connection_id, p_buf) !=
301 tL2CAP_DW_RESULT::SUCCESS) {
302 log::warn("Unable to write L2CAP data peer:{} cid:{} len:{}", p_ccb->device_address,
303 p_ccb->connection_id, p_rsp - p_rsp_start);
304 }
305 }
306
307 /*******************************************************************************
308 *
309 * Function process_service_attr_req
310 *
311 * Description This function handles an attribute request from the client.
312 * It builds a reply message with info from the database,
313 * and sends the reply back to the client.
314 *
315 * Returns void
316 *
317 ******************************************************************************/
process_service_attr_req(tCONN_CB * p_ccb,uint16_t trans_num,uint16_t param_len,uint8_t * p_req,uint8_t * p_req_end)318 static void process_service_attr_req(tCONN_CB* p_ccb, uint16_t trans_num, uint16_t param_len,
319 uint8_t* p_req, uint8_t* p_req_end) {
320 uint16_t max_list_len, len_to_send, cont_offset;
321 int16_t rem_len;
322 tSDP_ATTR_SEQ attr_seq, attr_seq_sav;
323 uint8_t *p_rsp, *p_rsp_start, *p_rsp_param_len;
324 uint16_t rsp_param_len, xx;
325 uint32_t rec_handle;
326 const tSDP_RECORD* p_rec;
327 const tSDP_ATTRIBUTE* p_attr;
328 bool is_cont = false;
329 bool is_hfp_fallback = false;
330 uint16_t attr_len;
331
332 if (p_req + sizeof(rec_handle) + sizeof(max_list_len) > p_req_end) {
333 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_SERV_REC_HDL,
334 SDP_TEXT_BAD_HANDLE);
335 return;
336 }
337
338 /* Extract the record handle */
339 BE_STREAM_TO_UINT32(rec_handle, p_req);
340 param_len -= sizeof(rec_handle);
341
342 /* Get the max list length we can send. Cap it at MTU size minus overhead */
343 BE_STREAM_TO_UINT16(max_list_len, p_req);
344 param_len -= sizeof(max_list_len);
345
346 if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN)) {
347 max_list_len = p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN;
348 }
349
350 p_req = sdpu_extract_attr_seq(p_req, param_len, &attr_seq);
351
352 if ((!p_req) || (!attr_seq.num_attr) || (p_req + sizeof(uint8_t) > p_req_end)) {
353 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_REQ_SYNTAX,
354 SDP_TEXT_BAD_ATTR_LIST);
355 return;
356 }
357
358 memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ));
359
360 /* Find a record with the record handle */
361 p_rec = sdp_db_find_record(rec_handle);
362 if (!p_rec) {
363 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_SERV_REC_HDL,
364 SDP_TEXT_BAD_HANDLE);
365 return;
366 }
367
368 if (max_list_len < 4) {
369 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_ILLEGAL_PARAMETER, NULL);
370 return;
371 }
372
373 /* Free and reallocate buffer */
374 osi_free(p_ccb->rsp_list);
375 p_ccb->rsp_list = (uint8_t*)osi_malloc(max_list_len);
376
377 /* Check if this is a continuation request */
378 if (p_req + 1 > p_req_end) {
379 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE,
380 SDP_TEXT_BAD_CONT_LEN);
381 return;
382 }
383 if (*p_req) {
384 if (*p_req++ != SDP_CONTINUATION_LEN || (p_req + sizeof(cont_offset) > p_req_end)) {
385 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE,
386 SDP_TEXT_BAD_CONT_LEN);
387 return;
388 }
389 BE_STREAM_TO_UINT16(cont_offset, p_req);
390
391 if (cont_offset != p_ccb->cont_offset) {
392 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE,
393 SDP_TEXT_BAD_CONT_INX);
394 return;
395 }
396 is_cont = true;
397
398 /* Initialise for continuation response */
399 p_rsp = &p_ccb->rsp_list[0];
400 attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start =
401 p_ccb->cont_info.next_attr_start_id;
402 } else {
403 p_ccb->cont_offset = 0;
404 p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */
405
406 /* Reset continuation parameters in p_ccb */
407 p_ccb->cont_info.prev_sdp_rec = NULL;
408 p_ccb->cont_info.next_attr_index = 0;
409 p_ccb->cont_info.attr_offset = 0;
410 }
411
412 bool is_service_avrc_target = false;
413 const tSDP_ATTRIBUTE* p_attr_service_id;
414 const tSDP_ATTRIBUTE* p_attr_profile_desc_list_id;
415 uint16_t avrc_sdp_version = 0;
416 p_attr_service_id = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_SERVICE_CLASS_ID_LIST,
417 ATTR_ID_SERVICE_CLASS_ID_LIST);
418 p_attr_profile_desc_list_id = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_BT_PROFILE_DESC_LIST,
419 ATTR_ID_BT_PROFILE_DESC_LIST);
420 if (p_attr_service_id) {
421 is_service_avrc_target = sdpu_is_service_id_avrc_target(p_attr_service_id);
422 }
423 /* Search for attributes that match the list given to us */
424 for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) {
425 p_attr = sdp_db_find_attr_in_rec(p_rec, attr_seq.attr_entry[xx].start,
426 attr_seq.attr_entry[xx].end);
427 if (p_attr) {
428 if (is_service_avrc_target) {
429 sdpu_set_avrc_target_version(p_attr, &(p_ccb->device_address));
430 if (p_attr->id == ATTR_ID_SUPPORTED_FEATURES) {
431 avrc_sdp_version = sdpu_is_avrcp_profile_description_list(p_attr_profile_desc_list_id);
432 log::error("avrc_sdp_version in SDP records {:x}", avrc_sdp_version);
433 sdpu_set_avrc_target_features(p_attr, &(p_ccb->device_address), avrc_sdp_version);
434 }
435 }
436 is_hfp_fallback = sdp_dynamic_change_hfp_version(p_attr, p_ccb->device_address);
437 /* Check if attribute fits. Assume 3-byte value type/length */
438 rem_len = max_list_len - (int16_t)(p_rsp - &p_ccb->rsp_list[0]);
439
440 /* just in case */
441 if (rem_len <= 0) {
442 p_ccb->cont_info.next_attr_index = xx;
443 p_ccb->cont_info.next_attr_start_id = p_attr->id;
444 break;
445 }
446
447 attr_len = sdpu_get_attrib_entry_len(p_attr);
448 /* if there is a partial attribute pending to be sent */
449 if (p_ccb->cont_info.attr_offset) {
450 if (attr_len < p_ccb->cont_info.attr_offset) {
451 log::error("offset is bigger than attribute length");
452 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE,
453 SDP_TEXT_BAD_CONT_LEN);
454 return;
455 }
456 p_rsp = sdpu_build_partial_attrib_entry(p_rsp, p_attr, rem_len,
457 &p_ccb->cont_info.attr_offset);
458
459 /* If the partial attrib could not been fully added yet */
460 if (p_ccb->cont_info.attr_offset != attr_len) {
461 break;
462 } else { /* If the partial attrib has been added in full by now */
463 p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */
464 }
465 } else if (rem_len < attr_len) /* Not enough space for attr... so add partially */
466 {
467 if (attr_len >= SDP_MAX_ATTR_LEN) {
468 log::error("SDP attr too big: max_list_len={},attr_len={}", max_list_len, attr_len);
469 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_NO_RESOURCES, NULL);
470 return;
471 }
472
473 /* add the partial attribute if possible */
474 p_rsp = sdpu_build_partial_attrib_entry(p_rsp, p_attr, (uint16_t)rem_len,
475 &p_ccb->cont_info.attr_offset);
476
477 p_ccb->cont_info.next_attr_index = xx;
478 p_ccb->cont_info.next_attr_start_id = p_attr->id;
479 break;
480 } else { /* build the whole attribute */
481 p_rsp = sdpu_build_attrib_entry(p_rsp, p_attr);
482 }
483
484 /* If doing a range, stick with this one till no more attributes found */
485 if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) {
486 /* Update for next time through */
487 attr_seq.attr_entry[xx].start = p_attr->id + 1;
488
489 xx--;
490 }
491 if (is_hfp_fallback) {
492 hfp_fallback(is_hfp_fallback, p_attr);
493 }
494 }
495 }
496 if (is_hfp_fallback) {
497 hfp_fallback(is_hfp_fallback, p_attr);
498 }
499 /* If all the attributes have been accomodated in p_rsp,
500 reset next_attr_index */
501 if (xx == attr_seq.num_attr) {
502 p_ccb->cont_info.next_attr_index = 0;
503 }
504
505 len_to_send = (uint16_t)(p_rsp - &p_ccb->rsp_list[0]);
506 cont_offset = 0;
507
508 if (!is_cont) {
509 p_ccb->list_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav) + 3;
510 /* Put in the sequence header (2 or 3 bytes) */
511 if (p_ccb->list_len > 255) {
512 p_ccb->rsp_list[0] = (uint8_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
513 p_ccb->rsp_list[1] = (uint8_t)((p_ccb->list_len - 3) >> 8);
514 p_ccb->rsp_list[2] = (uint8_t)(p_ccb->list_len - 3);
515 } else {
516 cont_offset = 1;
517
518 p_ccb->rsp_list[1] = (uint8_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
519 p_ccb->rsp_list[2] = (uint8_t)(p_ccb->list_len - 3);
520
521 p_ccb->list_len--;
522 len_to_send--;
523 }
524 }
525
526 /* Get a buffer to use to build the response */
527 BT_HDR* p_buf = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE);
528 p_buf->offset = L2CAP_MIN_OFFSET;
529 p_rsp = p_rsp_start = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
530
531 /* Start building a rsponse */
532 UINT8_TO_BE_STREAM(p_rsp, SDP_PDU_SERVICE_ATTR_RSP);
533 UINT16_TO_BE_STREAM(p_rsp, trans_num);
534
535 /* Skip the parameter length, add it when we know the length */
536 p_rsp_param_len = p_rsp;
537 p_rsp += 2;
538
539 UINT16_TO_BE_STREAM(p_rsp, len_to_send);
540
541 memcpy(p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
542 p_rsp += len_to_send;
543
544 p_ccb->cont_offset += len_to_send;
545
546 /* If anything left to send, continuation needed */
547 if (p_ccb->cont_offset < p_ccb->list_len) {
548 is_cont = true;
549
550 UINT8_TO_BE_STREAM(p_rsp, SDP_CONTINUATION_LEN);
551 UINT16_TO_BE_STREAM(p_rsp, p_ccb->cont_offset);
552 } else {
553 UINT8_TO_BE_STREAM(p_rsp, 0);
554 }
555
556 /* Go back and put the parameter length into the buffer */
557 rsp_param_len = p_rsp - p_rsp_param_len - 2;
558 UINT16_TO_BE_STREAM(p_rsp_param_len, rsp_param_len);
559
560 /* Set the length of the SDP data in the buffer */
561 p_buf->len = p_rsp - p_rsp_start;
562
563 /* Send the buffer through L2CAP */
564 if (stack::l2cap::get_interface().L2CA_DataWrite(p_ccb->connection_id, p_buf) !=
565 tL2CAP_DW_RESULT::SUCCESS) {
566 log::warn("Unable to write L2CAP data peer:{} cid:{} len:{}", p_ccb->device_address,
567 p_ccb->connection_id, p_rsp - p_rsp_start);
568 }
569 }
570
571 /*******************************************************************************
572 *
573 * Function process_service_search_attr_req
574 *
575 * Description This function handles a combined service search and
576 * attribute read request from the client. It builds a reply
577 * message with info from the database, and sends the reply
578 * back to the client.
579 *
580 * Returns void
581 *
582 ******************************************************************************/
process_service_search_attr_req(tCONN_CB * p_ccb,uint16_t trans_num,uint16_t param_len,uint8_t * p_req,uint8_t * p_req_end)583 static void process_service_search_attr_req(tCONN_CB* p_ccb, uint16_t trans_num, uint16_t param_len,
584 uint8_t* p_req, uint8_t* p_req_end) {
585 uint16_t max_list_len;
586 int16_t rem_len;
587 uint16_t len_to_send, cont_offset;
588 tSDP_UUID_SEQ uid_seq;
589 uint8_t *p_rsp, *p_rsp_start, *p_rsp_param_len;
590 uint16_t rsp_param_len, xx;
591 const tSDP_RECORD* p_rec;
592 tSDP_RECORD* p_prev_rec;
593 tSDP_ATTR_SEQ attr_seq, attr_seq_sav;
594 const tSDP_ATTRIBUTE* p_attr;
595 bool maxxed_out = false, is_cont = false;
596 uint8_t* p_seq_start;
597 bool is_hfp_fallback = false;
598 uint16_t seq_len, attr_len;
599
600 /* Extract the UUID sequence to search for */
601 p_req = sdpu_extract_uid_seq(p_req, param_len, &uid_seq);
602
603 if ((!p_req) || (!uid_seq.num_uids) || (p_req + sizeof(uint16_t) > p_req_end)) {
604 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_REQ_SYNTAX,
605 SDP_TEXT_BAD_UUID_LIST);
606 return;
607 }
608
609 /* Get the max list length we can send. Cap it at our max list length. */
610 BE_STREAM_TO_UINT16(max_list_len, p_req);
611
612 if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN)) {
613 max_list_len = p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN;
614 }
615
616 param_len = static_cast<uint16_t>(p_req_end - p_req);
617 p_req = sdpu_extract_attr_seq(p_req, param_len, &attr_seq);
618
619 if ((!p_req) || (!attr_seq.num_attr) || (p_req + sizeof(uint8_t) > p_req_end)) {
620 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_REQ_SYNTAX,
621 SDP_TEXT_BAD_ATTR_LIST);
622 return;
623 }
624
625 memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ));
626
627 if (max_list_len < 4) {
628 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_ILLEGAL_PARAMETER, NULL);
629 return;
630 }
631
632 /* Free and reallocate buffer */
633 osi_free(p_ccb->rsp_list);
634 p_ccb->rsp_list = (uint8_t*)osi_malloc(max_list_len);
635
636 /* Check if this is a continuation request */
637 if (p_req + 1 > p_req_end) {
638 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE,
639 SDP_TEXT_BAD_CONT_LEN);
640 return;
641 }
642 if (*p_req) {
643 if (*p_req++ != SDP_CONTINUATION_LEN || (p_req + sizeof(uint16_t) > p_req_end)) {
644 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE,
645 SDP_TEXT_BAD_CONT_LEN);
646 return;
647 }
648 BE_STREAM_TO_UINT16(cont_offset, p_req);
649
650 if (cont_offset != p_ccb->cont_offset) {
651 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE,
652 SDP_TEXT_BAD_CONT_INX);
653 return;
654 }
655 is_cont = true;
656
657 /* Initialise for continuation response */
658 p_rsp = &p_ccb->rsp_list[0];
659 attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start =
660 p_ccb->cont_info.next_attr_start_id;
661 } else {
662 p_ccb->cont_offset = 0;
663 p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */
664
665 /* Reset continuation parameters in p_ccb */
666 p_ccb->cont_info.prev_sdp_rec = NULL;
667 p_ccb->cont_info.next_attr_index = 0;
668 p_ccb->cont_info.last_attr_seq_desc_sent = false;
669 p_ccb->cont_info.attr_offset = 0;
670 }
671
672 /* Get a list of handles that match the UUIDs given to us */
673 for (p_rec = sdp_db_service_search(p_ccb->cont_info.prev_sdp_rec, &uid_seq); p_rec;
674 p_rec = sdp_db_service_search(p_rec, &uid_seq)) {
675 /* Store the actual record pointer which would be reused later */
676 p_prev_rec = (tSDP_RECORD*)p_rec;
677 /* Allow space for attribute sequence type and length */
678 p_seq_start = p_rsp;
679 if (!p_ccb->cont_info.last_attr_seq_desc_sent) {
680 /* See if there is enough room to include a new service in the current
681 * response */
682 rem_len = max_list_len - (int16_t)(p_rsp - &p_ccb->rsp_list[0]);
683 if (rem_len < 3) {
684 /* Not enough room. Update continuation info for next response */
685 p_ccb->cont_info.next_attr_index = 0;
686 p_ccb->cont_info.next_attr_start_id = attr_seq.attr_entry[0].start;
687 break;
688 }
689 p_rsp += 3;
690 }
691
692 bool is_service_avrc_target = false;
693 const tSDP_ATTRIBUTE* p_attr_service_id;
694 const tSDP_ATTRIBUTE* p_attr_profile_desc_list_id;
695 uint16_t avrc_sdp_version = 0;
696 p_attr_service_id = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_SERVICE_CLASS_ID_LIST,
697 ATTR_ID_SERVICE_CLASS_ID_LIST);
698 p_attr_profile_desc_list_id = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_BT_PROFILE_DESC_LIST,
699 ATTR_ID_BT_PROFILE_DESC_LIST);
700 if (p_attr_service_id) {
701 is_service_avrc_target = sdpu_is_service_id_avrc_target(p_attr_service_id);
702 }
703 /* Get a list of handles that match the UUIDs given to us */
704 for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) {
705 p_attr = sdp_db_find_attr_in_rec(p_rec, attr_seq.attr_entry[xx].start,
706 attr_seq.attr_entry[xx].end);
707
708 if (p_attr) {
709 if (is_service_avrc_target) {
710 sdpu_set_avrc_target_version(p_attr, &(p_ccb->device_address));
711 if (p_attr->id == ATTR_ID_SUPPORTED_FEATURES && p_attr_profile_desc_list_id != nullptr) {
712 avrc_sdp_version = sdpu_is_avrcp_profile_description_list(p_attr_profile_desc_list_id);
713 log::error("avrc_sdp_version in SDP records {:x}", avrc_sdp_version);
714 sdpu_set_avrc_target_features(p_attr, &(p_ccb->device_address), avrc_sdp_version);
715 }
716 }
717 is_hfp_fallback = sdp_dynamic_change_hfp_version(p_attr, p_ccb->device_address);
718 /* Check if attribute fits. Assume 3-byte value type/length */
719 rem_len = max_list_len - (int16_t)(p_rsp - &p_ccb->rsp_list[0]);
720
721 /* just in case */
722 if (rem_len <= 0) {
723 p_ccb->cont_info.next_attr_index = xx;
724 p_ccb->cont_info.next_attr_start_id = p_attr->id;
725 maxxed_out = true;
726 break;
727 }
728
729 attr_len = sdpu_get_attrib_entry_len(p_attr);
730 /* if there is a partial attribute pending to be sent */
731 if (p_ccb->cont_info.attr_offset) {
732 if (attr_len < p_ccb->cont_info.attr_offset) {
733 log::error("offset is bigger than attribute length");
734 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE,
735 SDP_TEXT_BAD_CONT_LEN);
736 return;
737 }
738 p_rsp = sdpu_build_partial_attrib_entry(p_rsp, p_attr, rem_len,
739 &p_ccb->cont_info.attr_offset);
740
741 /* If the partial attrib could not been fully added yet */
742 if (p_ccb->cont_info.attr_offset != attr_len) {
743 maxxed_out = true;
744 break;
745 } else { /* If the partial attrib has been added in full by now */
746 p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */
747 }
748 } else if (rem_len < attr_len) /* Not enough space for attr... so add partially */
749 {
750 if (attr_len >= SDP_MAX_ATTR_LEN) {
751 log::error("SDP attr too big: max_list_len={},attr_len={}", max_list_len, attr_len);
752 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_NO_RESOURCES, NULL);
753 return;
754 }
755
756 /* add the partial attribute if possible */
757 p_rsp = sdpu_build_partial_attrib_entry(p_rsp, p_attr, (uint16_t)rem_len,
758 &p_ccb->cont_info.attr_offset);
759
760 p_ccb->cont_info.next_attr_index = xx;
761 p_ccb->cont_info.next_attr_start_id = p_attr->id;
762 maxxed_out = true;
763 break;
764 } else { /* build the whole attribute */
765 p_rsp = sdpu_build_attrib_entry(p_rsp, p_attr);
766 }
767
768 /* If doing a range, stick with this one till no more attributes found
769 */
770 if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) {
771 /* Update for next time through */
772 attr_seq.attr_entry[xx].start = p_attr->id + 1;
773
774 xx--;
775 }
776 if (is_hfp_fallback) {
777 hfp_fallback(is_hfp_fallback, p_attr);
778 }
779 }
780 }
781 if (is_hfp_fallback) {
782 hfp_fallback(is_hfp_fallback, p_attr);
783 }
784
785 /* Go back and put the type and length into the buffer */
786 if (!p_ccb->cont_info.last_attr_seq_desc_sent) {
787 seq_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav);
788 if (seq_len != 0) {
789 UINT8_TO_BE_STREAM(p_seq_start, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
790 UINT16_TO_BE_STREAM(p_seq_start, seq_len);
791
792 if (maxxed_out) {
793 p_ccb->cont_info.last_attr_seq_desc_sent = true;
794 }
795 } else {
796 p_rsp = p_seq_start;
797 }
798 }
799
800 if (maxxed_out) {
801 break;
802 }
803
804 /* Restore the attr_seq to look for in the next sdp record */
805 memcpy(&attr_seq, &attr_seq_sav, sizeof(tSDP_ATTR_SEQ));
806
807 /* Reset the next attr index */
808 p_ccb->cont_info.next_attr_index = 0;
809 /* restore the record pointer.*/
810 p_rec = p_prev_rec;
811 p_ccb->cont_info.prev_sdp_rec = p_rec;
812 p_ccb->cont_info.last_attr_seq_desc_sent = false;
813 }
814
815 /* response length */
816 len_to_send = (uint16_t)(p_rsp - &p_ccb->rsp_list[0]);
817 cont_offset = 0;
818
819 // The current SDP server design has a critical flaw where it can run into
820 // an infinite request/response loop with the client. Here's the scenario:
821 // - client makes SDP request
822 // - server returns the first fragment of the response with a continuation
823 // token
824 // - an SDP record is deleted from the server
825 // - client issues another request with previous continuation token
826 // - server has nothing to send back because the record is unavailable but
827 // in the first fragment, it had specified more response bytes than are
828 // now available
829 // - server sends back no additional response bytes and returns the same
830 // continuation token
831 // - client issues another request with the continuation token, and the
832 // process repeats
833 //
834 // We work around this design flaw here by checking if we will make forward
835 // progress (i.e. we will send > 0 response bytes) on a continued request.
836 // If not, we must have run into the above situation and we tell the peer an
837 // error occurred.
838 //
839 // TODO(sharvil): rewrite SDP server.
840 if (is_cont && len_to_send == 0) {
841 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_CONT_STATE, NULL);
842 return;
843 }
844
845 /* If first response, insert sequence header */
846 if (!is_cont) {
847 /* Get the total list length for requested uid and attribute sequence */
848 p_ccb->list_len = sdpu_get_list_len(&uid_seq, &attr_seq_sav) + 3;
849
850 /* Get the length of denylisted attributes to be updated if device is
851 * denylisted */
852 p_ccb->pse_dynamic_attributes_len = 0;
853
854 log::verbose("p_ccb->list_len = {} pse_dynamic_attributes_len = {}", p_ccb->list_len,
855 p_ccb->pse_dynamic_attributes_len);
856
857 /* Put in the sequence header (2 or 3 bytes) */
858 if (p_ccb->list_len > 255) {
859 p_ccb->rsp_list[0] = (uint8_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
860 p_ccb->rsp_list[1] =
861 (uint8_t)((p_ccb->list_len - 3 + p_ccb->pse_dynamic_attributes_len) >> 8);
862 p_ccb->rsp_list[2] = (uint8_t)(p_ccb->list_len - 3 + p_ccb->pse_dynamic_attributes_len);
863 } else {
864 cont_offset = 1;
865
866 p_ccb->rsp_list[1] = (uint8_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
867 p_ccb->rsp_list[2] = (uint8_t)(p_ccb->list_len - 3 + p_ccb->pse_dynamic_attributes_len);
868
869 p_ccb->list_len--;
870 len_to_send--;
871 }
872 }
873
874 /* Get a buffer to use to build the response */
875 BT_HDR* p_buf = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE);
876 p_buf->offset = L2CAP_MIN_OFFSET;
877 p_rsp = p_rsp_start = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
878
879 /* Start building a rsponse */
880 UINT8_TO_BE_STREAM(p_rsp, SDP_PDU_SERVICE_SEARCH_ATTR_RSP);
881 UINT16_TO_BE_STREAM(p_rsp, trans_num);
882
883 /* Skip the parameter length, add it when we know the length */
884 p_rsp_param_len = p_rsp;
885 p_rsp += 2;
886
887 /* Stream the list length to send */
888 UINT16_TO_BE_STREAM(p_rsp, len_to_send);
889
890 /* copy from rsp_list to the actual buffer to be sent */
891 memcpy(p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
892 p_rsp += len_to_send;
893
894 p_ccb->cont_offset += len_to_send;
895
896 log::verbose(
897 "p_ccb->pse_dynamic_attributes_len {}, cont_offset = {}, p_ccb->list_len "
898 "= {}",
899 p_ccb->pse_dynamic_attributes_len, p_ccb->cont_offset,
900 p_ccb->list_len + p_ccb->pse_dynamic_attributes_len);
901 /* If anything left to send, continuation needed */
902 if (p_ccb->cont_offset < (p_ccb->list_len + p_ccb->pse_dynamic_attributes_len)) {
903 is_cont = true;
904 UINT8_TO_BE_STREAM(p_rsp, SDP_CONTINUATION_LEN);
905 UINT16_TO_BE_STREAM(p_rsp, p_ccb->cont_offset);
906 } else {
907 UINT8_TO_BE_STREAM(p_rsp, 0);
908 if (p_ccb->pse_dynamic_attributes_len) {
909 p_ccb->pse_dynamic_attributes_len = 0;
910 }
911 }
912
913 /* Go back and put the parameter length into the buffer */
914 rsp_param_len = p_rsp - p_rsp_param_len - 2;
915 UINT16_TO_BE_STREAM(p_rsp_param_len, rsp_param_len);
916
917 /* Set the length of the SDP data in the buffer */
918 p_buf->len = p_rsp - p_rsp_start;
919
920 /* Send the buffer through L2CAP */
921 if (stack::l2cap::get_interface().L2CA_DataWrite(p_ccb->connection_id, p_buf) !=
922 tL2CAP_DW_RESULT::SUCCESS) {
923 log::warn("Unable to write L2CAP data peer:{} cid:{} len:{}", p_ccb->device_address,
924 p_ccb->connection_id, p_rsp - p_rsp_start);
925 }
926 }
927
928 /*******************************************************************************
929 *
930 * Function sdp_server_handle_client_req
931 *
932 * Description This is the main dispatcher of the SDP server. It is called
933 * when any data is received from L2CAP, and dispatches the
934 * request to the appropriate handler.
935 *
936 * Returns void
937 *
938 ******************************************************************************/
sdp_server_handle_client_req(tCONN_CB * p_ccb,BT_HDR * p_msg)939 void sdp_server_handle_client_req(tCONN_CB* p_ccb, BT_HDR* p_msg) {
940 uint8_t* p_req = (uint8_t*)(p_msg + 1) + p_msg->offset;
941 uint8_t* p_req_end = p_req + p_msg->len;
942 uint8_t pdu_id;
943 uint16_t trans_num, param_len;
944
945 /* Start inactivity timer */
946 alarm_set_on_mloop(p_ccb->sdp_conn_timer, SDP_INACT_TIMEOUT_MS, sdp_conn_timer_timeout, p_ccb);
947
948 if (p_req + sizeof(pdu_id) + sizeof(trans_num) > p_req_end) {
949 trans_num = 0;
950 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_REQ_SYNTAX,
951 SDP_TEXT_BAD_HEADER);
952 return;
953 }
954
955 /* The first byte in the message is the pdu type */
956 pdu_id = *p_req++;
957
958 /* Extract the transaction number and parameter length */
959 BE_STREAM_TO_UINT16(trans_num, p_req);
960
961 if (p_req + sizeof(param_len) > p_req_end) {
962 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_REQ_SYNTAX,
963 SDP_TEXT_BAD_HEADER);
964 return;
965 }
966
967 BE_STREAM_TO_UINT16(param_len, p_req);
968
969 if ((p_req + param_len) != p_req_end) {
970 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_PDU_SIZE,
971 SDP_TEXT_BAD_HEADER);
972 return;
973 }
974
975 switch (pdu_id) {
976 case SDP_PDU_SERVICE_SEARCH_REQ:
977 process_service_search(p_ccb, trans_num, param_len, p_req, p_req_end);
978 break;
979
980 case SDP_PDU_SERVICE_ATTR_REQ:
981 process_service_attr_req(p_ccb, trans_num, param_len, p_req, p_req_end);
982 break;
983
984 case SDP_PDU_SERVICE_SEARCH_ATTR_REQ:
985 process_service_search_attr_req(p_ccb, trans_num, param_len, p_req, p_req_end);
986 break;
987
988 default:
989 sdpu_build_n_send_error(p_ccb, trans_num, tSDP_STATUS::SDP_INVALID_REQ_SYNTAX,
990 SDP_TEXT_BAD_PDU);
991 log::warn("SDP - server got unknown PDU: 0x{:x}", pdu_id);
992 break;
993 }
994 }
995
996 /*************************************************************************************
997 **
998 ** Function update_pce_entry_to_interop_database
999 **
1000 ** Description Update PCE 1.2 entry to dynamic interop database
1001 **
1002 ***************************************************************************************/
update_pce_entry_to_interop_database(RawAddress remote_addr)1003 void update_pce_entry_to_interop_database(RawAddress remote_addr) {
1004 if (!interop_match_addr_or_name(INTEROP_ADV_PBAP_VER_1_2, &remote_addr,
1005 &btif_storage_get_remote_device_property)) {
1006 interop_database_add_addr(INTEROP_ADV_PBAP_VER_1_2, &remote_addr, 3);
1007 log::verbose("device: {} is added into interop list", remote_addr);
1008 } else {
1009 log::warn("device: {} is already found on interop list", remote_addr);
1010 }
1011 }
1012
1013 /*************************************************************************************
1014 **
1015 ** Function is_sdp_pbap_pce_disabled
1016 **
1017 ** Description Checks if given PBAP record is for PBAP PSE and SDP
1018 *denylisted
1019 **
1020 ** Returns BOOLEAN
1021 **
1022 ***************************************************************************************/
is_sdp_pbap_pce_disabled(RawAddress remote_address)1023 bool is_sdp_pbap_pce_disabled(RawAddress remote_address) {
1024 if (interop_match_addr_or_name(INTEROP_DISABLE_PCE_SDP_AFTER_PAIRING, &remote_address,
1025 &btif_storage_get_remote_device_property)) {
1026 log::verbose("device is denylisted for PCE SDP");
1027 return true;
1028 } else {
1029 return false;
1030 }
1031 }
1032
1033 /*************************************************************************************
1034 **
1035 ** Function sdp_save_local_pse_record_attributes_val
1036 **
1037 ** Description Save pbap 1.2 sdp record attributes values, which would be
1038 *used for dynamic version upgrade.
1039 **
1040 ** Returns BOOLEAN
1041 **
1042 ***************************************************************************************/
sdp_save_local_pse_record_attributes(int32_t rfcomm_channel_number,int32_t l2cap_psm,int32_t profile_version,uint32_t supported_features,uint32_t supported_repositories)1043 void sdp_save_local_pse_record_attributes(int32_t rfcomm_channel_number, int32_t l2cap_psm,
1044 int32_t profile_version, uint32_t supported_features,
1045 uint32_t supported_repositories) {
1046 log::warn(
1047 "rfcomm_channel_number: 0x{:x}, l2cap_psm: 0x{:x} profile_version: "
1048 "0x{:x}supported_features: 0x{:x} supported_repositories: 0x{:x}",
1049 rfcomm_channel_number, l2cap_psm, profile_version, supported_features,
1050 supported_repositories);
1051 sdpPseLocalRecord.rfcomm_channel_number = rfcomm_channel_number;
1052 sdpPseLocalRecord.l2cap_psm = l2cap_psm;
1053 sdpPseLocalRecord.profile_version = profile_version;
1054 sdpPseLocalRecord.supported_features = supported_features;
1055 sdpPseLocalRecord.supported_repositories = supported_repositories;
1056 }
1057