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 interface functions
22 *
23 ******************************************************************************/
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include "bt_common.h"
30#include "bt_target.h"
31#include "bt_utils.h"
32#include "hcidefs.h"
33#include "hcimsgs.h"
34#include "l2cdefs.h"
35
36#include "btu.h"
37#include "sdp_api.h"
38#include "sdpint.h"
39
40#include "osi/include/osi.h"
41
42using bluetooth::Uuid;
43
44/**********************************************************************
45 *   C L I E N T    F U N C T I O N    P R O T O T Y P E S            *
46 **********************************************************************/
47
48/*******************************************************************************
49 *
50 * Function         SDP_InitDiscoveryDb
51 *
52 * Description      This function is called to initialize a discovery database.
53 *
54 * Parameters:      p_db        - (input) address of an area of memory where the
55 *                                        discovery database is managed.
56 *                  len         - (input) size (in bytes) of the memory
57 *                                 NOTE: This must be larger than
58 *                                       sizeof(tSDP_DISCOVERY_DB)
59 *                  num_uuid    - (input) number of UUID filters applied
60 *                  p_uuid_list - (input) list of UUID filters
61 *                  num_attr    - (input) number of attribute filters applied
62 *                  p_attr_list - (input) list of attribute filters
63 *
64 *
65 * Returns          bool
66 *                          true if successful
67 *                          false if one or more parameters are bad
68 *
69 ******************************************************************************/
70bool SDP_InitDiscoveryDb(tSDP_DISCOVERY_DB* p_db, uint32_t len,
71                         uint16_t num_uuid, const Uuid* p_uuid_list,
72                         uint16_t num_attr, uint16_t* p_attr_list) {
73  uint16_t xx;
74
75  /* verify the parameters */
76  if (p_db == NULL || (sizeof(tSDP_DISCOVERY_DB) > len) ||
77      num_attr > SDP_MAX_ATTR_FILTERS || num_uuid > SDP_MAX_UUID_FILTERS) {
78    SDP_TRACE_ERROR(
79        "SDP_InitDiscoveryDb Illegal param: p_db 0x%x, len %d, num_uuid %d, "
80        "num_attr %d",
81        PTR_TO_UINT(p_db), len, num_uuid, num_attr);
82
83    return (false);
84  }
85
86  memset(p_db, 0, (size_t)len);
87
88  p_db->mem_size = len - sizeof(tSDP_DISCOVERY_DB);
89  p_db->mem_free = p_db->mem_size;
90  p_db->p_first_rec = NULL;
91  p_db->p_free_mem = (uint8_t*)(p_db + 1);
92
93  for (xx = 0; xx < num_uuid; xx++) p_db->uuid_filters[xx] = *p_uuid_list++;
94
95  p_db->num_uuid_filters = num_uuid;
96
97  for (xx = 0; xx < num_attr; xx++) p_db->attr_filters[xx] = *p_attr_list++;
98
99  /* sort attributes */
100  sdpu_sort_attr_list(num_attr, p_db);
101
102  p_db->num_attr_filters = num_attr;
103  return (true);
104}
105
106/*******************************************************************************
107 *
108 * Function         SDP_CancelServiceSearch
109 *
110 * Description      This function cancels an active query to an SDP server.
111 *
112 * Returns          true if discovery cancelled, false if a matching activity is
113 *                  not found.
114 *
115 ******************************************************************************/
116bool SDP_CancelServiceSearch(tSDP_DISCOVERY_DB* p_db) {
117  tCONN_CB* p_ccb = sdpu_find_ccb_by_db(p_db);
118  if (!p_ccb) return (false);
119
120  sdp_disconnect(p_ccb, SDP_CANCEL);
121  p_ccb->disc_state = SDP_DISC_WAIT_CANCEL;
122  return (true);
123}
124
125/*******************************************************************************
126 *
127 * Function         SDP_ServiceSearchRequest
128 *
129 * Description      This function queries an SDP server for information.
130 *
131 * Returns          true if discovery started, false if failed.
132 *
133 ******************************************************************************/
134bool SDP_ServiceSearchRequest(const RawAddress& p_bd_addr,
135                              tSDP_DISCOVERY_DB* p_db,
136                              tSDP_DISC_CMPL_CB* p_cb) {
137  tCONN_CB* p_ccb;
138
139  /* Specific BD address */
140  p_ccb = sdp_conn_originate(p_bd_addr);
141
142  if (!p_ccb) return (false);
143
144  p_ccb->disc_state = SDP_DISC_WAIT_CONN;
145  p_ccb->p_db = p_db;
146  p_ccb->p_cb = p_cb;
147
148  return (true);
149}
150
151/*******************************************************************************
152 *
153 * Function         SDP_ServiceSearchAttributeRequest
154 *
155 * Description      This function queries an SDP server for information.
156 *
157 *                  The difference between this API function and the function
158 *                  SDP_ServiceSearchRequest is that this one does a
159 *                  combined ServiceSearchAttributeRequest SDP function.
160 *                  (This is for Unplug Testing)
161 *
162 * Returns          true if discovery started, false if failed.
163 *
164 ******************************************************************************/
165bool SDP_ServiceSearchAttributeRequest(const RawAddress& p_bd_addr,
166                                       tSDP_DISCOVERY_DB* p_db,
167                                       tSDP_DISC_CMPL_CB* p_cb) {
168  tCONN_CB* p_ccb;
169
170  /* Specific BD address */
171  p_ccb = sdp_conn_originate(p_bd_addr);
172
173  if (!p_ccb) return (false);
174
175  p_ccb->disc_state = SDP_DISC_WAIT_CONN;
176  p_ccb->p_db = p_db;
177  p_ccb->p_cb = p_cb;
178
179  p_ccb->is_attr_search = true;
180
181  return (true);
182}
183/*******************************************************************************
184 *
185 * Function         SDP_ServiceSearchAttributeRequest2
186 *
187 * Description      This function queries an SDP server for information.
188 *
189 *                  The difference between this API function and the function
190 *                  SDP_ServiceSearchRequest is that this one does a
191 *                  combined ServiceSearchAttributeRequest SDP function.
192 *                  (This is for Unplug Testing)
193 *
194 * Returns          true if discovery started, false if failed.
195 *
196 ******************************************************************************/
197bool SDP_ServiceSearchAttributeRequest2(const RawAddress& p_bd_addr,
198                                        tSDP_DISCOVERY_DB* p_db,
199                                        tSDP_DISC_CMPL_CB2* p_cb2,
200                                        void* user_data) {
201  tCONN_CB* p_ccb;
202
203  /* Specific BD address */
204  p_ccb = sdp_conn_originate(p_bd_addr);
205
206  if (!p_ccb) return (false);
207
208  p_ccb->disc_state = SDP_DISC_WAIT_CONN;
209  p_ccb->p_db = p_db;
210  p_ccb->p_cb2 = p_cb2;
211
212  p_ccb->is_attr_search = true;
213  p_ccb->user_data = user_data;
214
215  return (true);
216}
217
218/*******************************************************************************
219 *
220 * Function         SDP_FindAttributeInDb
221 *
222 * Description      This function queries an SDP database for a specific
223 *                  attribute. If the p_start_rec pointer is NULL, it looks from
224 *                  the beginning of the database, else it continues from the
225 *                  next record after p_start_rec.
226 *
227 * Returns          Pointer to matching record, or NULL
228 *
229 ******************************************************************************/
230tSDP_DISC_REC* SDP_FindAttributeInDb(tSDP_DISCOVERY_DB* p_db, uint16_t attr_id,
231                                     tSDP_DISC_REC* p_start_rec) {
232  tSDP_DISC_REC* p_rec;
233  tSDP_DISC_ATTR* p_attr;
234
235  /* Must have a valid database */
236  if (p_db == NULL) return (NULL);
237
238  if (!p_start_rec)
239    p_rec = p_db->p_first_rec;
240  else
241    p_rec = p_start_rec->p_next_rec;
242
243  while (p_rec) {
244    p_attr = p_rec->p_first_attr;
245    while (p_attr) {
246      if (p_attr->attr_id == attr_id) return (p_rec);
247
248      p_attr = p_attr->p_next_attr;
249    }
250
251    p_rec = p_rec->p_next_rec;
252  }
253  /* If here, no matching attribute found */
254  return (NULL);
255}
256
257/*******************************************************************************
258 *
259 * Function         SDP_FindAttributeInRec
260 *
261 * Description      This function searches an SDP discovery record for a
262 *                  specific attribute.
263 *
264 * Returns          Pointer to matching attribute entry, or NULL
265 *
266 ******************************************************************************/
267tSDP_DISC_ATTR* SDP_FindAttributeInRec(tSDP_DISC_REC* p_rec, uint16_t attr_id) {
268  tSDP_DISC_ATTR* p_attr;
269
270  p_attr = p_rec->p_first_attr;
271  while (p_attr) {
272    if (p_attr->attr_id == attr_id) return (p_attr);
273
274    p_attr = p_attr->p_next_attr;
275  }
276
277  /* If here, no matching attribute found */
278  return (NULL);
279}
280
281/*******************************************************************************
282 *
283 * Function         SDP_FindServiceUUIDInRec
284 *
285 * Description      This function is called to read the service UUID within a
286 *                  record if there is any.
287 *
288 * Parameters:      p_rec      - pointer to a SDP record.
289 *                  p_uuid     - output parameter to save the UUID found.
290 *
291 * Returns          true if found, otherwise false.
292 *
293 ******************************************************************************/
294bool SDP_FindServiceUUIDInRec(tSDP_DISC_REC* p_rec, Uuid* p_uuid) {
295  tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr;
296
297  p_attr = p_rec->p_first_attr;
298
299  while (p_attr) {
300    if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) &&
301        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
302      for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
303           p_sattr = p_sattr->p_next_attr) {
304        if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) {
305          if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == Uuid::kNumBytes16) {
306            *p_uuid = Uuid::From16Bit(p_sattr->attr_value.v.u16);
307          } else if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) ==
308                     Uuid::kNumBytes128) {
309            *p_uuid = Uuid::From128BitBE(p_sattr->attr_value.v.array);
310          } else if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) ==
311                     Uuid::kNumBytes32) {
312            *p_uuid = Uuid::From32Bit(p_sattr->attr_value.v.u32);
313          }
314
315          return (true);
316        }
317
318        /* Checking for Toyota G Block Car Kit:
319        **  This car kit puts an extra data element sequence
320        **  where the UUID is suppose to be!!!
321        */
322        else {
323          if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) ==
324              DATA_ELE_SEQ_DESC_TYPE) {
325            /* Look through data element sequence until no more UUIDs */
326            for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr;
327                 p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr) {
328              /* Increment past this to see if the next attribut is UUID */
329              if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) ==
330                   UUID_DESC_TYPE)
331                  /* only support 16 bits UUID for now */
332                  && (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2)) {
333                *p_uuid = Uuid::From16Bit(p_extra_sattr->attr_value.v.u16);
334                return (true);
335              }
336            }
337          }
338        }
339      }
340      break;
341    } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) {
342      if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE)
343          /* only support 16 bits UUID for now */
344          && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2)) {
345        *p_uuid = Uuid::From16Bit(p_attr->attr_value.v.u16);
346        return (true);
347      }
348    }
349    p_attr = p_attr->p_next_attr;
350  }
351  return false;
352}
353
354/*******************************************************************************
355 *
356 * Function         SDP_FindServiceUUIDInRec_128bit
357 *
358 * Description      This function is called to read the 128-bit service UUID
359 *                  within a record if there is any.
360 *
361 * Parameters:      p_rec      - pointer to a SDP record.
362 *                  p_uuid     - output parameter to save the UUID found.
363 *
364 * Returns          true if found, otherwise false.
365 *
366 ******************************************************************************/
367bool SDP_FindServiceUUIDInRec_128bit(tSDP_DISC_REC* p_rec, Uuid* p_uuid) {
368  tSDP_DISC_ATTR* p_attr = p_rec->p_first_attr;
369  while (p_attr) {
370    if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) &&
371        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
372      tSDP_DISC_ATTR* p_sattr = p_attr->attr_value.v.p_sub_attr;
373      while (p_sattr) {
374        if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) {
375          /* only support 128 bits UUID for now */
376          if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 16) {
377            *p_uuid = Uuid::From128BitBE(p_sattr->attr_value.v.array);
378          }
379          return (true);
380        }
381
382        p_sattr = p_sattr->p_next_attr;
383      }
384      break;
385    } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) {
386      if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE)
387          /* only support 128 bits UUID for now */
388          && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 16)) {
389        *p_uuid = Uuid::From128BitBE(p_attr->attr_value.v.array);
390        return (true);
391      }
392    }
393    p_attr = p_attr->p_next_attr;
394  }
395  return false;
396}
397
398/*******************************************************************************
399 *
400 * Function         SDP_FindServiceInDb
401 *
402 * Description      This function queries an SDP database for a specific
403 *                  service. If the p_start_rec pointer is NULL, it looks from
404 *                  the beginning of the database, else it continues from the
405 *                  next record after p_start_rec.
406 *
407 * Returns          Pointer to record containing service class, or NULL
408 *
409 ******************************************************************************/
410tSDP_DISC_REC* SDP_FindServiceInDb(tSDP_DISCOVERY_DB* p_db,
411                                   uint16_t service_uuid,
412                                   tSDP_DISC_REC* p_start_rec) {
413  tSDP_DISC_REC* p_rec;
414  tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr;
415
416  /* Must have a valid database */
417  if (p_db == NULL) return (NULL);
418
419  if (!p_start_rec)
420    p_rec = p_db->p_first_rec;
421  else
422    p_rec = p_start_rec->p_next_rec;
423
424  while (p_rec) {
425    p_attr = p_rec->p_first_attr;
426    while (p_attr) {
427      if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) &&
428          (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) ==
429           DATA_ELE_SEQ_DESC_TYPE)) {
430        for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
431             p_sattr = p_sattr->p_next_attr) {
432          if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) &&
433              (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)) {
434            SDP_TRACE_DEBUG(
435                "SDP_FindServiceInDb - p_sattr value = 0x%x serviceuuid = 0x%x",
436                p_sattr->attr_value.v.u16, service_uuid);
437            if (service_uuid == UUID_SERVCLASS_HDP_PROFILE) {
438              if ((p_sattr->attr_value.v.u16 == UUID_SERVCLASS_HDP_SOURCE) ||
439                  (p_sattr->attr_value.v.u16 == UUID_SERVCLASS_HDP_SINK)) {
440                SDP_TRACE_DEBUG(
441                    "SDP_FindServiceInDb found HDP source or sink\n");
442                return (p_rec);
443              }
444            }
445          }
446
447          if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE &&
448              (service_uuid == 0 ||
449               (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2 &&
450                p_sattr->attr_value.v.u16 == service_uuid)))
451          /* for a specific uuid, or any one */
452          {
453            return (p_rec);
454          }
455
456          /* Checking for Toyota G Block Car Kit:
457          **  This car kit puts an extra data element sequence
458          **  where the UUID is suppose to be!!!
459          */
460          else {
461            if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) ==
462                DATA_ELE_SEQ_DESC_TYPE) {
463              /* Look through data element sequence until no more UUIDs */
464              for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr;
465                   p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr) {
466                /* Increment past this to see if the next attribut is UUID */
467                if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) ==
468                     UUID_DESC_TYPE) &&
469                    (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2)
470                    /* for a specific uuid, or any one */
471                    && ((p_extra_sattr->attr_value.v.u16 == service_uuid) ||
472                        (service_uuid == 0))) {
473                  return (p_rec);
474                }
475              }
476            }
477          }
478        }
479        break;
480      } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) {
481        if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) &&
482            (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2)
483            /* find a specific UUID or anyone */
484            &&
485            ((p_attr->attr_value.v.u16 == service_uuid) || service_uuid == 0))
486          return (p_rec);
487      }
488
489      p_attr = p_attr->p_next_attr;
490    }
491
492    p_rec = p_rec->p_next_rec;
493  }
494  /* If here, no matching UUID found */
495  return (NULL);
496}
497
498/*******************************************************************************
499 *
500 * Function         SDP_FindServiceInDb_128bit
501 *
502 * Description      Query an SDP database for a specific service. If the
503 *                  p_start_rec pointer is NULL, it looks from the beginning of
504 *                  the database, else it continues from the next record after
505 *                  p_start_rec.
506 *
507 *                  This function is kept separate from SDP_FindServiceInDb
508 *                  since that API is expected to return only 16-bit UUIDs
509 *
510 * Returns          Pointer to record containing service class, or NULL
511 *
512 ******************************************************************************/
513tSDP_DISC_REC* SDP_FindServiceInDb_128bit(tSDP_DISCOVERY_DB* p_db,
514                                          tSDP_DISC_REC* p_start_rec) {
515  tSDP_DISC_REC* p_rec;
516  tSDP_DISC_ATTR *p_attr, *p_sattr;
517
518  /* Must have a valid database */
519  if (p_db == NULL) return (NULL);
520
521  if (!p_start_rec)
522    p_rec = p_db->p_first_rec;
523  else
524    p_rec = p_start_rec->p_next_rec;
525
526  while (p_rec) {
527    p_attr = p_rec->p_first_attr;
528    while (p_attr) {
529      if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) &&
530          (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) ==
531           DATA_ELE_SEQ_DESC_TYPE)) {
532        for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
533             p_sattr = p_sattr->p_next_attr) {
534          if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) &&
535              (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 16)) {
536            return (p_rec);
537          }
538        }
539        break;
540      } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) {
541        if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) &&
542            (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 16))
543          return (p_rec);
544      }
545
546      p_attr = p_attr->p_next_attr;
547    }
548
549    p_rec = p_rec->p_next_rec;
550  }
551  /* If here, no matching UUID found */
552  return (NULL);
553}
554
555/*******************************************************************************
556 *
557 * Function         SDP_FindServiceUUIDInDb
558 *
559 * Description      Query an SDP database for a specific service. If the
560 *                  p_start_rec pointer is NULL, it looks from the beginning of
561 *                  the database, else it continues from the next record after
562 *                  p_start_rec.
563 *
564 * NOTE             the only difference between this function and the previous
565 *                  function "SDP_FindServiceInDb()" is that this function takes
566 *                  a Uuid input
567 *
568 * Returns          Pointer to record containing service class, or NULL
569 *
570 ******************************************************************************/
571tSDP_DISC_REC* SDP_FindServiceUUIDInDb(tSDP_DISCOVERY_DB* p_db,
572                                       const Uuid& uuid,
573                                       tSDP_DISC_REC* p_start_rec) {
574  tSDP_DISC_REC* p_rec;
575  tSDP_DISC_ATTR *p_attr, *p_sattr;
576
577  /* Must have a valid database */
578  if (p_db == NULL) return (NULL);
579
580  if (!p_start_rec)
581    p_rec = p_db->p_first_rec;
582  else
583    p_rec = p_start_rec->p_next_rec;
584
585  while (p_rec) {
586    p_attr = p_rec->p_first_attr;
587    while (p_attr) {
588      if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) &&
589          (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) ==
590           DATA_ELE_SEQ_DESC_TYPE)) {
591        for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
592             p_sattr = p_sattr->p_next_attr) {
593          if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) {
594            if (sdpu_compare_uuid_with_attr(uuid, p_sattr)) return (p_rec);
595          }
596        }
597        break;
598      } else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) {
599        if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) {
600          if (sdpu_compare_uuid_with_attr(uuid, p_attr)) return (p_rec);
601        }
602      }
603
604      p_attr = p_attr->p_next_attr;
605    }
606
607    p_rec = p_rec->p_next_rec;
608  }
609  /* If here, no matching UUID found */
610  return (NULL);
611}
612
613/*******************************************************************************
614 *
615 * Function         sdp_fill_proto_elem
616 *
617 * Description      This function retrieves the protocol element.
618 *
619 * Returns          true if found, false if not
620 *                  If found, the passed protocol list element is filled in.
621 *
622 ******************************************************************************/
623static bool sdp_fill_proto_elem(tSDP_DISC_ATTR* p_attr, uint16_t layer_uuid,
624                                tSDP_PROTOCOL_ELEM* p_elem) {
625  tSDP_DISC_ATTR* p_sattr;
626
627  /* Walk through the protocol descriptor list */
628  for (p_attr = p_attr->attr_value.v.p_sub_attr; p_attr;
629       p_attr = p_attr->p_next_attr) {
630    /* Safety check - each entry should itself be a sequence */
631    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
632      return (false);
633
634    /* Now, see if the entry contains the layer we are interested in */
635    for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
636         p_sattr = p_sattr->p_next_attr) {
637      /* SDP_TRACE_DEBUG ("SDP - p_sattr 0x%x, layer_uuid:0x%x, u16:0x%x####",
638          p_sattr, layer_uuid, p_sattr->attr_value.v.u16); */
639
640      if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) &&
641          (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) &&
642          (p_sattr->attr_value.v.u16 == layer_uuid)) {
643        /* Bingo. Now fill in the passed element */
644        p_elem->protocol_uuid = layer_uuid;
645        p_elem->num_params = 0;
646
647        /* Store the parameters, if any */
648        for (p_sattr = p_sattr->p_next_attr; p_sattr;
649             p_sattr = p_sattr->p_next_attr) {
650          if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) != UINT_DESC_TYPE)
651            break;
652
653          if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)
654            p_elem->params[p_elem->num_params++] = p_sattr->attr_value.v.u16;
655          else
656            p_elem->params[p_elem->num_params++] = p_sattr->attr_value.v.u8;
657
658          if (p_elem->num_params >= SDP_MAX_PROTOCOL_PARAMS) break;
659        }
660        return (true);
661      }
662    }
663  }
664
665  return (false);
666}
667
668/*******************************************************************************
669 *
670 * Function         SDP_FindProtocolListElemInRec
671 *
672 * Description      This function looks at a specific discovery record for a
673 *                  protocol list element.
674 *
675 * Returns          true if found, false if not
676 *                  If found, the passed protocol list element is filled in.
677 *
678 ******************************************************************************/
679bool SDP_FindProtocolListElemInRec(tSDP_DISC_REC* p_rec, uint16_t layer_uuid,
680                                   tSDP_PROTOCOL_ELEM* p_elem) {
681  tSDP_DISC_ATTR* p_attr;
682
683  p_attr = p_rec->p_first_attr;
684  while (p_attr) {
685    /* Find the protocol descriptor list */
686    if ((p_attr->attr_id == ATTR_ID_PROTOCOL_DESC_LIST) &&
687        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
688      return sdp_fill_proto_elem(p_attr, layer_uuid, p_elem);
689    }
690    p_attr = p_attr->p_next_attr;
691  }
692  /* If here, no match found */
693  return (false);
694}
695
696/*******************************************************************************
697 *
698 * Function         SDP_FindAddProtoListsElemInRec
699 *
700 * Description      This function looks at a specific discovery record for a
701 *                  protocol list element.
702 *
703 * Returns          true if found, false if not
704 *                  If found, the passed protocol list element is filled in.
705 *
706 ******************************************************************************/
707bool SDP_FindAddProtoListsElemInRec(tSDP_DISC_REC* p_rec, uint16_t layer_uuid,
708                                    tSDP_PROTOCOL_ELEM* p_elem) {
709  tSDP_DISC_ATTR *p_attr, *p_sattr;
710  bool ret = false;
711
712  p_attr = p_rec->p_first_attr;
713  while (p_attr) {
714    /* Find the additional protocol descriptor list attribute */
715    if ((p_attr->attr_id == ATTR_ID_ADDITION_PROTO_DESC_LISTS) &&
716        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
717      for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
718           p_sattr = p_sattr->p_next_attr) {
719        /* Safety check - each entry should itself be a sequence */
720        if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) ==
721            DATA_ELE_SEQ_DESC_TYPE) {
722          ret = sdp_fill_proto_elem(p_sattr, layer_uuid, p_elem);
723          if (ret) break;
724        }
725      }
726      return ret;
727    }
728    p_attr = p_attr->p_next_attr;
729  }
730  /* If here, no match found */
731  return (false);
732}
733
734/*******************************************************************************
735 *
736 * Function         SDP_FindProfileVersionInRec
737 *
738 * Description      This function looks at a specific discovery record for the
739 *                  Profile list descriptor, and pulls out the version number.
740 *                  The version number consists of an 8-bit major version and
741 *                  an 8-bit minor version.
742 *
743 * Returns          true if found, false if not
744 *                  If found, the major and minor version numbers that were
745 *                  passed in are filled in.
746 *
747 ******************************************************************************/
748bool SDP_FindProfileVersionInRec(tSDP_DISC_REC* p_rec, uint16_t profile_uuid,
749                                 uint16_t* p_version) {
750  tSDP_DISC_ATTR *p_attr, *p_sattr;
751
752  p_attr = p_rec->p_first_attr;
753  while (p_attr) {
754    /* Find the profile descriptor list */
755    if ((p_attr->attr_id == ATTR_ID_BT_PROFILE_DESC_LIST) &&
756        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
757      /* Walk through the protocol descriptor list */
758      for (p_attr = p_attr->attr_value.v.p_sub_attr; p_attr;
759           p_attr = p_attr->p_next_attr) {
760        /* Safety check - each entry should itself be a sequence */
761        if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
762          return (false);
763
764        /* Now, see if the entry contains the profile UUID we are interested in
765         */
766        for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
767             p_sattr = p_sattr->p_next_attr) {
768          if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) &&
769              (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) ==
770               2) /* <- This is bytes, not size code! */
771              && (p_sattr->attr_value.v.u16 == profile_uuid)) {
772            /* Now fill in the major and minor numbers */
773            /* if the attribute matches the description for version (type UINT,
774             * size 2 bytes) */
775            p_sattr = p_sattr->p_next_attr;
776
777            if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) ==
778                 UINT_DESC_TYPE) &&
779                (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)) {
780              /* The high order 8 bits is the major number, low order is the
781               * minor number (big endian) */
782              *p_version = p_sattr->attr_value.v.u16;
783
784              return (true);
785            } else
786              return (false); /* The type and/or size was not valid for the
787                                 profile list version */
788          }
789        }
790      }
791
792      return (false);
793    }
794    p_attr = p_attr->p_next_attr;
795  }
796
797  /* If here, no match found */
798  return (false);
799}
800
801/*******************************************************************************
802 *                   Device Identification (DI) Client Functions
803 ******************************************************************************/
804
805/*******************************************************************************
806 *
807 * Function         SDP_DiDiscover
808 *
809 * Description      This function queries a remote device for DI information.
810 *
811 * Returns          SDP_SUCCESS if query started successfully, else error
812 *
813 ******************************************************************************/
814uint16_t SDP_DiDiscover(const RawAddress& remote_device,
815                        tSDP_DISCOVERY_DB* p_db, uint32_t len,
816                        tSDP_DISC_CMPL_CB* p_cb) {
817  uint16_t result = SDP_DI_DISC_FAILED;
818  uint16_t num_uuids = 1;
819  uint16_t di_uuid = UUID_SERVCLASS_PNP_INFORMATION;
820
821  /* build uuid for db init */
822  Uuid init_uuid = Uuid::From16Bit(di_uuid);
823
824  if (SDP_InitDiscoveryDb(p_db, len, num_uuids, &init_uuid, 0, NULL))
825    if (SDP_ServiceSearchRequest(remote_device, p_db, p_cb))
826      result = SDP_SUCCESS;
827
828  return result;
829}
830
831/*******************************************************************************
832 *
833 * Function         SDP_GetNumDiRecords
834 *
835 * Description      Searches specified database for DI records
836 *
837 * Returns          number of DI records found
838 *
839 ******************************************************************************/
840uint8_t SDP_GetNumDiRecords(tSDP_DISCOVERY_DB* p_db) {
841  uint8_t num_records = 0;
842  tSDP_DISC_REC* p_curr_record = NULL;
843
844  do {
845    p_curr_record = SDP_FindServiceInDb(p_db, UUID_SERVCLASS_PNP_INFORMATION,
846                                        p_curr_record);
847    if (p_curr_record) num_records++;
848  } while (p_curr_record);
849
850  return num_records;
851}
852
853/*******************************************************************************
854 *
855 * Function         SDP_AttrStringCopy
856 *
857 * Description      This function copy given attribute to specified buffer as a
858 *                  string
859 *
860 * Returns          none
861 *
862 ******************************************************************************/
863static void SDP_AttrStringCopy(char* dst, tSDP_DISC_ATTR* p_attr,
864                               uint16_t dst_size) {
865  if (dst == NULL) return;
866  if (p_attr) {
867    uint16_t len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
868    if (len > dst_size - 1) {
869      len = dst_size - 1;
870    }
871    memcpy(dst, (char*)p_attr->attr_value.v.array, len);
872    dst[len] = '\0';
873  } else {
874    dst[0] = '\0';
875  }
876}
877
878/*******************************************************************************
879 *
880 * Function         SDP_GetDiRecord
881 *
882 * Description      This function retrieves a remote device's DI record from
883 *                  the specified database.
884 *
885 * Returns          SDP_SUCCESS if record retrieved, else error
886 *
887 ******************************************************************************/
888uint16_t SDP_GetDiRecord(uint8_t get_record_index,
889                         tSDP_DI_GET_RECORD* p_device_info,
890                         tSDP_DISCOVERY_DB* p_db) {
891  uint16_t result = SDP_NO_DI_RECORD_FOUND;
892  uint8_t curr_record_index = 1;
893
894  tSDP_DISC_REC* p_curr_record = NULL;
895
896  /* find the requested SDP record in the discovery database */
897  do {
898    p_curr_record = SDP_FindServiceInDb(p_db, UUID_SERVCLASS_PNP_INFORMATION,
899                                        p_curr_record);
900    if (p_curr_record) {
901      if (curr_record_index++ == get_record_index) {
902        result = SDP_SUCCESS;
903        break;
904      }
905    }
906  } while (p_curr_record);
907
908  if (result == SDP_SUCCESS) {
909    /* copy the information from the SDP record to the DI record */
910    tSDP_DISC_ATTR* p_curr_attr = NULL;
911
912    /* ClientExecutableURL is optional */
913    p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_CLIENT_EXE_URL);
914    SDP_AttrStringCopy(p_device_info->rec.client_executable_url, p_curr_attr,
915                       SDP_MAX_ATTR_LEN);
916
917    /* Service Description is optional */
918    p_curr_attr =
919        SDP_FindAttributeInRec(p_curr_record, ATTR_ID_SERVICE_DESCRIPTION);
920    SDP_AttrStringCopy(p_device_info->rec.service_description, p_curr_attr,
921                       SDP_MAX_ATTR_LEN);
922
923    /* DocumentationURL is optional */
924    p_curr_attr =
925        SDP_FindAttributeInRec(p_curr_record, ATTR_ID_DOCUMENTATION_URL);
926    SDP_AttrStringCopy(p_device_info->rec.documentation_url, p_curr_attr,
927                       SDP_MAX_ATTR_LEN);
928
929    p_curr_attr =
930        SDP_FindAttributeInRec(p_curr_record, ATTR_ID_SPECIFICATION_ID);
931    if (p_curr_attr)
932      p_device_info->spec_id = p_curr_attr->attr_value.v.u16;
933    else
934      result = SDP_ERR_ATTR_NOT_PRESENT;
935
936    p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_VENDOR_ID);
937    if (p_curr_attr)
938      p_device_info->rec.vendor = p_curr_attr->attr_value.v.u16;
939    else
940      result = SDP_ERR_ATTR_NOT_PRESENT;
941
942    p_curr_attr =
943        SDP_FindAttributeInRec(p_curr_record, ATTR_ID_VENDOR_ID_SOURCE);
944    if (p_curr_attr)
945      p_device_info->rec.vendor_id_source = p_curr_attr->attr_value.v.u16;
946    else
947      result = SDP_ERR_ATTR_NOT_PRESENT;
948
949    p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_PRODUCT_ID);
950    if (p_curr_attr)
951      p_device_info->rec.product = p_curr_attr->attr_value.v.u16;
952    else
953      result = SDP_ERR_ATTR_NOT_PRESENT;
954
955    p_curr_attr =
956        SDP_FindAttributeInRec(p_curr_record, ATTR_ID_PRODUCT_VERSION);
957    if (p_curr_attr)
958      p_device_info->rec.version = p_curr_attr->attr_value.v.u16;
959    else
960      result = SDP_ERR_ATTR_NOT_PRESENT;
961
962    p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_PRIMARY_RECORD);
963    if (p_curr_attr)
964      p_device_info->rec.primary_record = (bool)p_curr_attr->attr_value.v.u8;
965    else
966      result = SDP_ERR_ATTR_NOT_PRESENT;
967  }
968
969  return result;
970}
971
972/*******************************************************************************
973 *                   Device Identification (DI) Server Functions
974 ******************************************************************************/
975
976/*******************************************************************************
977 *
978 * Function         SDP_SetLocalDiRecord
979 *
980 * Description      This function adds a DI record to the local SDP database.
981 *
982 *
983 *
984 * Returns          Returns SDP_SUCCESS if record added successfully, else error
985 *
986 ******************************************************************************/
987uint16_t SDP_SetLocalDiRecord(tSDP_DI_RECORD* p_device_info,
988                              uint32_t* p_handle) {
989#if (SDP_SERVER_ENABLED == TRUE)
990  uint16_t result = SDP_SUCCESS;
991  uint32_t handle;
992  uint16_t di_uuid = UUID_SERVCLASS_PNP_INFORMATION;
993  uint16_t di_specid = BLUETOOTH_DI_SPECIFICATION;
994  uint8_t temp_u16[2];
995  uint8_t* p_temp;
996  uint8_t u8;
997
998  *p_handle = 0;
999  if (p_device_info == NULL) return SDP_ILLEGAL_PARAMETER;
1000
1001  /* if record is to be primary record, get handle to replace old primary */
1002  if (p_device_info->primary_record && sdp_cb.server_db.di_primary_handle)
1003    handle = sdp_cb.server_db.di_primary_handle;
1004  else {
1005    handle = SDP_CreateRecord();
1006    if (handle == 0) return SDP_NO_RESOURCES;
1007  }
1008
1009  *p_handle = handle;
1010
1011  /* build the SDP entry */
1012  /* Add the UUID to the Service Class ID List */
1013  if (!(SDP_AddServiceClassIdList(handle, 1, &di_uuid)))
1014    result = SDP_DI_REG_FAILED;
1015
1016  /* mandatory */
1017  if (result == SDP_SUCCESS) {
1018    p_temp = temp_u16;
1019    UINT16_TO_BE_STREAM(p_temp, di_specid);
1020    if (!(SDP_AddAttribute(handle, ATTR_ID_SPECIFICATION_ID, UINT_DESC_TYPE,
1021                           sizeof(di_specid), temp_u16)))
1022      result = SDP_DI_REG_FAILED;
1023  }
1024
1025  /* optional - if string is null, do not add attribute */
1026  if (result == SDP_SUCCESS) {
1027    if (p_device_info->client_executable_url[0] != '\0') {
1028      if (!((strlen(p_device_info->client_executable_url) + 1 <=
1029             SDP_MAX_ATTR_LEN) &&
1030            SDP_AddAttribute(
1031                handle, ATTR_ID_CLIENT_EXE_URL, URL_DESC_TYPE,
1032                (uint32_t)(strlen(p_device_info->client_executable_url) + 1),
1033                (uint8_t*)p_device_info->client_executable_url)))
1034        result = SDP_DI_REG_FAILED;
1035    }
1036  }
1037
1038  /* optional - if string is null, do not add attribute */
1039  if (result == SDP_SUCCESS) {
1040    if (p_device_info->service_description[0] != '\0') {
1041      if (!((strlen(p_device_info->service_description) + 1 <=
1042             SDP_MAX_ATTR_LEN) &&
1043            SDP_AddAttribute(
1044                handle, ATTR_ID_SERVICE_DESCRIPTION, TEXT_STR_DESC_TYPE,
1045                (uint32_t)(strlen(p_device_info->service_description) + 1),
1046                (uint8_t*)p_device_info->service_description)))
1047        result = SDP_DI_REG_FAILED;
1048    }
1049  }
1050
1051  /* optional - if string is null, do not add attribute */
1052  if (result == SDP_SUCCESS) {
1053    if (p_device_info->documentation_url[0] != '\0') {
1054      if (!((strlen(p_device_info->documentation_url) + 1 <=
1055             SDP_MAX_ATTR_LEN) &&
1056            SDP_AddAttribute(
1057                handle, ATTR_ID_DOCUMENTATION_URL, URL_DESC_TYPE,
1058                (uint32_t)(strlen(p_device_info->documentation_url) + 1),
1059                (uint8_t*)p_device_info->documentation_url)))
1060        result = SDP_DI_REG_FAILED;
1061    }
1062  }
1063
1064  /* mandatory */
1065  if (result == SDP_SUCCESS) {
1066    p_temp = temp_u16;
1067    UINT16_TO_BE_STREAM(p_temp, p_device_info->vendor);
1068    if (!(SDP_AddAttribute(handle, ATTR_ID_VENDOR_ID, UINT_DESC_TYPE,
1069                           sizeof(p_device_info->vendor), temp_u16)))
1070      result = SDP_DI_REG_FAILED;
1071  }
1072
1073  /* mandatory */
1074  if (result == SDP_SUCCESS) {
1075    p_temp = temp_u16;
1076    UINT16_TO_BE_STREAM(p_temp, p_device_info->product);
1077    if (!(SDP_AddAttribute(handle, ATTR_ID_PRODUCT_ID, UINT_DESC_TYPE,
1078                           sizeof(p_device_info->product), temp_u16)))
1079      result = SDP_DI_REG_FAILED;
1080  }
1081
1082  /* mandatory */
1083  if (result == SDP_SUCCESS) {
1084    p_temp = temp_u16;
1085    UINT16_TO_BE_STREAM(p_temp, p_device_info->version);
1086    if (!(SDP_AddAttribute(handle, ATTR_ID_PRODUCT_VERSION, UINT_DESC_TYPE,
1087                           sizeof(p_device_info->version), temp_u16)))
1088      result = SDP_DI_REG_FAILED;
1089  }
1090
1091  /* mandatory */
1092  if (result == SDP_SUCCESS) {
1093    u8 = (uint8_t)p_device_info->primary_record;
1094    if (!(SDP_AddAttribute(handle, ATTR_ID_PRIMARY_RECORD, BOOLEAN_DESC_TYPE, 1,
1095                           &u8)))
1096      result = SDP_DI_REG_FAILED;
1097  }
1098
1099  /* mandatory */
1100  if (result == SDP_SUCCESS) {
1101    p_temp = temp_u16;
1102    UINT16_TO_BE_STREAM(p_temp, p_device_info->vendor_id_source);
1103    if (!(SDP_AddAttribute(handle, ATTR_ID_VENDOR_ID_SOURCE, UINT_DESC_TYPE,
1104                           sizeof(p_device_info->vendor_id_source), temp_u16)))
1105      result = SDP_DI_REG_FAILED;
1106  }
1107
1108  if (result != SDP_SUCCESS)
1109    SDP_DeleteRecord(handle);
1110  else if (p_device_info->primary_record)
1111    sdp_cb.server_db.di_primary_handle = handle;
1112
1113  return result;
1114#else  /* SDP_SERVER_ENABLED is FALSE */
1115  return SDP_DI_REG_FAILED;
1116#endif /* if SDP_SERVER_ENABLED */
1117}
1118
1119/*******************************************************************************
1120 *
1121 * Function         SDP_SetTraceLevel
1122 *
1123 * Description      This function sets the trace level for SDP. If called with
1124 *                  a value of 0xFF, it simply reads the current trace level.
1125 *
1126 * Returns          the new (current) trace level
1127 *
1128 ******************************************************************************/
1129uint8_t SDP_SetTraceLevel(uint8_t new_level) {
1130  if (new_level != 0xFF) sdp_cb.trace_level = new_level;
1131
1132  return (sdp_cb.trace_level);
1133}
1134