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