Home | History | Annotate | Download | only in sdp
      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 *******************************************************************************/
    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 *******************************************************************************/
    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] = {0};
    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 *******************************************************************************/
    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 >= SDP_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 *******************************************************************************/
    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 >= SDP_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