xref: /btstack/src/classic/pbap_client.c (revision 838023a844e627d5ed09b8d2d8cc71c360435bab)
1 /*
2  * Copyright (C) 2014 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "pbap_client.c"
39 
40 #include "btstack_config.h"
41 
42 #include <stdint.h>
43 #include <string.h>
44 
45 #include "hci_cmd.h"
46 #include "btstack_run_loop.h"
47 #include "btstack_debug.h"
48 #include "hci.h"
49 #include "btstack_memory.h"
50 #include "hci_dump.h"
51 #include "l2cap.h"
52 #include "bluetooth_sdp.h"
53 #include "classic/sdp_client_rfcomm.h"
54 #include "btstack_event.h"
55 #include "md5.h"
56 #include "yxml.h"
57 
58 #include "classic/obex.h"
59 #include "classic/obex_iterator.h"
60 #include "classic/goep_client.h"
61 #include "classic/pbap_client.h"
62 
63 // 796135f0-f0c5-11d8-0966- 0800200c9a66
64 static const uint8_t pbap_uuid[] = { 0x79, 0x61, 0x35, 0xf0, 0xf0, 0xc5, 0x11, 0xd8, 0x09, 0x66, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66};
65 
66 const char * pbap_phonebook_type     = "x-bt/phonebook";
67 const char * pbap_vcard_listing_type = "x-bt/vcard-listing";
68 const char * pbap_vcard_entry_type   = "x-bt/vcard";
69 
70 const char * pbap_vcard_listing_name = "pb";
71 
72 static uint32_t pbap_supported_features = \
73     PBAP_SUPPORTED_FEATURES_DOWNLOAD |
74     PBAP_SUPPORTED_FEATURES_BROWSING |
75     PBAP_SUPPORTED_FEATURES_DATABASE_IDENTIFIER |
76     PBAP_SUPPORTED_FEATURES_FOLDER_VERSION_COUNTERS |
77     PBAP_SUPPORTED_FEATURES_VCARD_SELECTING |
78     PBAP_SUPPORTED_FEATURES_ENHANCED_MISSED_CALLS |
79     PBAP_SUPPORTED_FEATURES_DEFAULT_CONTACT_IMAGE_FORMAT |
80     PBAP_SUPPORTED_FEATURES_X_BT_UCI_VCARD_PROPERTY |
81     PBAP_SUPPORTED_FEATURES_X_BT_UID_VCARD_PROPERTY |
82     PBAP_SUPPORTED_FEATURES_CONTACT_REFERENCING;
83 
84 typedef enum {
85     PBAP_INIT = 0,
86     PBAP_W4_GOEP_CONNECTION,
87     PBAP_W2_SEND_CONNECT_REQUEST,
88     PBAP_W4_CONNECT_RESPONSE,
89     PBAP_W4_USER_AUTHENTICATION,
90     PBAP_W2_SEND_AUTHENTICATED_CONNECT,
91     PBAP_CONNECT_RESPONSE_RECEIVED,
92     PBAP_CONNECTED,
93     //
94     PBAP_W2_SEND_DISCONNECT_REQUEST,
95     PBAP_W4_DISCONNECT_RESPONSE,
96     //
97     PBAP_W2_PULL_PHONEBOOK,
98     PBAP_W4_PHONEBOOK,
99     PBAP_W2_SET_PATH_ROOT,
100     PBAP_W4_SET_PATH_ROOT_COMPLETE,
101     PBAP_W2_SET_PATH_ELEMENT,
102     PBAP_W4_SET_PATH_ELEMENT_COMPLETE,
103     PBAP_W2_GET_PHONEBOOK_SIZE,
104     PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE,
105     // - pull vacard liast
106     PBAP_W2_GET_CARD_LIST,
107     PBAP_W4_GET_CARD_LIST_COMPLETE,
108     // - pull vcard entry
109     PBAP_W2_GET_CARD_ENTRY,
110     PBAP_W4_GET_CARD_ENTRY_COMPLETE
111 
112 } pbap_state_t;
113 
114 typedef enum {
115     SRM_DISABLED,
116     SRM_W4_CONFIRM,
117     SRM_ENABLED_BUT_WAITING,
118     SRM_ENABLED
119 } srm_state_t;
120 
121 typedef struct pbap_client {
122     pbap_state_t state;
123     uint16_t  cid;
124     bd_addr_t bd_addr;
125     hci_con_handle_t con_handle;
126     uint8_t   incoming;
127     uint16_t  goep_cid;
128     btstack_packet_handler_t client_handler;
129     int request_number;
130     srm_state_t srm_state;
131     const char * current_folder;
132     const char * phone_number;
133     const char * phonebook_path;
134     const char * vcard_name;
135     uint16_t set_path_offset;
136     /* vcard selector / operator */
137     uint32_t vcard_selector;
138     uint8_t  vcard_selector_operator;
139     uint8_t  vcard_selector_supported;
140     /* abort */
141     uint8_t  abort_operation;
142     /* authentication */
143     uint8_t  authentication_options;
144     uint16_t authentication_nonce[16];
145     const char * authentication_password;
146     /* xml parser */
147     yxml_t  xml_parser;
148     uint8_t xml_buffer[50];
149     /* vcard listing parser */
150     bool parser_card_found;
151     bool parser_name_found;
152     bool parser_handle_found;
153     char parser_name[PBAP_MAX_NAME_LEN];
154     char parser_handle[PBAP_MAX_HANDLE_LEN];
155     /* flow control mode */
156     uint8_t flow_control_enabled;
157     uint8_t flow_next_triggered;
158 } pbap_client_t;
159 
160 static pbap_client_t _pbap_client;
161 static pbap_client_t * pbap_client = &_pbap_client;
162 
163 static void pbap_client_emit_connected_event(pbap_client_t * context, uint8_t status){
164     uint8_t event[15];
165     int pos = 0;
166     event[pos++] = HCI_EVENT_PBAP_META;
167     pos++;  // skip len
168     event[pos++] = PBAP_SUBEVENT_CONNECTION_OPENED;
169     little_endian_store_16(event,pos,context->cid);
170     pos+=2;
171     event[pos++] = status;
172     (void)memcpy(&event[pos], context->bd_addr, 6);
173     pos += 6;
174     little_endian_store_16(event,pos,context->con_handle);
175     pos += 2;
176     event[pos++] = context->incoming;
177     event[1] = pos - 2;
178     if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos);
179     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
180 }
181 
182 static void pbap_client_emit_connection_closed_event(pbap_client_t * context){
183     uint8_t event[5];
184     int pos = 0;
185     event[pos++] = HCI_EVENT_PBAP_META;
186     pos++;  // skip len
187     event[pos++] = PBAP_SUBEVENT_CONNECTION_CLOSED;
188     little_endian_store_16(event,pos,context->cid);
189     pos+=2;
190     event[1] = pos - 2;
191     if (pos != sizeof(event)) log_error("pbap_client_emit_connection_closed_event size %u", pos);
192     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
193 }
194 
195 static void pbap_client_emit_operation_complete_event(pbap_client_t * context, uint8_t status){
196     uint8_t event[6];
197     int pos = 0;
198     event[pos++] = HCI_EVENT_PBAP_META;
199     pos++;  // skip len
200     event[pos++] = PBAP_SUBEVENT_OPERATION_COMPLETED;
201     little_endian_store_16(event,pos,context->cid);
202     pos+=2;
203     event[pos++]= status;
204     event[1] = pos - 2;
205     if (pos != sizeof(event)) log_error("pbap_client_emit_can_send_now_event size %u", pos);
206     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
207 }
208 
209 static void pbap_client_emit_phonebook_size_event(pbap_client_t * context, uint8_t status, uint16_t phonebook_size){
210     uint8_t event[8];
211     int pos = 0;
212     event[pos++] = HCI_EVENT_PBAP_META;
213     pos++;  // skip len
214     event[pos++] = PBAP_SUBEVENT_PHONEBOOK_SIZE;
215     little_endian_store_16(event,pos,context->cid);
216     pos+=2;
217     event[pos++] = status;
218     little_endian_store_16(event,pos, phonebook_size);
219     pos+=2;
220     event[1] = pos - 2;
221     if (pos != sizeof(event)) log_error("pbap_client_emit_phonebook_size_event size %u", pos);
222     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
223 }
224 
225 static void pbap_client_emit_authentication_event(pbap_client_t * context, uint8_t options){
226     // split options
227     uint8_t user_id_required = (options & 1) ? 1 : 0;
228     uint8_t full_access      = (options & 2) ? 1 : 0;
229 
230     uint8_t event[7];
231     int pos = 0;
232     event[pos++] = HCI_EVENT_PBAP_META;
233     pos++;  // skip len
234     event[pos++] = PBAP_SUBEVENT_AUTHENTICATION_REQUEST;
235     little_endian_store_16(event,pos,context->cid);
236     pos+=2;
237     event[pos++] = user_id_required;
238     event[pos++] = full_access;
239     if (pos != sizeof(event)) log_error("pbap_client_emit_authentication_event size %u", pos);
240     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
241 }
242 
243 static void pbap_client_emit_card_result_event(pbap_client_t * context, const char * name, const char * handle){
244     uint8_t event[5 + PBAP_MAX_NAME_LEN + PBAP_MAX_HANDLE_LEN];
245     int pos = 0;
246     event[pos++] = HCI_EVENT_PBAP_META;
247     pos++;  // skip len
248     event[pos++] = PBAP_SUBEVENT_CARD_RESULT;
249     little_endian_store_16(event,pos,context->cid);
250     pos+=2;
251     int name_len = btstack_min(PBAP_MAX_NAME_LEN, strlen(name));
252     event[pos++] = name_len;
253     (void)memcpy(&event[pos], name, name_len);
254     pos += name_len;
255     int handle_len = btstack_min(PBAP_MAX_HANDLE_LEN, strlen(handle));
256     event[pos++] = handle_len;
257     (void)memcpy(&event[pos], handle, handle_len);
258     pos += handle_len;
259     event[1] = pos - 2;
260     context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
261 }
262 
263 static const uint8_t collon = (uint8_t) ':';
264 
265 static void pbap_handle_can_send_now(void){
266     uint8_t  path_element[20];
267     uint16_t path_element_start;
268     uint16_t path_element_len;
269     uint8_t  application_parameters[PBAP_MAX_PHONE_NUMBER_LEN + 10];
270     uint8_t  challenge_response[36];
271     int i;
272     uint16_t phone_number_len;
273 
274     MD5_CTX md5_ctx;
275 
276     if (pbap_client->abort_operation){
277         pbap_client->abort_operation = 0;
278         pbap_client->state = PBAP_CONNECTED;
279         goep_client_request_create_abort(pbap_client->goep_cid);
280         goep_client_execute(pbap_client->goep_cid);
281         return;
282     }
283 
284     switch (pbap_client->state){
285         case PBAP_W2_SEND_CONNECT_REQUEST:
286             goep_client_request_create_connect(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT);
287             goep_client_header_add_target(pbap_client->goep_cid, pbap_uuid, 16);
288             // Mandatory if the PSE advertises a PbapSupportedFeatures attribute in its SDP record, else excluded.
289             if (goep_client_get_pbap_supported_features(pbap_client->goep_cid) != PBAP_FEATURES_NOT_PRESENT){
290                 application_parameters[0] = PBAP_APPLICATION_PARAMETER_PBAP_SUPPORTED_FEATURES;
291                 application_parameters[1] = 4;
292                 big_endian_store_32(application_parameters, 2, pbap_supported_features);
293                 goep_client_header_add_application_parameters(pbap_client->goep_cid, &application_parameters[0], 6);
294             }
295             pbap_client->state = PBAP_W4_CONNECT_RESPONSE;
296             goep_client_execute(pbap_client->goep_cid);
297             break;
298         case PBAP_W2_SEND_AUTHENTICATED_CONNECT:
299             goep_client_request_create_connect(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT);
300             goep_client_header_add_target(pbap_client->goep_cid, pbap_uuid, 16);
301             // setup authentication challenge response
302             i = 0;
303             challenge_response[i++] = 0;  // Tag Digest
304             challenge_response[i++] = 16; // Len
305             // calculate md5
306             MD5_Init(&md5_ctx);
307             MD5_Update(&md5_ctx, pbap_client->authentication_nonce, 16);
308             MD5_Update(&md5_ctx, &collon, 1);
309             MD5_Update(&md5_ctx, pbap_client->authentication_password, strlen(pbap_client->authentication_password));
310             MD5_Final(&challenge_response[i], &md5_ctx);
311             i += 16;
312             challenge_response[i++] = 2;  // Tag Nonce
313             challenge_response[i++] = 16; // Len
314             (void)memcpy(&challenge_response[i],
315                          pbap_client->authentication_nonce, 16);
316             i += 16;
317             goep_client_header_add_challenge_response(pbap_client->goep_cid, challenge_response, i);
318             pbap_client->state = PBAP_W4_CONNECT_RESPONSE;
319             goep_client_execute(pbap_client->goep_cid);
320             break;
321         case PBAP_W2_SEND_DISCONNECT_REQUEST:
322             goep_client_request_create_disconnect(pbap_client->goep_cid);
323             pbap_client->state = PBAP_W4_DISCONNECT_RESPONSE;
324             goep_client_execute(pbap_client->goep_cid);
325             return;
326         case PBAP_W2_PULL_PHONEBOOK:
327         case PBAP_W2_GET_PHONEBOOK_SIZE:
328             goep_client_request_create_get(pbap_client->goep_cid);
329             if (pbap_client->request_number == 0){
330                 if (!pbap_client->flow_control_enabled){
331                     goep_client_header_add_srm_enable(pbap_client->goep_cid);
332                     pbap_client->srm_state = SRM_W4_CONFIRM;
333                 }
334                 goep_client_header_add_name(pbap_client->goep_cid, pbap_client->phonebook_path);
335                 goep_client_header_add_type(pbap_client->goep_cid, pbap_phonebook_type);
336                 i = 0;
337                 if (pbap_client->vcard_selector_supported){
338                     // vCard Selector
339                     if (pbap_client->vcard_selector){
340                         application_parameters[i++] = PBAP_APPLICATION_PARAMETER_VCARD_SELECTOR;
341                         application_parameters[i++] = 8;
342                         memset(&application_parameters[i], 0, 4);
343                         i += 4;
344                         big_endian_store_32(application_parameters, i, pbap_client->vcard_selector);
345                         i += 4;
346                     }
347                     // vCard Selector Operator
348                     if (pbap_client->vcard_selector_operator != PBAP_VCARD_SELECTOR_OPERATOR_OR){
349                         application_parameters[i++] = PBAP_APPLICATION_PARAMETER_VCARD_SELECTOR_OPERATOR;
350                         application_parameters[i++] = 1;
351                         application_parameters[i++] = pbap_client->vcard_selector_operator;
352                     }
353                 }
354                 if (pbap_client->state == PBAP_W2_GET_PHONEBOOK_SIZE){
355                     // Regular TLV wih 1-byte len
356                     application_parameters[i++] = PBAP_APPLICATION_PARAMETER_MAX_LIST_COUNT;
357                     application_parameters[i++] = 2;
358                     big_endian_store_16(application_parameters, 2, 0);
359                     i += 2;
360                 }
361                 if (i){
362                     goep_client_header_add_application_parameters(pbap_client->goep_cid, application_parameters, i);
363                 }
364             }
365             if (pbap_client->state == PBAP_W2_GET_PHONEBOOK_SIZE){
366                 // state
367                 pbap_client->state = PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE;
368             } else {
369                 // state
370                 pbap_client->state = PBAP_W4_PHONEBOOK;
371             }
372             // send packet
373             pbap_client->request_number++;
374             goep_client_execute(pbap_client->goep_cid);
375             break;
376         case PBAP_W2_GET_CARD_LIST:
377             goep_client_request_create_get(pbap_client->goep_cid);
378             if (pbap_client->request_number == 0){
379                 if (!pbap_client->flow_control_enabled){
380                     goep_client_header_add_srm_enable(pbap_client->goep_cid);
381                     pbap_client->srm_state = SRM_W4_CONFIRM;
382                 }
383                 goep_client_header_add_name(pbap_client->goep_cid, pbap_client->phonebook_path);
384                 goep_client_header_add_type(pbap_client->goep_cid, pbap_vcard_listing_type);
385                 i = 0;
386                 if (pbap_client->vcard_selector_supported){
387                     // vCard Selector
388                     if (pbap_client->vcard_selector){
389                         application_parameters[i++] = PBAP_APPLICATION_PARAMETER_VCARD_SELECTOR;
390                         application_parameters[i++] = 8;
391                         memset(&application_parameters[i], 0, 4);
392                         i += 4;
393                         big_endian_store_32(application_parameters, i, pbap_client->vcard_selector);
394                         i += 4;
395                     }
396                     // vCard Selector Operator
397                     if (pbap_client->vcard_selector_operator != PBAP_VCARD_SELECTOR_OPERATOR_OR){
398                         application_parameters[i++] = PBAP_APPLICATION_PARAMETER_VCARD_SELECTOR_OPERATOR;
399                         application_parameters[i++] = 1;
400                         application_parameters[i++] = pbap_client->vcard_selector_operator;
401                     }
402                 }
403                 if (pbap_client->phone_number){
404                     // Search by phone number
405                     phone_number_len = btstack_min(PBAP_MAX_PHONE_NUMBER_LEN, strlen(pbap_client->phone_number));
406                     application_parameters[i++] = PBAP_APPLICATION_PARAMETER_SEARCH_VALUE;
407                     application_parameters[i++] = phone_number_len;
408                     (void)memcpy(&application_parameters[i],
409                                  pbap_client->phone_number, phone_number_len);
410                     i += phone_number_len;
411                     application_parameters[i++] = PBAP_APPLICATION_PARAMETER_SEARCH_PROPERTY;
412                     application_parameters[i++] = 1;
413                     application_parameters[i++] = 0x01; // Number
414                 }
415                 if (i){
416                     goep_client_header_add_application_parameters(pbap_client->goep_cid, &application_parameters[0], i);
417                 }
418             }
419             // send packet
420             pbap_client->state = PBAP_W4_GET_CARD_LIST_COMPLETE;
421             pbap_client->request_number++;
422             goep_client_execute(pbap_client->goep_cid);
423             break;
424         case PBAP_W2_GET_CARD_ENTRY:
425             goep_client_request_create_get(pbap_client->goep_cid);
426             if (pbap_client->request_number == 0){
427                 if (!pbap_client->flow_control_enabled){
428                     goep_client_header_add_srm_enable(pbap_client->goep_cid);
429                     pbap_client->srm_state = SRM_W4_CONFIRM;
430                 }
431                 goep_client_header_add_name(pbap_client->goep_cid, pbap_client->vcard_name);
432                 goep_client_header_add_type(pbap_client->goep_cid, pbap_vcard_entry_type);
433                 // TODO: support property selector
434                 // TODO: support format
435                 i = 0;
436                 uint32_t property_selector_lower = 0;
437                 uint32_t property_selector_higher = 0;
438                 if (strncmp(pbap_client->vcard_name, "X-BT-UID:", 9) == 0) {
439                     property_selector_lower = 1U << 31;
440                 }
441                 if (strncmp(pbap_client->vcard_name, "X-BT-UCI:", 9) == 0) {
442                     property_selector_lower = 1U << 30;
443                 }
444                 if (property_selector_lower != 0){
445                     application_parameters[i++] = PBAP_APPLICATION_PARAMETER_PROPERTY_SELECTOR;
446                     application_parameters[i++] = 8;
447                     big_endian_store_32(application_parameters, i, property_selector_higher);
448                     i += 4;
449                     big_endian_store_32(application_parameters, i, property_selector_lower);
450                     i += 4;
451                 }
452                 if (i > 0){
453                     goep_client_header_add_application_parameters(pbap_client->goep_cid, &application_parameters[0], i);
454                 }
455                 pbap_client->state = PBAP_W4_GET_CARD_ENTRY_COMPLETE;
456             }
457             // send packet
458             pbap_client->request_number++;
459             goep_client_execute(pbap_client->goep_cid);
460             break;
461         case PBAP_W2_SET_PATH_ROOT:
462             goep_client_request_create_set_path(pbap_client->goep_cid, 1 << 1); // Don’t create directory
463             goep_client_header_add_name(pbap_client->goep_cid, "");
464             // state
465             pbap_client->state = PBAP_W4_SET_PATH_ROOT_COMPLETE;
466             // send packet
467             goep_client_execute(pbap_client->goep_cid);
468             break;
469         case PBAP_W2_SET_PATH_ELEMENT:
470             // find '/' or '\0'
471             path_element_start = pbap_client->set_path_offset;
472             while ((pbap_client->current_folder[pbap_client->set_path_offset] != '\0') &&
473                 (pbap_client->current_folder[pbap_client->set_path_offset] != '/')){
474                 pbap_client->set_path_offset++;
475             }
476             path_element_len = pbap_client->set_path_offset-path_element_start;
477             (void)memcpy(path_element,
478                          &pbap_client->current_folder[path_element_start],
479                          path_element_len);
480             path_element[path_element_len] = 0;
481 
482             // skip /
483             if (pbap_client->current_folder[pbap_client->set_path_offset] == '/'){
484                 pbap_client->set_path_offset++;
485             }
486 
487             // status
488             log_info("Path element '%s'", path_element);
489 
490             goep_client_request_create_set_path(pbap_client->goep_cid, 1 << 1); // Don’t create directory
491             goep_client_header_add_name(pbap_client->goep_cid, (const char *) path_element); // next element
492             // state
493             pbap_client->state = PBAP_W4_SET_PATH_ELEMENT_COMPLETE;
494             // send packet
495             goep_client_execute(pbap_client->goep_cid);
496             break;
497         default:
498             break;
499     }
500 }
501 
502 static void pbap_parse_authentication_challenge(pbap_client_t * context, const uint8_t * challenge_data, uint16_t challenge_len){
503     int i;
504 
505     for (i=0 ; i<challenge_len ; ){
506         int tag = challenge_data[i];
507         int len = challenge_data[i + 1];
508         i += 2;
509         switch (tag) {
510             case 0:
511                 if (len != 0x10) {
512                     log_error("Invalid OBEX digest len %u", len);
513                     return;
514                 }
515                 (void)memcpy(context->authentication_nonce, &challenge_data[i], 16);
516                 break;
517             case 1:
518                 context->authentication_options = challenge_data[i];
519                 break;
520             case 2:
521                 // TODO: handle charset
522                 // charset_code = challenge_data[i];
523                 break;
524             default:
525                 break;
526         }
527         i += len;
528     }
529 }
530 
531 static void pbap_process_srm_headers(pbap_client_t * context, uint8_t *packet, uint16_t size){
532 
533     if (packet[0] != OBEX_RESP_CONTINUE) return;
534 
535     // get SRM and SRMP Headers
536     int srm_value = OBEX_SRM_DISABLE;
537     int srmp_value = OBEX_SRMP_NEXT;
538     obex_iterator_t it;
539     for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(context->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
540         uint8_t hi = obex_iterator_get_hi(&it);
541         uint16_t     data_len = obex_iterator_get_data_len(&it);
542         const uint8_t  * data = obex_iterator_get_data(&it);
543         switch (hi){
544             case OBEX_HEADER_SINGLE_RESPONSE_MODE:
545                 if (data_len != 1) break;
546                 srm_value = *data;
547                 break;
548             case OBEX_HEADER_SINGLE_RESPONSE_MODE_PARAMETER:
549                 if (data_len != 1) break;
550                 srmp_value = *data;
551                 break;
552             default:
553                 break;
554         }
555     }
556 
557     // Update SRM state based on SRM haders
558     switch (context->srm_state){
559         case SRM_W4_CONFIRM:
560             switch (srm_value){
561                 case OBEX_SRM_ENABLE:
562                     switch (srmp_value){
563                         case OBEX_SRMP_WAIT:
564                             context->srm_state = SRM_ENABLED_BUT_WAITING;
565                             break;
566                         default:
567                             context->srm_state = SRM_ENABLED;
568                             break;
569                     }
570                     break;
571                 default:
572                     context->srm_state = SRM_DISABLED;
573                     break;
574             }
575             break;
576         case SRM_ENABLED_BUT_WAITING:
577             switch (srmp_value){
578                 case OBEX_SRMP_WAIT:
579                     context->srm_state = SRM_ENABLED_BUT_WAITING;
580                     break;
581                 default:
582                     context->srm_state = SRM_ENABLED;
583                     break;
584             }
585             break;
586         default:
587             break;
588     }
589     log_info("SRM state %u", context->srm_state);
590 }
591 
592 static void pbap_client_process_vcard_listing(uint8_t *packet, uint16_t size){
593     obex_iterator_t it;
594     for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
595         uint8_t hi = obex_iterator_get_hi(&it);
596         if ((hi == OBEX_HEADER_END_OF_BODY) ||
597             (hi == OBEX_HEADER_BODY)){
598             uint16_t     data_len = obex_iterator_get_data_len(&it);
599             const uint8_t  * data =  obex_iterator_get_data(&it);
600             // now try parsing it
601             yxml_init(&pbap_client->xml_parser, pbap_client->xml_buffer, sizeof(pbap_client->xml_buffer));
602             pbap_client->parser_card_found = false;
603             pbap_client->parser_name_found = false;
604             pbap_client->parser_handle_found = false;
605             uint16_t char_len;
606             while (data_len--){
607                 yxml_ret_t r = yxml_parse(&pbap_client->xml_parser, *data++);
608                 switch (r){
609                     case YXML_ELEMSTART:
610                         pbap_client->parser_card_found = strcmp("card", pbap_client->xml_parser.elem) == 0;
611                         break;
612                     case YXML_ELEMEND:
613                         if (pbap_client->parser_card_found){
614                             pbap_client_emit_card_result_event(pbap_client, pbap_client->parser_name, pbap_client->parser_handle);
615                         }
616                         pbap_client->parser_card_found = false;
617                         break;
618                     case YXML_ATTRSTART:
619                         if (!pbap_client->parser_card_found) break;
620                         if (strcmp("name", pbap_client->xml_parser.attr) == 0){
621                             pbap_client->parser_name_found = true;
622                             pbap_client->parser_name[0]    = 0;
623                             break;
624                         }
625                         if (strcmp("handle", pbap_client->xml_parser.attr) == 0){
626                             pbap_client->parser_handle_found = true;
627                             pbap_client->parser_handle[0]    = 0;
628                             break;
629                         }
630                         break;
631                     case YXML_ATTRVAL:
632                         if (pbap_client->parser_name_found) {
633                             // "In UTF-8, characters from the U+0000..U+10FFFF range (the UTF-16 accessible range) are encoded using sequences of 1 to 4 octets."
634                             char_len = strlen(pbap_client->xml_parser.data);
635                             if ((strlen(pbap_client->parser_name) + char_len + 1) >= sizeof(pbap_client->parser_name)) break;
636                             strcat(pbap_client->parser_name, pbap_client->xml_parser.data);
637                             break;
638                         }
639                         if (pbap_client->parser_handle_found) {
640                             // "In UTF-8, characters from the U+0000..U+10FFFF range (the UTF-16 accessible range) are encoded using sequences of 1 to 4 octets."
641                             char_len = strlen(pbap_client->xml_parser.data);
642                             if ((strlen(pbap_client->parser_handle) + char_len + 1) >= sizeof(pbap_client->parser_handle)) break;
643                             strcat(pbap_client->parser_handle, pbap_client->xml_parser.data);
644                             break;
645                         }
646                         break;
647                     case YXML_ATTREND:
648                         pbap_client->parser_name_found = false;
649                         pbap_client->parser_handle_found = false;
650                         break;
651                     default:
652                         break;
653                 }
654             }
655         }
656     }
657 }
658 static void pbap_packet_handler_hci(uint8_t *packet, uint16_t size){
659     UNUSED(size);
660     uint8_t status;
661     switch (hci_event_packet_get_type(packet)) {
662         case HCI_EVENT_GOEP_META:
663             switch (hci_event_goep_meta_get_subevent_code(packet)){
664                 case GOEP_SUBEVENT_CONNECTION_OPENED:
665                     status = goep_subevent_connection_opened_get_status(packet);
666                     pbap_client->con_handle = goep_subevent_connection_opened_get_con_handle(packet);
667                     pbap_client->incoming = goep_subevent_connection_opened_get_incoming(packet);
668                     goep_subevent_connection_opened_get_bd_addr(packet, pbap_client->bd_addr);
669                     if (status){
670                         log_info("pbap: connection failed %u", status);
671                         pbap_client->state = PBAP_INIT;
672                         pbap_client_emit_connected_event(pbap_client, status);
673                     } else {
674                         log_info("pbap: connection established");
675                         pbap_client->goep_cid = goep_subevent_connection_opened_get_goep_cid(packet);
676                         pbap_client->state = PBAP_W2_SEND_CONNECT_REQUEST;
677                         goep_client_request_can_send_now(pbap_client->goep_cid);
678                     }
679                     break;
680                 case GOEP_SUBEVENT_CONNECTION_CLOSED:
681                     if (pbap_client->state != PBAP_CONNECTED){
682                         pbap_client_emit_operation_complete_event(pbap_client, OBEX_DISCONNECTED);
683                     }
684                     pbap_client->state = PBAP_INIT;
685                     pbap_client_emit_connection_closed_event(pbap_client);
686                     break;
687                 case GOEP_SUBEVENT_CAN_SEND_NOW:
688                     pbap_handle_can_send_now();
689                     break;
690                 default:
691                     break;
692             }
693             break;
694         default:
695             break;
696     }
697 }
698 
699 static void pbap_packet_handler_goep(uint8_t *packet, uint16_t size){
700     obex_iterator_t it;
701     int wait_for_user = 0;
702 
703     // TODO: handle chunked data
704     switch (pbap_client->state){
705         case PBAP_W4_CONNECT_RESPONSE:
706             switch (packet[0]){
707                 case OBEX_RESP_SUCCESS:
708                     for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
709                         uint8_t hi = obex_iterator_get_hi(&it);
710                         if (hi == OBEX_HEADER_CONNECTION_ID){
711                             goep_client_set_connection_id(pbap_client->goep_cid, obex_iterator_get_data_32(&it));
712                         }
713                     }
714                     pbap_client->state = PBAP_CONNECTED;
715                     pbap_client->vcard_selector_supported = pbap_supported_features & goep_client_get_pbap_supported_features(pbap_client->goep_cid) & PBAP_SUPPORTED_FEATURES_VCARD_SELECTING;
716                     pbap_client_emit_connected_event(pbap_client, 0);
717                     break;
718                 case OBEX_RESP_UNAUTHORIZED:
719                     for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
720                         uint8_t hi = obex_iterator_get_hi(&it);
721                         if (hi == OBEX_HEADER_AUTHENTICATION_CHALLENGE){
722                             pbap_parse_authentication_challenge(pbap_client, obex_iterator_get_data(&it), obex_iterator_get_data_len(&it));
723                         }
724                     }
725                     pbap_client->state = PBAP_W4_USER_AUTHENTICATION;
726                     pbap_client_emit_authentication_event(pbap_client, pbap_client->authentication_options);
727                     break;
728                 default:
729                     log_info("pbap: obex connect failed, result 0x%02x", packet[0]);
730                     pbap_client->state = PBAP_INIT;
731                     pbap_client_emit_connected_event(pbap_client, OBEX_CONNECT_FAILED);
732                     break;
733             }
734             break;
735         case PBAP_W4_DISCONNECT_RESPONSE:
736             goep_client_disconnect(pbap_client->goep_cid);
737             break;
738         case PBAP_W4_SET_PATH_ROOT_COMPLETE:
739         case PBAP_W4_SET_PATH_ELEMENT_COMPLETE:
740             if (packet[0] == OBEX_RESP_SUCCESS){
741                 // more path?
742                 if (pbap_client->current_folder[pbap_client->set_path_offset]){
743                     pbap_client->state = PBAP_W2_SET_PATH_ELEMENT;
744                     goep_client_request_can_send_now(pbap_client->goep_cid);
745                 } else {
746                     pbap_client->current_folder = NULL;
747                     pbap_client->state = PBAP_CONNECTED;
748                     pbap_client_emit_operation_complete_event(pbap_client, 0);
749                 }
750             } else if (packet[0] == OBEX_RESP_NOT_FOUND){
751                 pbap_client->state = PBAP_CONNECTED;
752                 pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_FOUND);
753             } else {
754                 pbap_client->state = PBAP_CONNECTED;
755                 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR);
756             }
757             break;
758         case PBAP_W4_PHONEBOOK:
759             pbap_client->flow_next_triggered = 0;
760             wait_for_user = 0;
761             for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
762                 uint8_t hi = obex_iterator_get_hi(&it);
763                 uint16_t     data_len = obex_iterator_get_data_len(&it);
764                 const uint8_t  * data = obex_iterator_get_data(&it);
765                 switch (hi){
766                     case OBEX_HEADER_BODY:
767                     case OBEX_HEADER_END_OF_BODY:
768                         pbap_client->client_handler(PBAP_DATA_PACKET, pbap_client->cid, (uint8_t *) data, data_len);
769                         wait_for_user++;
770                         if (wait_for_user > 1){
771                             log_error("wait_for_user %u", wait_for_user);
772                         }
773                         break;
774                     default:
775                         break;
776                 }
777             }
778             switch(packet[0]){
779                 case OBEX_RESP_CONTINUE:
780                     pbap_process_srm_headers(pbap_client, packet, size);
781                     if (pbap_client->srm_state ==  SRM_ENABLED) break;
782                     pbap_client->state = PBAP_W2_PULL_PHONEBOOK;
783                     if (!pbap_client->flow_control_enabled || !wait_for_user || pbap_client->flow_next_triggered) {
784                         goep_client_request_can_send_now(pbap_client->goep_cid);
785                     }
786                     break;
787                 case OBEX_RESP_SUCCESS:
788                     pbap_client->state = PBAP_CONNECTED;
789                     pbap_client_emit_operation_complete_event(pbap_client, 0);
790                     break;
791                 default:
792                     log_info("unexpected response 0x%02x", packet[0]);
793                     pbap_client->state = PBAP_CONNECTED;
794                     pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR);
795                     break;
796             }
797             break;
798         case PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE:
799             pbap_client->state = PBAP_CONNECTED;
800             if (packet[0] == OBEX_RESP_SUCCESS){
801                 int have_size = 0;
802                 uint16_t phonebook_size;
803                 for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
804                     uint8_t hi = obex_iterator_get_hi(&it);
805                     if (hi == OBEX_HEADER_APPLICATION_PARAMETERS){
806                         uint16_t     data_len = obex_iterator_get_data_len(&it);
807                         const uint8_t  * data =  obex_iterator_get_data(&it);
808                         // iterate over application headers (TLV with 1 bytes len)
809                         unsigned int i = 0;
810                         while (i<data_len){
811                             uint8_t tag = data[i++];
812                             uint8_t len = data[i++];
813                             if ((tag == PBAP_APPLICATION_PARAMETER_PHONEBOOK_SIZE) && (len == 2)){
814                                 have_size = 1;
815                                 phonebook_size = big_endian_read_16(data, i);
816                             }
817                             i+=len;
818                         }
819                     }
820                 }
821                 if (have_size){
822                     pbap_client_emit_phonebook_size_event(pbap_client, 0, phonebook_size);
823                     break;
824                 }
825             }
826             pbap_client_emit_phonebook_size_event(pbap_client, OBEX_UNKNOWN_ERROR, 0);
827             break;
828         case PBAP_W4_GET_CARD_LIST_COMPLETE:
829             switch (packet[0]){
830                 case OBEX_RESP_CONTINUE:
831                     // process data
832                     pbap_client_process_vcard_listing(packet, size);
833                     // handle continue
834                     pbap_process_srm_headers(pbap_client, packet, size);
835                     if (pbap_client->srm_state ==  SRM_ENABLED) break;
836                     pbap_client->state = PBAP_W2_GET_CARD_LIST;
837                     if (!pbap_client->flow_control_enabled || !wait_for_user || pbap_client->flow_next_triggered) {
838                         goep_client_request_can_send_now(pbap_client->goep_cid);
839                     }
840                     break;
841                 case OBEX_RESP_SUCCESS:
842                     // process data
843                     pbap_client_process_vcard_listing(packet, size);
844                     // done
845                     pbap_client->state = PBAP_CONNECTED;
846                     pbap_client_emit_operation_complete_event(pbap_client, 0);
847                     break;
848                 case OBEX_RESP_NOT_ACCEPTABLE:
849                     pbap_client->state = PBAP_CONNECTED;
850                     pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_ACCEPTABLE);
851                     break;
852                 default:
853                     log_info("unexpected response 0x%02x", packet[0]);
854                     pbap_client->state = PBAP_CONNECTED;
855                     pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR);
856                     break;
857             }
858             break;
859         case PBAP_W4_GET_CARD_ENTRY_COMPLETE:
860             switch (packet[0]){
861                 case OBEX_RESP_CONTINUE:
862                     pbap_process_srm_headers(pbap_client, packet, size);
863                     if (pbap_client->srm_state ==  SRM_ENABLED) break;
864                     pbap_client->state = PBAP_W2_GET_CARD_ENTRY;
865                     if (!pbap_client->flow_control_enabled || !wait_for_user || pbap_client->flow_next_triggered) {
866                         goep_client_request_can_send_now(pbap_client->goep_cid);
867                     }
868                     break;
869                 case OBEX_RESP_SUCCESS:
870                     for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
871                         uint8_t hi = obex_iterator_get_hi(&it);
872                         if ((hi == OBEX_HEADER_END_OF_BODY) ||
873                             (hi == OBEX_HEADER_BODY)){
874                             // uint16_t     data_len = obex_iterator_get_data_len(&it);
875                             // const uint8_t  * data =  obex_iterator_get_data(&it);
876                             // now try parsing it
877                         }
878                     }
879                     pbap_client->state = PBAP_CONNECTED;
880                     pbap_client_emit_operation_complete_event(pbap_client, 0);
881                     break;
882                 case OBEX_RESP_NOT_ACCEPTABLE:
883                     pbap_client->state = PBAP_CONNECTED;
884                     pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_ACCEPTABLE);
885                     break;
886                 default:
887                     log_info("unexpected response 0x%02x", packet[0]);
888                     pbap_client->state = PBAP_CONNECTED;
889                     pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR);
890                     break;
891             }
892             break;
893         default:
894             break;
895     }
896 }
897 
898 static void pbap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
899     UNUSED(channel); // ok: there is no channel
900     UNUSED(size);    // ok: handling own geop events
901 
902     switch (packet_type){
903         case HCI_EVENT_PACKET:
904             pbap_packet_handler_hci(packet, size);
905             break;
906         case GOEP_DATA_PACKET:
907             pbap_packet_handler_goep(packet, size);
908             break;
909         default:
910             break;
911     }
912 }
913 
914 void pbap_client_init(void){
915     memset(pbap_client, 0, sizeof(pbap_client_t));
916     pbap_client->state = PBAP_INIT;
917     pbap_client->cid = 1;
918 }
919 
920 void pbap_client_deinit(void){
921 }
922 
923 uint8_t pbap_connect(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t * out_cid){
924     if (pbap_client->state != PBAP_INIT) return BTSTACK_MEMORY_ALLOC_FAILED;
925 
926     pbap_client->state = PBAP_W4_GOEP_CONNECTION;
927     pbap_client->client_handler = handler;
928     pbap_client->vcard_selector = 0;
929     pbap_client->vcard_selector_operator = PBAP_VCARD_SELECTOR_OPERATOR_OR;
930 
931     uint8_t err = goep_client_create_connection(&pbap_packet_handler, addr, BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS_PSE, &pbap_client->goep_cid);
932     *out_cid = pbap_client->cid;
933     if (err) return err;
934     return 0;
935 }
936 
937 uint8_t pbap_disconnect(uint16_t pbap_cid){
938     UNUSED(pbap_cid);
939     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
940     pbap_client->state = PBAP_W2_SEND_DISCONNECT_REQUEST;
941     goep_client_request_can_send_now(pbap_client->goep_cid);
942     return 0;
943 }
944 
945 uint8_t pbap_get_phonebook_size(uint16_t pbap_cid, const char * path){
946     UNUSED(pbap_cid);
947     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
948     pbap_client->state = PBAP_W2_GET_PHONEBOOK_SIZE;
949     pbap_client->phonebook_path = path;
950     pbap_client->request_number = 0;
951     goep_client_request_can_send_now(pbap_client->goep_cid);
952     return 0;
953 }
954 
955 uint8_t pbap_pull_phonebook(uint16_t pbap_cid, const char * path){
956     UNUSED(pbap_cid);
957     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
958     pbap_client->state = PBAP_W2_PULL_PHONEBOOK;
959     pbap_client->phonebook_path = path;
960     pbap_client->request_number = 0;
961     goep_client_request_can_send_now(pbap_client->goep_cid);
962     return 0;
963 }
964 
965 uint8_t pbap_set_phonebook(uint16_t pbap_cid, const char * path){
966     UNUSED(pbap_cid);
967     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
968     pbap_client->state = PBAP_W2_SET_PATH_ROOT;
969     pbap_client->current_folder = path;
970     pbap_client->set_path_offset = 0;
971     goep_client_request_can_send_now(pbap_client->goep_cid);
972     return 0;
973 }
974 
975 uint8_t pbap_authentication_password(uint16_t pbap_cid, const char * password){
976     UNUSED(pbap_cid);
977     if (pbap_client->state != PBAP_W4_USER_AUTHENTICATION) return BTSTACK_BUSY;
978     pbap_client->state = PBAP_W2_SEND_AUTHENTICATED_CONNECT;
979     pbap_client->authentication_password = password;
980     goep_client_request_can_send_now(pbap_client->goep_cid);
981     return 0;
982 }
983 
984 uint8_t pbap_pull_vcard_listing(uint16_t pbap_cid, const char * path){
985     UNUSED(pbap_cid);
986     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
987     pbap_client->state = PBAP_W2_GET_CARD_LIST;
988     pbap_client->phonebook_path = path;
989     pbap_client->phone_number = NULL;
990     pbap_client->request_number = 0;
991     goep_client_request_can_send_now(pbap_client->goep_cid);
992     return 0;
993 }
994 
995 uint8_t pbap_pull_vcard_entry(uint16_t pbap_cid, const char * path){
996     UNUSED(pbap_cid);
997     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
998     pbap_client->state = PBAP_W2_GET_CARD_ENTRY;
999     // pbap_client->phonebook_path = NULL;
1000     // pbap_client->phone_number = NULL;
1001     pbap_client->vcard_name = path;
1002     pbap_client->request_number = 0;
1003     goep_client_request_can_send_now(pbap_client->goep_cid);
1004     return 0;
1005 }
1006 
1007 uint8_t pbap_lookup_by_number(uint16_t pbap_cid, const char * phone_number){
1008     UNUSED(pbap_cid);
1009     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
1010     pbap_client->state = PBAP_W2_GET_CARD_LIST;
1011     pbap_client->phonebook_path = pbap_vcard_listing_name;
1012     pbap_client->phone_number   = phone_number;
1013     pbap_client->request_number = 0;
1014     goep_client_request_can_send_now(pbap_client->goep_cid);
1015     return 0;
1016 }
1017 
1018 uint8_t pbap_abort(uint16_t pbap_cid){
1019     UNUSED(pbap_cid);
1020     log_info("abort current operation, state 0x%02x", pbap_client->state);
1021     pbap_client->abort_operation = 1;
1022     return 0;
1023 }
1024 
1025 uint8_t pbap_next_packet(uint16_t pbap_cid){
1026     // log_info("pbap_next_packet, state %x", pbap_client->state);
1027     UNUSED(pbap_cid);
1028     if (!pbap_client->flow_control_enabled) return 0;
1029     switch (pbap_client->state){
1030         case PBAP_W2_PULL_PHONEBOOK:
1031             goep_client_request_can_send_now(pbap_client->goep_cid);
1032             break;
1033         case PBAP_W4_PHONEBOOK:
1034             pbap_client->flow_next_triggered = 1;
1035             break;
1036         default:
1037             break;
1038     }
1039     return 0;
1040 }
1041 
1042 uint8_t pbap_set_flow_control_mode(uint16_t pbap_cid, int enable){
1043     UNUSED(pbap_cid);
1044     if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
1045     pbap_client->flow_control_enabled = enable;
1046     return 0;
1047 }
1048 
1049 uint8_t pbap_set_vcard_selector(uint16_t pbap_cid, uint32_t vcard_selector){
1050     UNUSED(pbap_cid);
1051     pbap_client->vcard_selector = vcard_selector;
1052     return 0;
1053 }
1054 
1055 uint8_t pbap_set_vcard_selector_operator(uint16_t pbap_cid, int vcard_selector_operator){
1056     UNUSED(pbap_cid);
1057     pbap_client->vcard_selector_operator = vcard_selector_operator;
1058     return 0;
1059 }
1060