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 "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 /********************************************************************************/
     53 static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
     54                                     UINT16 param_len, UINT8 *p_req,
     55                                     UINT8 *p_req_end);
     56 
     57 static 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 
     61 static 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 *******************************************************************************/
    115 void 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 *******************************************************************************/
    173 static 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 *******************************************************************************/
    316 static 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 *******************************************************************************/
    571 static 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