1/******************************************************************************
2 *
3 *  Copyright (C) 1998-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#include <string.h>
20
21#include "bt_target.h"
22#if (HL_INCLUDED == TRUE)
23
24#include "bta_hl_int.h"
25#include "osi/include/osi.h"
26#include "sdp_api.h"
27#include "utl.h"
28
29/*******************************************************************************
30 *
31 * Function         bta_hl_fill_sup_feature_list
32 *
33 * Description      Fill the supported features from teh SDP record
34 *
35 * Returns          true if found, false if not
36 *                  If found, the passed protocol list element is filled in.
37 *
38 ******************************************************************************/
39bool bta_hl_fill_sup_feature_list(const tSDP_DISC_ATTR* p_attr,
40                                  tBTA_HL_SUP_FEATURE_LIST_ELEM* p_list) {
41  tSDP_DISC_ATTR* p_sattr;
42  uint8_t item_cnt;
43  uint8_t list_cnt = 0;
44  bool status = true;
45
46  for (p_attr = p_attr->attr_value.v.p_sub_attr; p_attr;
47       p_attr = p_attr->p_next_attr) {
48    /* mdep sequence */
49    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) {
50      return (false);
51    }
52
53    item_cnt = 0;
54
55    for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr && (item_cnt < 4);
56         p_sattr = p_sattr->p_next_attr) {
57      /* for each mdep list */
58
59      p_list->list_elem[list_cnt].p_mdep_desp = NULL;
60      switch (item_cnt) {
61        case 0:
62          p_list->list_elem[list_cnt].mdep_id = p_sattr->attr_value.v.u8;
63          break;
64        case 1:
65          p_list->list_elem[list_cnt].data_type = p_sattr->attr_value.v.u16;
66          break;
67        case 2:
68          p_list->list_elem[list_cnt].mdep_role =
69              (tBTA_HL_MDEP_ROLE)p_sattr->attr_value.v.u8;
70          break;
71        case 3:
72          p_list->list_elem[list_cnt].p_mdep_desp =
73              (char*)p_sattr->attr_value.v.array;
74          break;
75      }
76
77      item_cnt++;
78    }
79    list_cnt++;
80  }
81  p_list->num_elems = list_cnt;
82  return (status);
83}
84
85/*******************************************************************************
86 *
87 * Function         bta_hl_compose_supported_feature_list
88 *
89 * Description      This function is called to compose a data sequence from
90 *                  the supported  feature element list struct pointer
91 *
92 * Returns          the length of the data sequence
93 *
94 ******************************************************************************/
95int bta_hl_compose_supported_feature_list(
96    uint8_t* p, uint16_t num_elem,
97    const tBTA_HL_SUP_FEATURE_ELEM* p_elem_list) {
98  uint16_t xx, str_len, seq_len;
99  uint8_t* p_head = p;
100
101  for (xx = 0; xx < num_elem; xx++, p_elem_list++) {
102    UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
103    seq_len = 7;
104    str_len = 0;
105    if (p_elem_list->p_mdep_desp) {
106      str_len = strlen(p_elem_list->p_mdep_desp) + 1;
107      seq_len += str_len + 2; /* todo add a # symbol for 2 */
108    }
109
110    *p++ = (uint8_t)seq_len;
111
112    UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE);
113    UINT8_TO_BE_STREAM(p, p_elem_list->mdep_id);
114    UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
115    UINT16_TO_BE_STREAM(p, p_elem_list->data_type);
116    UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE);
117    UINT8_TO_BE_STREAM(p, p_elem_list->mdep_role);
118
119    if (str_len) {
120      UINT8_TO_BE_STREAM(p, (TEXT_STR_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
121      UINT8_TO_BE_STREAM(p, str_len);
122      ARRAY_TO_BE_STREAM(p, p_elem_list->p_mdep_desp, str_len);
123    }
124  }
125
126  return (p - p_head);
127}
128
129/*******************************************************************************
130 *
131 * Function         bta_hl_add_sup_feature_list
132 *
133 * Description      This function is called to add a protocol descriptor list to
134 *                  a record. This would be through the SDP database maintenance
135 *                  API. If the protocol list already exists in the record, it
136 *                  is replaced with the new list.
137 *
138 * Returns          true if added OK, else false
139 *
140 ******************************************************************************/
141bool bta_hl_add_sup_feature_list(uint32_t handle, uint16_t num_elem,
142                                 const tBTA_HL_SUP_FEATURE_ELEM* p_elem_list) {
143  int offset;
144  bool result;
145  uint8_t* p_buf = (uint8_t*)osi_malloc(BTA_HL_SUP_FEATURE_SDP_BUF_SIZE);
146
147  offset = bta_hl_compose_supported_feature_list(p_buf, num_elem, p_elem_list);
148  result = SDP_AddAttribute(handle, ATTR_ID_HDP_SUP_FEAT_LIST,
149                            DATA_ELE_SEQ_DESC_TYPE, (uint32_t)offset, p_buf);
150  osi_free(p_buf);
151
152  return result;
153}
154
155/*****************************************************************************
156 *
157 *  Function:    bta_hl_sdp_update
158 *
159 *  Purpose:     Register an HDP application with SDP
160 *
161 *  Parameters:
162 *
163 *  Returns:     void
164 *
165 ****************************************************************************/
166tBTA_HL_STATUS bta_hl_sdp_update(UNUSED_ATTR uint8_t app_id) {
167  uint16_t svc_class_id_list[BTA_HL_NUM_SVC_ELEMS];
168  tSDP_PROTOCOL_ELEM proto_elem_list[BTA_HL_NUM_PROTO_ELEMS];
169  tSDP_PROTO_LIST_ELEM add_proto_list;
170  tBTA_HL_SUP_FEATURE_LIST_ELEM sup_feature_list;
171  uint16_t browse_list[] = {UUID_SERVCLASS_PUBLIC_BROWSE_GROUP};
172  uint8_t i, j, cnt, mdep_id, mdep_role;
173  uint8_t data_exchange_spec = BTA_HL_SDP_IEEE_11073_20601;
174  uint8_t mcap_sup_proc = BTA_HL_MCAP_SUP_PROC_MASK;
175  uint16_t profile_uuid = UUID_SERVCLASS_HDP_PROFILE;
176  uint16_t version = BTA_HL_VERSION;
177  uint8_t num_services = 1;
178  tBTA_HL_APP_CB* p_cb = BTA_HL_GET_APP_CB_PTR(0);
179  bool result = true;
180  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
181
182  if ((p_cb->sup_feature.app_role_mask == BTA_HL_MDEP_ROLE_MASK_SOURCE) &&
183      (!p_cb->sup_feature.advertize_source_sdp)) {
184    return BTA_HL_STATUS_OK;
185  }
186
187  num_services = 1;
188  svc_class_id_list[0] = UUID_SERVCLASS_HDP_SOURCE;
189  if (p_cb->sup_feature.app_role_mask == BTA_HL_MDEP_ROLE_MASK_SINK) {
190    svc_class_id_list[0] = UUID_SERVCLASS_HDP_SINK;
191  } else {
192    if (p_cb->sup_feature.app_role_mask != BTA_HL_MDEP_ROLE_MASK_SOURCE) {
193      /* dual role */
194      num_services = 2;
195      svc_class_id_list[1] = UUID_SERVCLASS_HDP_SINK;
196    }
197  }
198  result &= SDP_AddServiceClassIdList(p_cb->sdp_handle, num_services,
199                                      svc_class_id_list);
200
201  if (result) {
202    /* add the protocol element sequence */
203    proto_elem_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
204    proto_elem_list[0].num_params = 1;
205    proto_elem_list[0].params[0] = p_cb->ctrl_psm;
206    proto_elem_list[1].protocol_uuid = UUID_PROTOCOL_MCAP_CTRL;
207    proto_elem_list[1].num_params = 1;
208    proto_elem_list[1].params[0] = version;
209    result &= SDP_AddProtocolList(p_cb->sdp_handle, BTA_HL_NUM_PROTO_ELEMS,
210                                  proto_elem_list);
211
212    result &=
213        SDP_AddProfileDescriptorList(p_cb->sdp_handle, profile_uuid, version);
214  }
215
216  if (result) {
217    add_proto_list.num_elems = BTA_HL_NUM_ADD_PROTO_ELEMS;
218    add_proto_list.list_elem[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
219    add_proto_list.list_elem[0].num_params = 1;
220    add_proto_list.list_elem[0].params[0] = p_cb->data_psm;
221    add_proto_list.list_elem[1].protocol_uuid = UUID_PROTOCOL_MCAP_DATA;
222    add_proto_list.list_elem[1].num_params = 0;
223    result &=
224        SDP_AddAdditionProtoLists(p_cb->sdp_handle, BTA_HL_NUM_ADD_PROTO_LISTS,
225                                  (tSDP_PROTO_LIST_ELEM*)&add_proto_list);
226  }
227
228  if (result) {
229    if (p_cb->srv_name[0]) {
230      result &= SDP_AddAttribute(
231          p_cb->sdp_handle, (uint16_t)ATTR_ID_SERVICE_NAME,
232          (uint8_t)TEXT_STR_DESC_TYPE, (uint32_t)(strlen(p_cb->srv_name) + 1),
233          (uint8_t*)p_cb->srv_name);
234    } /* end of setting optional service name */
235  }
236
237  if (result) {
238    if (p_cb->srv_desp[0]) {
239      result &= SDP_AddAttribute(
240          p_cb->sdp_handle, (uint16_t)ATTR_ID_SERVICE_DESCRIPTION,
241          (uint8_t)TEXT_STR_DESC_TYPE, (uint32_t)(strlen(p_cb->srv_desp) + 1),
242          (uint8_t*)p_cb->srv_desp);
243
244    } /* end of setting optional service description */
245  }
246
247  if (result) {
248    if (p_cb->provider_name[0]) {
249      result &=
250          SDP_AddAttribute(p_cb->sdp_handle, (uint16_t)ATTR_ID_PROVIDER_NAME,
251                           (uint8_t)TEXT_STR_DESC_TYPE,
252                           (uint32_t)(strlen(p_cb->provider_name) + 1),
253                           (uint8_t*)p_cb->provider_name);
254    } /* end of setting optional provider name */
255  }
256
257  /* add supported feture list */
258
259  if (result) {
260    cnt = 0;
261    for (i = 1; i < BTA_HL_NUM_MDEPS; i++) {
262      if (p_cb->sup_feature.mdep[i].mdep_id) {
263        mdep_id = (uint8_t)p_cb->sup_feature.mdep[i].mdep_id;
264        mdep_role = (uint8_t)p_cb->sup_feature.mdep[i].mdep_cfg.mdep_role;
265
266        APPL_TRACE_DEBUG(
267            "num_of_mdep_data_types %d ",
268            p_cb->sup_feature.mdep[i].mdep_cfg.num_of_mdep_data_types);
269        for (j = 0;
270             j < p_cb->sup_feature.mdep[i].mdep_cfg.num_of_mdep_data_types;
271             j++) {
272          sup_feature_list.list_elem[cnt].mdep_id = mdep_id;
273          sup_feature_list.list_elem[cnt].mdep_role = mdep_role;
274          sup_feature_list.list_elem[cnt].data_type =
275              p_cb->sup_feature.mdep[i].mdep_cfg.data_cfg[j].data_type;
276          if (p_cb->sup_feature.mdep[i].mdep_cfg.data_cfg[j].desp[0] != '\0') {
277            sup_feature_list.list_elem[cnt].p_mdep_desp =
278                p_cb->sup_feature.mdep[i].mdep_cfg.data_cfg[j].desp;
279          } else {
280            sup_feature_list.list_elem[cnt].p_mdep_desp = NULL;
281          }
282
283          cnt++;
284          if (cnt == BTA_HL_NUM_SUP_FEATURE_ELEMS) {
285            result = false;
286            break;
287          }
288        }
289      }
290    }
291    sup_feature_list.num_elems = cnt;
292    result &= bta_hl_add_sup_feature_list(p_cb->sdp_handle,
293                                          sup_feature_list.num_elems,
294                                          sup_feature_list.list_elem);
295  }
296  if (result) {
297    result &= SDP_AddAttribute(p_cb->sdp_handle, ATTR_ID_HDP_DATA_EXCH_SPEC,
298                               UINT_DESC_TYPE, (uint32_t)1,
299                               (uint8_t*)&data_exchange_spec);
300  }
301
302  if (result) {
303    result &=
304        SDP_AddAttribute(p_cb->sdp_handle, ATTR_ID_HDP_MCAP_SUP_PROC,
305                         UINT_DESC_TYPE, (uint32_t)1, (uint8_t*)&mcap_sup_proc);
306  }
307
308  if (result) {
309    result &= SDP_AddUuidSequence(p_cb->sdp_handle, ATTR_ID_BROWSE_GROUP_LIST,
310                                  1, browse_list);
311  }
312
313  if (result) {
314    for (i = 0; i < num_services; i++) {
315      bta_sys_add_uuid(svc_class_id_list[i]);
316      APPL_TRACE_DEBUG("dbg bta_sys_add_uuid i=%d uuid=0x%x", i,
317                       svc_class_id_list[i]);  // todo
318    }
319  } else {
320    if (p_cb->sdp_handle) {
321      SDP_DeleteRecord(p_cb->sdp_handle);
322      p_cb->sdp_handle = 0;
323    }
324    status = BTA_HL_STATUS_SDP_FAIL;
325  }
326#if (BTA_HL_DEBUG == TRUE)
327  APPL_TRACE_DEBUG("bta_hl_sdp_update status=%s", bta_hl_status_code(status));
328#endif
329  return status;
330}
331
332/*****************************************************************************
333 *
334 *  Function:    bta_hl_sdp_register
335 *
336 *  Purpose:     Register an HDP application with SDP
337 *
338 *  Parameters:  p_cb           - Pointer to MA instance control block
339 *               p_service_name - MA server name
340 *               inst_id        - MAS instance ID
341 *               msg_type       - Supported message type(s)
342 *
343 *
344 *  Returns:     void
345 *
346 ****************************************************************************/
347tBTA_HL_STATUS bta_hl_sdp_register(uint8_t app_idx) {
348  uint16_t svc_class_id_list[BTA_HL_NUM_SVC_ELEMS];
349  tSDP_PROTOCOL_ELEM proto_elem_list[BTA_HL_NUM_PROTO_ELEMS];
350  tSDP_PROTO_LIST_ELEM add_proto_list;
351  tBTA_HL_SUP_FEATURE_LIST_ELEM sup_feature_list;
352  uint16_t browse_list[] = {UUID_SERVCLASS_PUBLIC_BROWSE_GROUP};
353  uint8_t i, j, cnt, mdep_id, mdep_role;
354  uint8_t data_exchange_spec = BTA_HL_SDP_IEEE_11073_20601;
355  uint8_t mcap_sup_proc = BTA_HL_MCAP_SUP_PROC_MASK;
356  uint16_t profile_uuid = UUID_SERVCLASS_HDP_PROFILE;
357  uint16_t version = BTA_HL_VERSION;
358  uint8_t num_services = 1;
359  tBTA_HL_APP_CB* p_cb = BTA_HL_GET_APP_CB_PTR(app_idx);
360  bool result = true;
361  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
362
363#if (BTA_HL_DEBUG == TRUE)
364  APPL_TRACE_DEBUG("bta_hl_sdp_register app_idx=%d", app_idx);
365#endif
366
367  if ((p_cb->sup_feature.app_role_mask == BTA_HL_MDEP_ROLE_MASK_SOURCE) &&
368      (!p_cb->sup_feature.advertize_source_sdp)) {
369    return BTA_HL_STATUS_OK;
370  }
371
372  p_cb->sdp_handle = SDP_CreateRecord();
373  if (p_cb->sdp_handle == 0) {
374    return BTA_HL_STATUS_SDP_NO_RESOURCE;
375  }
376
377  num_services = 1;
378  svc_class_id_list[0] = UUID_SERVCLASS_HDP_SOURCE;
379  if (p_cb->sup_feature.app_role_mask == BTA_HL_MDEP_ROLE_MASK_SINK) {
380    svc_class_id_list[0] = UUID_SERVCLASS_HDP_SINK;
381  } else {
382    if (p_cb->sup_feature.app_role_mask != BTA_HL_MDEP_ROLE_MASK_SOURCE) {
383      /* dual role */
384      num_services = 2;
385      svc_class_id_list[1] = UUID_SERVCLASS_HDP_SINK;
386    }
387  }
388  result &= SDP_AddServiceClassIdList(p_cb->sdp_handle, num_services,
389                                      svc_class_id_list);
390
391  if (result) {
392    /* add the protocol element sequence */
393    proto_elem_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
394    proto_elem_list[0].num_params = 1;
395    proto_elem_list[0].params[0] = p_cb->ctrl_psm;
396    proto_elem_list[1].protocol_uuid = UUID_PROTOCOL_MCAP_CTRL;
397    proto_elem_list[1].num_params = 1;
398    proto_elem_list[1].params[0] = version;
399    result &= SDP_AddProtocolList(p_cb->sdp_handle, BTA_HL_NUM_PROTO_ELEMS,
400                                  proto_elem_list);
401
402    result &=
403        SDP_AddProfileDescriptorList(p_cb->sdp_handle, profile_uuid, version);
404  }
405
406  if (result) {
407    add_proto_list.num_elems = BTA_HL_NUM_ADD_PROTO_ELEMS;
408    add_proto_list.list_elem[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
409    add_proto_list.list_elem[0].num_params = 1;
410    add_proto_list.list_elem[0].params[0] = p_cb->data_psm;
411    add_proto_list.list_elem[1].protocol_uuid = UUID_PROTOCOL_MCAP_DATA;
412    add_proto_list.list_elem[1].num_params = 0;
413    result &=
414        SDP_AddAdditionProtoLists(p_cb->sdp_handle, BTA_HL_NUM_ADD_PROTO_LISTS,
415                                  (tSDP_PROTO_LIST_ELEM*)&add_proto_list);
416  }
417
418  if (result) {
419    if (p_cb->srv_name[0]) {
420      result &= SDP_AddAttribute(
421          p_cb->sdp_handle, (uint16_t)ATTR_ID_SERVICE_NAME,
422          (uint8_t)TEXT_STR_DESC_TYPE, (uint32_t)(strlen(p_cb->srv_name) + 1),
423          (uint8_t*)p_cb->srv_name);
424    } /* end of setting optional service name */
425  }
426
427  if (result) {
428    if (p_cb->srv_desp[0]) {
429      result &= SDP_AddAttribute(
430          p_cb->sdp_handle, (uint16_t)ATTR_ID_SERVICE_DESCRIPTION,
431          (uint8_t)TEXT_STR_DESC_TYPE, (uint32_t)(strlen(p_cb->srv_desp) + 1),
432          (uint8_t*)p_cb->srv_desp);
433
434    } /* end of setting optional service description */
435  }
436
437  if (result) {
438    if (p_cb->provider_name[0]) {
439      result &=
440          SDP_AddAttribute(p_cb->sdp_handle, (uint16_t)ATTR_ID_PROVIDER_NAME,
441                           (uint8_t)TEXT_STR_DESC_TYPE,
442                           (uint32_t)(strlen(p_cb->provider_name) + 1),
443                           (uint8_t*)p_cb->provider_name);
444    } /* end of setting optional provider name */
445  }
446
447  /* add supported feture list */
448
449  if (result) {
450    cnt = 0;
451    for (i = 1; i <= p_cb->sup_feature.num_of_mdeps; i++) {
452      mdep_id = (uint8_t)p_cb->sup_feature.mdep[i].mdep_id;
453      mdep_role = (uint8_t)p_cb->sup_feature.mdep[i].mdep_cfg.mdep_role;
454
455      for (j = 0; j < p_cb->sup_feature.mdep[i].mdep_cfg.num_of_mdep_data_types;
456           j++) {
457        sup_feature_list.list_elem[cnt].mdep_id = mdep_id;
458        sup_feature_list.list_elem[cnt].mdep_role = mdep_role;
459        sup_feature_list.list_elem[cnt].data_type =
460            p_cb->sup_feature.mdep[i].mdep_cfg.data_cfg[j].data_type;
461        if (p_cb->sup_feature.mdep[i].mdep_cfg.data_cfg[j].desp[0] != '\0') {
462          sup_feature_list.list_elem[cnt].p_mdep_desp =
463              p_cb->sup_feature.mdep[i].mdep_cfg.data_cfg[j].desp;
464        } else {
465          sup_feature_list.list_elem[cnt].p_mdep_desp = NULL;
466        }
467
468        cnt++;
469        if (cnt == BTA_HL_NUM_SUP_FEATURE_ELEMS) {
470          result = false;
471          break;
472        }
473      }
474    }
475    sup_feature_list.num_elems = cnt;
476    result &= bta_hl_add_sup_feature_list(p_cb->sdp_handle,
477                                          sup_feature_list.num_elems,
478                                          sup_feature_list.list_elem);
479  }
480  if (result) {
481    result &= SDP_AddAttribute(p_cb->sdp_handle, ATTR_ID_HDP_DATA_EXCH_SPEC,
482                               UINT_DESC_TYPE, (uint32_t)1,
483                               (uint8_t*)&data_exchange_spec);
484  }
485
486  if (result) {
487    result &=
488        SDP_AddAttribute(p_cb->sdp_handle, ATTR_ID_HDP_MCAP_SUP_PROC,
489                         UINT_DESC_TYPE, (uint32_t)1, (uint8_t*)&mcap_sup_proc);
490  }
491
492  if (result) {
493    result &= SDP_AddUuidSequence(p_cb->sdp_handle, ATTR_ID_BROWSE_GROUP_LIST,
494                                  1, browse_list);
495  }
496
497  if (result) {
498    for (i = 0; i < num_services; i++) {
499      bta_sys_add_uuid(svc_class_id_list[i]);
500      APPL_TRACE_DEBUG("dbg bta_sys_add_uuid i=%d uuid=0x%x", i,
501                       svc_class_id_list[i]);  // todo
502    }
503  } else {
504    if (p_cb->sdp_handle) {
505      SDP_DeleteRecord(p_cb->sdp_handle);
506      p_cb->sdp_handle = 0;
507    }
508    status = BTA_HL_STATUS_SDP_FAIL;
509  }
510#if (BTA_HL_DEBUG == TRUE)
511  APPL_TRACE_DEBUG("bta_hl_sdp_register status=%s", bta_hl_status_code(status));
512#endif
513  return status;
514}
515
516/*******************************************************************************
517 *
518 * Function         bta_hl_find_sink_or_src_srv_class_in_db
519 *
520 * Description      This function queries an SDP database for either a HDP Sink
521 *                  or Source service class ID.
522 *                  If the p_start_rec pointer is NULL, it looks from the
523 *                  beginning of the database, else it continues from the next
524 *                  record after p_start_rec.
525 *
526 * Returns          Pointer to record containing service class, or NULL
527 *
528 ******************************************************************************/
529tSDP_DISC_REC* bta_hl_find_sink_or_src_srv_class_in_db(
530    const tSDP_DISCOVERY_DB* p_db, const tSDP_DISC_REC* p_start_rec) {
531  tSDP_DISC_REC* p_rec;
532  tSDP_DISC_ATTR *p_attr, *p_sattr;
533
534  /* Must have a valid database */
535  if (p_db == NULL) return (NULL);
536
537  if (!p_start_rec) {
538    p_rec = p_db->p_first_rec;
539  } else {
540    p_rec = p_start_rec->p_next_rec;
541  }
542
543  while (p_rec) {
544    p_attr = p_rec->p_first_attr;
545    while (p_attr) {
546      if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) &&
547          (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) ==
548           DATA_ELE_SEQ_DESC_TYPE)) {
549        for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
550             p_sattr = p_sattr->p_next_attr) {
551          if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) &&
552              (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) &&
553              ((p_sattr->attr_value.v.u16 == UUID_SERVCLASS_HDP_SINK) ||
554               (p_sattr->attr_value.v.u16 == UUID_SERVCLASS_HDP_SOURCE))) {
555            return (p_rec);
556          }
557        }
558        break;
559      }
560
561      p_attr = p_attr->p_next_attr;
562    }
563
564    p_rec = p_rec->p_next_rec;
565  }
566/* If here, no matching UUID found */
567
568#if (BTA_HL_DEBUG == TRUE)
569  APPL_TRACE_DEBUG("bta_hl_find_sink_or_src_srv_class_in_db failed");
570#endif
571
572  return (NULL);
573}
574#endif /* HL_INCLUDED */
575