• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2  *
3  *  Copyright (C) 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 
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdio.h>
29 
30 #include "gki.h"
31 #include "bt_types.h"
32 #include "btu.h"
33 
34 #include "l2cdefs.h"
35 #include "hcidefs.h"
36 #include "hcimsgs.h"
37 
38 #include "sdp_api.h"
39 #include "sdpint.h"
40 
41 
42 #if SDP_SERVER_ENABLED == TRUE
43 
44 /* Maximum number of bytes to reserve out of SDP MTU for response data */
45 #define SDP_MAX_SERVICE_RSPHDR_LEN      12
46 #define SDP_MAX_SERVATTR_RSPHDR_LEN     10
47 #define SDP_MAX_ATTR_RSPHDR_LEN         10
48 
49 /********************************************************************************/
50 /*              L O C A L    F U N C T I O N     P R O T O T Y P E S            */
51 /********************************************************************************/
52 static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
53                                     UINT16 param_len, UINT8 *p_req,
54                                     UINT8 *p_req_end);
55 
56 static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
57                                       UINT16 param_len, UINT8 *p_req,
58                                       UINT8 *p_req_end);
59 
60 static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
61                                              UINT16 param_len, UINT8 *p_req,
62                                              UINT8 *p_req_end);
63 
64 
65 /********************************************************************************/
66 /*                  E R R O R   T E X T   S T R I N G S                         */
67 /*                                                                              */
68 /* The default is to have no text string, but we allow the strings to be        */
69 /* configured in target.h if people want them.                                  */
70 /********************************************************************************/
71 #ifndef SDP_TEXT_BAD_HEADER
72 #define SDP_TEXT_BAD_HEADER     NULL
73 #endif
74 
75 #ifndef SDP_TEXT_BAD_PDU
76 #define SDP_TEXT_BAD_PDU        NULL
77 #endif
78 
79 #ifndef SDP_TEXT_BAD_UUID_LIST
80 #define SDP_TEXT_BAD_UUID_LIST  NULL
81 #endif
82 
83 #ifndef SDP_TEXT_BAD_HANDLE
84 #define SDP_TEXT_BAD_HANDLE     NULL
85 #endif
86 
87 #ifndef SDP_TEXT_BAD_ATTR_LIST
88 #define SDP_TEXT_BAD_ATTR_LIST  NULL
89 #endif
90 
91 #ifndef SDP_TEXT_BAD_CONT_LEN
92 #define SDP_TEXT_BAD_CONT_LEN   NULL
93 #endif
94 
95 #ifndef SDP_TEXT_BAD_CONT_INX
96 #define SDP_TEXT_BAD_CONT_INX   NULL
97 #endif
98 
99 /*******************************************************************************
100 **
101 ** Function         sdp_server_handle_client_req
102 **
103 ** Description      This is the main dispatcher of the SDP server. It is called
104 **                  when any data is received from L2CAP, and dispatches the
105 **                  request to the appropriate handler.
106 **
107 ** Returns          void
108 **
109 *******************************************************************************/
sdp_server_handle_client_req(tCONN_CB * p_ccb,BT_HDR * p_msg)110 void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg)
111 {
112     UINT8   *p_req     = (UINT8 *) (p_msg + 1) + p_msg->offset;
113     UINT8   *p_req_end = p_req + p_msg->len;
114     UINT8   pdu_id;
115     UINT16  trans_num, param_len;
116 
117 
118     /* Start inactivity timer */
119     btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
120 
121     /* The first byte in the message is the pdu type */
122     pdu_id = *p_req++;
123 
124     /* Extract the transaction number and parameter length */
125     BE_STREAM_TO_UINT16 (trans_num, p_req);
126     BE_STREAM_TO_UINT16 (param_len, p_req);
127 
128     if ((p_req + param_len) > p_req_end)
129     {
130         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_PDU_SIZE, SDP_TEXT_BAD_HEADER);
131         return;
132     }
133 
134     switch (pdu_id)
135     {
136     case SDP_PDU_SERVICE_SEARCH_REQ:
137         process_service_search (p_ccb, trans_num, param_len, p_req, p_req_end);
138         break;
139 
140     case SDP_PDU_SERVICE_ATTR_REQ:
141         process_service_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end);
142         break;
143 
144     case SDP_PDU_SERVICE_SEARCH_ATTR_REQ:
145         process_service_search_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end);
146         break;
147 
148     default:
149         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_PDU);
150         SDP_TRACE_WARNING1 ("SDP - server got unknown PDU: 0x%x", pdu_id);
151         break;
152     }
153 }
154 
155 
156 
157 /*******************************************************************************
158 **
159 ** Function         process_service_search
160 **
161 ** Description      This function handles a service search request from the
162 **                  client. It builds a reply message with info from the database,
163 **                  and sends the reply back to the client.
164 **
165 ** Returns          void
166 **
167 *******************************************************************************/
process_service_search(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)168 static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
169                                     UINT16 param_len, UINT8 *p_req,
170                                     UINT8 *p_req_end)
171 {
172     UINT16          max_replies, cur_handles, rem_handles, cont_offset;
173     tSDP_UUID_SEQ   uid_seq;
174     UINT8           *p_rsp, *p_rsp_start, *p_rsp_param_len;
175     UINT16          rsp_param_len, num_rsp_handles, xx;
176     UINT32          rsp_handles[SDP_MAX_RECORDS];
177     tSDP_RECORD    *p_rec = NULL;
178     BT_HDR         *p_buf;
179     BOOLEAN         is_cont = FALSE;
180 
181     p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq);
182 
183     if ((!p_req) || (!uid_seq.num_uids))
184     {
185         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST);
186         return;
187     }
188 
189     /* Get the max replies we can send. Cap it at our max anyways. */
190     BE_STREAM_TO_UINT16 (max_replies, p_req);
191 
192     if (max_replies > SDP_MAX_RECORDS)
193         max_replies = SDP_MAX_RECORDS;
194 
195     /* Get a list of handles that match the UUIDs given to us */
196     for (num_rsp_handles = 0; num_rsp_handles < max_replies; )
197     {
198         p_rec = sdp_db_service_search (p_rec, &uid_seq);
199 
200         if (p_rec)
201             rsp_handles[num_rsp_handles++] = p_rec->record_handle;
202         else
203             break;
204     }
205 
206     /* Check if this is a continuation request */
207     if (*p_req)
208     {
209         if (*p_req++ != SDP_CONTINUATION_LEN)
210         {
211             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE,
212                                      SDP_TEXT_BAD_CONT_LEN);
213             return;
214         }
215         BE_STREAM_TO_UINT16 (cont_offset, p_req);
216 
217         if (cont_offset != p_ccb->cont_offset)
218         {
219             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE,
220                                      SDP_TEXT_BAD_CONT_INX);
221             return;
222         }
223 
224         rem_handles = num_rsp_handles - cont_offset;    /* extract the remaining handles */
225     }
226     else
227     {
228         rem_handles = num_rsp_handles;
229         cont_offset = 0;
230     }
231 
232     /* Calculate how many handles will fit in one PDU */
233     cur_handles = (UINT16)((p_ccb->rem_mtu_size - SDP_MAX_SERVICE_RSPHDR_LEN) / 4);
234 
235     if (rem_handles <= cur_handles)
236         cur_handles = rem_handles;
237     else /* Continuation is set */
238     {
239         p_ccb->cont_offset += cur_handles;
240         is_cont = TRUE;
241     }
242 
243     /* Get a buffer to use to build the response */
244     if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL)
245     {
246         SDP_TRACE_ERROR0 ("SDP - no buf for search rsp");
247         return;
248     }
249     p_buf->offset = L2CAP_MIN_OFFSET;
250     p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
251 
252     /* Start building a rsponse */
253     UINT8_TO_BE_STREAM  (p_rsp, SDP_PDU_SERVICE_SEARCH_RSP);
254     UINT16_TO_BE_STREAM (p_rsp, trans_num);
255 
256     /* Skip the length, we need to add it at the end */
257     p_rsp_param_len = p_rsp;
258     p_rsp += 2;
259 
260     /* Put in total and current number of handles, and handles themselves */
261     UINT16_TO_BE_STREAM (p_rsp, num_rsp_handles);
262     UINT16_TO_BE_STREAM (p_rsp, cur_handles);
263 
264 /*    SDP_TRACE_DEBUG5("SDP Service Rsp: tothdl %d, curhdlr %d, start %d, end %d, cont %d",
265                      num_rsp_handles, cur_handles, cont_offset,
266                      cont_offset + cur_handles-1, is_cont); */
267     for (xx = cont_offset; xx < cont_offset + cur_handles; xx++)
268         UINT32_TO_BE_STREAM (p_rsp, rsp_handles[xx]);
269 
270     if (is_cont)
271     {
272         UINT8_TO_BE_STREAM  (p_rsp, SDP_CONTINUATION_LEN);
273         UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
274     }
275     else
276         UINT8_TO_BE_STREAM (p_rsp, 0);
277 
278     /* Go back and put the parameter length into the buffer */
279     rsp_param_len = p_rsp - p_rsp_param_len - 2;
280     UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
281 
282     /* Set the length of the SDP data in the buffer */
283     p_buf->len = p_rsp - p_rsp_start;
284 
285 
286     /* Send the buffer through L2CAP */
287     L2CA_DataWrite (p_ccb->connection_id, p_buf);
288 }
289 
290 
291 /*******************************************************************************
292 **
293 ** Function         process_service_attr_req
294 **
295 ** Description      This function handles an attribute request from the client.
296 **                  It builds a reply message with info from the database,
297 **                  and sends the reply back to the client.
298 **
299 ** Returns          void
300 **
301 *******************************************************************************/
process_service_attr_req(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)302 static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
303                                       UINT16 param_len, UINT8 *p_req,
304                                       UINT8 *p_req_end)
305 {
306     UINT16          max_list_len, len_to_send, cont_offset;
307     INT16           rem_len;
308     tSDP_ATTR_SEQ   attr_seq, attr_seq_sav;
309     UINT8           *p_rsp, *p_rsp_start, *p_rsp_param_len;
310     UINT16          rsp_param_len, xx;
311     UINT32          rec_handle;
312     tSDP_RECORD     *p_rec;
313     tSDP_ATTRIBUTE  *p_attr;
314     BT_HDR          *p_buf;
315     BOOLEAN         is_cont = FALSE;
316     UINT16          attr_len;
317 
318     /* Extract the record handle */
319     BE_STREAM_TO_UINT32 (rec_handle, p_req);
320 
321     if (p_req > p_req_end)
322     {
323         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE);
324         return;
325     }
326 
327     /* Get the max list length we can send. Cap it at MTU size minus overhead */
328     BE_STREAM_TO_UINT16 (max_list_len, p_req);
329 
330     if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN))
331         max_list_len = p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN;
332 
333     p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq);
334 
335     if ((!p_req) || (!attr_seq.num_attr) || (p_req > p_req_end))
336     {
337         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST);
338         return;
339     }
340 
341     memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ;
342 
343     /* Find a record with the record handle */
344     p_rec = sdp_db_find_record (rec_handle);
345     if (!p_rec)
346     {
347         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE);
348         return;
349     }
350 
351     /* Check if this is a continuation request */
352     if (*p_req)
353     {
354         if (*p_req++ != SDP_CONTINUATION_LEN)
355         {
356             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN);
357             return;
358         }
359         BE_STREAM_TO_UINT16 (cont_offset, p_req);
360 
361         if (cont_offset != p_ccb->cont_offset)
362         {
363             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX);
364             return;
365         }
366 
367         if (!p_ccb->rsp_list)
368         {
369             sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
370             return;
371         }
372         is_cont = TRUE;
373 
374         /* Initialise for continuation response */
375         p_rsp = &p_ccb->rsp_list[0];
376         attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id;
377     }
378     else
379     {
380         /* Get a scratch buffer to store response */
381         if (!p_ccb->rsp_list)
382         {
383             p_ccb->rsp_list = (UINT8 *)GKI_getbuf (max_list_len);
384             if (p_ccb->rsp_list == NULL)
385             {
386                 SDP_TRACE_ERROR0 ("SDP - no scratch buf for search rsp");
387                 return;
388             }
389         }
390 
391         p_ccb->cont_offset = 0;
392         p_rsp = &p_ccb->rsp_list[3];            /* Leave space for data elem descr */
393 
394         /* Reset continuation parameters in p_ccb */
395         p_ccb->cont_info.prev_sdp_rec = NULL;
396         p_ccb->cont_info.next_attr_index = 0;
397         p_ccb->cont_info.attr_offset = 0;
398     }
399 
400     /* Search for attributes that match the list given to us */
401     for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++)
402     {
403         p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end);
404 
405         if (p_attr)
406         {
407             /* Check if attribute fits. Assume 3-byte value type/length */
408             rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
409 
410             /* just in case */
411             if (rem_len <= 0)
412             {
413                 p_ccb->cont_info.next_attr_index = xx;
414                 p_ccb->cont_info.next_attr_start_id = p_attr->id;
415                 break;
416             }
417 
418             attr_len = sdpu_get_attrib_entry_len(p_attr);
419             /* if there is a partial attribute pending to be sent */
420             if (p_ccb->cont_info.attr_offset)
421             {
422                 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len,
423                                                          &p_ccb->cont_info.attr_offset);
424 
425                 /* If the partial attrib could not been fully added yet */
426                 if (p_ccb->cont_info.attr_offset != attr_len)
427                     break;
428                 else /* If the partial attrib has been added in full by now */
429                     p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */
430             }
431             else if (rem_len < attr_len) /* Not enough space for attr... so add partially */
432             {
433                 if (attr_len >= MAX_ATTR_LEN)
434                 {
435                     SDP_TRACE_ERROR2("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len);
436                     sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
437                     return;
438                 }
439 
440                 /* add the partial attribute if possible */
441                 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len,
442                                                          &p_ccb->cont_info.attr_offset);
443 
444                 p_ccb->cont_info.next_attr_index = xx;
445                 p_ccb->cont_info.next_attr_start_id = p_attr->id;
446                 break;
447             }
448             else /* build the whole attribute */
449                 p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
450 
451             /* If doing a range, stick with this one till no more attributes found */
452             if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end)
453             {
454                 /* Update for next time through */
455                 attr_seq.attr_entry[xx].start = p_attr->id + 1;
456 
457                 xx--;
458             }
459         }
460     }
461     /* If all the attributes have been accomodated in p_rsp,
462        reset next_attr_index */
463     if (xx == attr_seq.num_attr)
464         p_ccb->cont_info.next_attr_index = 0;
465 
466     len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]);
467     cont_offset = 0;
468 
469     if (!is_cont)
470     {
471         p_ccb->list_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav) + 3;
472         /* Put in the sequence header (2 or 3 bytes) */
473         if (p_ccb->list_len > 255)
474         {
475             p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
476             p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8);
477             p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
478         }
479         else
480         {
481             cont_offset = 1;
482 
483             p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
484             p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
485 
486             p_ccb->list_len--;
487             len_to_send--;
488         }
489     }
490 
491     /* Get a buffer to use to build the response */
492     if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL)
493     {
494         SDP_TRACE_ERROR0 ("SDP - no buf for search rsp");
495         return;
496     }
497     p_buf->offset = L2CAP_MIN_OFFSET;
498     p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
499 
500     /* Start building a rsponse */
501     UINT8_TO_BE_STREAM  (p_rsp, SDP_PDU_SERVICE_ATTR_RSP);
502     UINT16_TO_BE_STREAM (p_rsp, trans_num);
503 
504     /* Skip the parameter length, add it when we know the length */
505     p_rsp_param_len = p_rsp;
506     p_rsp += 2;
507 
508     UINT16_TO_BE_STREAM (p_rsp, len_to_send);
509 
510     memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
511     p_rsp += len_to_send;
512 
513     p_ccb->cont_offset += len_to_send;
514 
515     /* If anything left to send, continuation needed */
516     if (p_ccb->cont_offset < p_ccb->list_len)
517     {
518         is_cont = TRUE;
519 
520         UINT8_TO_BE_STREAM  (p_rsp, SDP_CONTINUATION_LEN);
521         UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
522     }
523     else
524         UINT8_TO_BE_STREAM (p_rsp, 0);
525 
526     /* Go back and put the parameter length into the buffer */
527     rsp_param_len = p_rsp - p_rsp_param_len - 2;
528     UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
529 
530     /* Set the length of the SDP data in the buffer */
531     p_buf->len = p_rsp - p_rsp_start;
532 
533 
534     /* Send the buffer through L2CAP */
535     L2CA_DataWrite (p_ccb->connection_id, p_buf);
536 }
537 
538 
539 
540 /*******************************************************************************
541 **
542 ** Function         process_service_search_attr_req
543 **
544 ** Description      This function handles a combined service search and attribute
545 **                  read request from the client. It builds a reply message with
546 **                  info from the database, and sends the reply back to the client.
547 **
548 ** Returns          void
549 **
550 *******************************************************************************/
process_service_search_attr_req(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)551 static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
552                                              UINT16 param_len, UINT8 *p_req,
553                                              UINT8 *p_req_end)
554 {
555     UINT16         max_list_len;
556     INT16          rem_len;
557     UINT16         len_to_send, cont_offset;
558     tSDP_UUID_SEQ   uid_seq;
559     UINT8           *p_rsp, *p_rsp_start, *p_rsp_param_len;
560     UINT16          rsp_param_len, xx;
561     tSDP_RECORD    *p_rec;
562     tSDP_ATTR_SEQ   attr_seq, attr_seq_sav;
563     tSDP_ATTRIBUTE *p_attr;
564     BT_HDR         *p_buf;
565     BOOLEAN         maxxed_out = FALSE, is_cont = FALSE;
566     UINT8           *p_seq_start;
567     UINT16          seq_len, attr_len;
568 
569     /* Extract the UUID sequence to search for */
570     p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq);
571 
572     if ((!p_req) || (!uid_seq.num_uids))
573     {
574         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST);
575         return;
576     }
577 
578     /* Get the max list length we can send. Cap it at our max list length. */
579     BE_STREAM_TO_UINT16 (max_list_len, p_req);
580 
581     if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN))
582         max_list_len = p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN;
583 
584     p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq);
585 
586     if ((!p_req) || (!attr_seq.num_attr))
587     {
588         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST);
589         return;
590     }
591 
592     memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ;
593 
594     /* Check if this is a continuation request */
595     if (*p_req)
596     {
597         if (*p_req++ != SDP_CONTINUATION_LEN)
598         {
599             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN);
600             return;
601         }
602         BE_STREAM_TO_UINT16 (cont_offset, p_req);
603 
604         if (cont_offset != p_ccb->cont_offset)
605         {
606             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX);
607             return;
608         }
609 
610         if (!p_ccb->rsp_list)
611         {
612             sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
613             return;
614         }
615         is_cont = TRUE;
616 
617         /* Initialise for continuation response */
618         p_rsp = &p_ccb->rsp_list[0];
619         attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id;
620     }
621     else
622     {
623         /* Get a scratch buffer to store response */
624         if (!p_ccb->rsp_list)
625         {
626             p_ccb->rsp_list = (UINT8 *)GKI_getbuf (max_list_len);
627             if (p_ccb->rsp_list == NULL)
628             {
629                 SDP_TRACE_ERROR0 ("SDP - no scratch buf for search rsp");
630                 return;
631             }
632         }
633 
634         p_ccb->cont_offset = 0;
635         p_rsp = &p_ccb->rsp_list[3];            /* Leave space for data elem descr */
636 
637         /* Reset continuation parameters in p_ccb */
638         p_ccb->cont_info.prev_sdp_rec = NULL;
639         p_ccb->cont_info.next_attr_index = 0;
640         p_ccb->cont_info.last_attr_seq_desc_sent = FALSE;
641         p_ccb->cont_info.attr_offset = 0;
642     }
643 
644     /* Get a list of handles that match the UUIDs given to us */
645     for (p_rec = sdp_db_service_search (p_ccb->cont_info.prev_sdp_rec, &uid_seq); p_rec; p_rec = sdp_db_service_search (p_rec, &uid_seq))
646     {
647         /* Allow space for attribute sequence type and length */
648         p_seq_start = p_rsp;
649         if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE)
650         {
651             /* See if there is enough room to include a new service in the current response */
652             rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
653             if (rem_len < 3)
654             {
655                 /* Not enough room. Update continuation info for next response */
656                 p_ccb->cont_info.next_attr_index = 0;
657                 p_ccb->cont_info.next_attr_start_id = attr_seq.attr_entry[0].start;
658                 break;
659             }
660             p_rsp += 3;
661         }
662 
663         /* Get a list of handles that match the UUIDs given to us */
664         for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++)
665         {
666             p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end);
667 
668             if (p_attr)
669             {
670                 /* Check if attribute fits. Assume 3-byte value type/length */
671                 rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
672 
673                 /* just in case */
674                 if (rem_len <= 0)
675                 {
676                     p_ccb->cont_info.next_attr_index = xx;
677                     p_ccb->cont_info.next_attr_start_id = p_attr->id;
678                     maxxed_out = TRUE;
679                     break;
680                 }
681 
682                 attr_len = sdpu_get_attrib_entry_len(p_attr);
683                 /* if there is a partial attribute pending to be sent */
684                 if (p_ccb->cont_info.attr_offset)
685                 {
686                     p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len,
687                                                              &p_ccb->cont_info.attr_offset);
688 
689                     /* If the partial attrib could not been fully added yet */
690                     if (p_ccb->cont_info.attr_offset != attr_len)
691                     {
692                         maxxed_out = TRUE;
693                         break;
694                     }
695                     else /* If the partial attrib has been added in full by now */
696                         p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */
697                 }
698                 else if (rem_len < attr_len) /* Not enough space for attr... so add partially */
699                 {
700                     if (attr_len >= MAX_ATTR_LEN)
701                     {
702                         SDP_TRACE_ERROR2("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len);
703                         sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
704                         return;
705                     }
706 
707                     /* add the partial attribute if possible */
708                     p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len,
709                                                              &p_ccb->cont_info.attr_offset);
710 
711                     p_ccb->cont_info.next_attr_index = xx;
712                     p_ccb->cont_info.next_attr_start_id = p_attr->id;
713                     maxxed_out = TRUE;
714                     break;
715                 }
716                 else /* build the whole attribute */
717                     p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
718 
719                 /* If doing a range, stick with this one till no more attributes found */
720                 if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end)
721                 {
722                     /* Update for next time through */
723                     attr_seq.attr_entry[xx].start = p_attr->id + 1;
724 
725                     xx--;
726                 }
727             }
728         }
729 
730         /* Go back and put the type and length into the buffer */
731         if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE)
732         {
733             seq_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav);
734             if (seq_len != 0)
735             {
736                 UINT8_TO_BE_STREAM  (p_seq_start, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
737                 UINT16_TO_BE_STREAM (p_seq_start, seq_len);
738 
739                 if (maxxed_out)
740                     p_ccb->cont_info.last_attr_seq_desc_sent = TRUE;
741             }
742             else
743                 p_rsp = p_seq_start;
744         }
745 
746         if (maxxed_out)
747             break;
748 
749         /* Restore the attr_seq to look for in the next sdp record */
750         memcpy(&attr_seq, &attr_seq_sav, sizeof(tSDP_ATTR_SEQ)) ;
751 
752         /* Reset the next attr index */
753         p_ccb->cont_info.next_attr_index = 0;
754         p_ccb->cont_info.prev_sdp_rec = p_rec;
755         p_ccb->cont_info.last_attr_seq_desc_sent = FALSE;
756     }
757 
758     /* response length */
759     len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]);
760     cont_offset = 0;
761 
762     /* If first response, insert sequence header */
763     if (!is_cont)
764     {
765         /* Get the total list length for requested uid and attribute sequence */
766         p_ccb->list_len = sdpu_get_list_len(&uid_seq, &attr_seq_sav) + 3;
767         /* Put in the sequence header (2 or 3 bytes) */
768         if (p_ccb->list_len > 255)
769         {
770             p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
771             p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8);
772             p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
773         }
774         else
775         {
776             cont_offset = 1;
777 
778             p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
779             p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
780 
781             p_ccb->list_len--;
782             len_to_send--;
783         }
784     }
785 
786     /* Get a buffer to use to build the response */
787     if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL)
788     {
789         SDP_TRACE_ERROR0 ("SDP - no buf for search rsp");
790         return;
791     }
792     p_buf->offset = L2CAP_MIN_OFFSET;
793     p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
794 
795     /* Start building a rsponse */
796     UINT8_TO_BE_STREAM  (p_rsp, SDP_PDU_SERVICE_SEARCH_ATTR_RSP);
797     UINT16_TO_BE_STREAM (p_rsp, trans_num);
798 
799     /* Skip the parameter length, add it when we know the length */
800     p_rsp_param_len = p_rsp;
801     p_rsp += 2;
802 
803     /* Stream the list length to send */
804     UINT16_TO_BE_STREAM (p_rsp, len_to_send);
805 
806     /* copy from rsp_list to the actual buffer to be sent */
807     memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
808     p_rsp += len_to_send;
809 
810     p_ccb->cont_offset += len_to_send;
811 
812     /* If anything left to send, continuation needed */
813     if (p_ccb->cont_offset < p_ccb->list_len)
814     {
815         is_cont = TRUE;
816 
817         UINT8_TO_BE_STREAM  (p_rsp, SDP_CONTINUATION_LEN);
818         UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
819     }
820     else
821         UINT8_TO_BE_STREAM (p_rsp, 0);
822 
823     /* Go back and put the parameter length into the buffer */
824     rsp_param_len = p_rsp - p_rsp_param_len - 2;
825     UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
826 
827     /* Set the length of the SDP data in the buffer */
828     p_buf->len = p_rsp - p_rsp_start;
829 
830 
831     /* Send the buffer through L2CAP */
832     L2CA_DataWrite (p_ccb->connection_id, p_buf);
833 }
834 
835 #endif  /* SDP_SERVER_ENABLED == TRUE */
836