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 BLUEKITCHEN
24 * GMBH 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
57 #include "classic/obex.h"
58 #include "classic/obex_parser.h"
59 #include "classic/goep_client.h"
60 #include "classic/pbap.h"
61 #include "classic/pbap_client.h"
62 #include "sdp_util.h"
63
64 // 796135f0-f0c5-11d8-0966- 0800200c9a66
65 static const uint8_t pbap_uuid[] = { 0x79, 0x61, 0x35, 0xf0, 0xf0, 0xc5, 0x11, 0xd8, 0x09, 0x66, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66};
66
67 const char * pbap_phonebook_type = "x-bt/phonebook";
68 const char * pbap_vcard_listing_type = "x-bt/vcard-listing";
69 const char * pbap_vcard_entry_type = "x-bt/vcard";
70
71 const char * pbap_vcard_listing_name = "pb";
72
73 // used for MD5
74 static const uint8_t colon = (uint8_t) ':';
75
76 static uint32_t pbap_client_supported_features;
77
78 static bool pbap_client_singleton_used;
79
80 static btstack_linked_list_t pbap_clients;
81
82 // emit events
pbap_client_emit_connected_event(pbap_client_t * context,uint8_t status)83 static void pbap_client_emit_connected_event(pbap_client_t * context, uint8_t status){
84 uint8_t event[15];
85 int pos = 0;
86 event[pos++] = HCI_EVENT_PBAP_META;
87 pos++; // skip len
88 event[pos++] = PBAP_SUBEVENT_CONNECTION_OPENED;
89 little_endian_store_16(event,pos,context->goep_cid);
90 pos+=2;
91 event[pos++] = status;
92 (void)memcpy(&event[pos], context->bd_addr, 6);
93 pos += 6;
94 little_endian_store_16(event,pos,context->con_handle);
95 pos += 2;
96 event[pos++] = 0;
97 event[1] = pos - 2;
98 if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos);
99 context->client_handler(HCI_EVENT_PACKET, context->goep_cid, &event[0], pos);
100 }
101
pbap_client_emit_connection_closed_event(pbap_client_t * context)102 static void pbap_client_emit_connection_closed_event(pbap_client_t * context){
103 uint8_t event[5];
104 int pos = 0;
105 event[pos++] = HCI_EVENT_PBAP_META;
106 pos++; // skip len
107 event[pos++] = PBAP_SUBEVENT_CONNECTION_CLOSED;
108 little_endian_store_16(event,pos,context->goep_cid);
109 pos+=2;
110 event[1] = pos - 2;
111 if (pos != sizeof(event)) log_error("pbap_client_emit_connection_closed_event size %u", pos);
112 context->client_handler(HCI_EVENT_PACKET, context->goep_cid, &event[0], pos);
113 }
114
pbap_client_emit_operation_complete_event(pbap_client_t * context,uint8_t status)115 static void pbap_client_emit_operation_complete_event(pbap_client_t * context, uint8_t status){
116 uint8_t event[6];
117 int pos = 0;
118 event[pos++] = HCI_EVENT_PBAP_META;
119 pos++; // skip len
120 event[pos++] = PBAP_SUBEVENT_OPERATION_COMPLETED;
121 little_endian_store_16(event,pos,context->goep_cid);
122 pos+=2;
123 event[pos++]= status;
124 event[1] = pos - 2;
125 if (pos != sizeof(event)) log_error("pbap_client_emit_can_send_now_event size %u", pos);
126 context->client_handler(HCI_EVENT_PACKET, context->goep_cid, &event[0], pos);
127 }
128
pbap_client_emit_phonebook_size_event(pbap_client_t * context,uint8_t status,uint16_t phonebook_size)129 static void pbap_client_emit_phonebook_size_event(pbap_client_t * context, uint8_t status, uint16_t phonebook_size){
130 uint8_t event[8];
131 int pos = 0;
132 event[pos++] = HCI_EVENT_PBAP_META;
133 pos++; // skip len
134 event[pos++] = PBAP_SUBEVENT_PHONEBOOK_SIZE;
135 little_endian_store_16(event,pos,context->goep_cid);
136 pos+=2;
137 event[pos++] = status;
138 little_endian_store_16(event,pos, phonebook_size);
139 pos+=2;
140 event[1] = pos - 2;
141 if (pos != sizeof(event)) log_error("pbap_client_emit_phonebook_size_event size %u", pos);
142 context->client_handler(HCI_EVENT_PACKET, context->goep_cid, &event[0], pos);
143 }
144
pbap_client_emit_authentication_event(pbap_client_t * context,uint8_t options)145 static void pbap_client_emit_authentication_event(pbap_client_t * context, uint8_t options){
146 // split options
147 uint8_t user_id_required = (options & 1) ? 1 : 0;
148 uint8_t full_access = (options & 2) ? 1 : 0;
149
150 uint8_t event[7];
151 int pos = 0;
152 event[pos++] = HCI_EVENT_PBAP_META;
153 pos++; // skip len
154 event[pos++] = PBAP_SUBEVENT_AUTHENTICATION_REQUEST;
155 little_endian_store_16(event,pos,context->goep_cid);
156 pos+=2;
157 event[pos++] = user_id_required;
158 event[pos++] = full_access;
159 if (pos != sizeof(event)) log_error("pbap_client_emit_authentication_event size %u", pos);
160 context->client_handler(HCI_EVENT_PACKET, context->goep_cid, &event[0], pos);
161 }
162
pbap_client_emit_card_result_event(pbap_client_t * context,const char * name,const char * handle)163 static void pbap_client_emit_card_result_event(pbap_client_t * context, const char * name, const char * handle){
164 uint8_t event[5 + PBAP_MAX_NAME_LEN + PBAP_MAX_HANDLE_LEN];
165 int pos = 0;
166 event[pos++] = HCI_EVENT_PBAP_META;
167 pos++; // skip len
168 event[pos++] = PBAP_SUBEVENT_CARD_RESULT;
169 little_endian_store_16(event,pos,context->goep_cid);
170 pos+=2;
171 int name_len = btstack_min(PBAP_MAX_NAME_LEN, (uint16_t) strlen(name));
172 event[pos++] = name_len;
173 (void)memcpy(&event[pos], name, name_len);
174 pos += name_len;
175 int handle_len = btstack_min(PBAP_MAX_HANDLE_LEN, (uint16_t) strlen(handle));
176 event[pos++] = handle_len;
177 (void)memcpy(&event[pos], handle, handle_len);
178 pos += handle_len;
179 event[1] = pos - 2;
180 context->client_handler(HCI_EVENT_PACKET, context->goep_cid, &event[0], pos);
181 }
182
pbap_client_for_cid(uint16_t cid)183 static pbap_client_t * pbap_client_for_cid(uint16_t cid){
184 btstack_linked_list_iterator_t it;
185 btstack_linked_list_iterator_init(&it, &pbap_clients);
186 while (btstack_linked_list_iterator_has_next(&it)){
187 pbap_client_t * client = (pbap_client_t *) btstack_linked_list_iterator_next(&it);
188 if (client->goep_cid == cid){
189 return client;
190 }
191 }
192 return NULL;
193 }
194
pbap_client_finalize(pbap_client_t * client)195 static void pbap_client_finalize(pbap_client_t *client) {
196 client->state = PBAP_CLIENT_INIT;
197 btstack_linked_list_remove(&pbap_clients, (btstack_linked_item_t*) client);
198 }
199
pbap_client_vcard_listing_init_parser(pbap_client_t * client)200 static void pbap_client_vcard_listing_init_parser(pbap_client_t * client){
201 yxml_init(&client->xml_parser, client->xml_buffer, sizeof(client->xml_buffer));
202 client->parser_card_found = false;
203 client->parser_name_found = false;
204 client->parser_handle_found = false;
205 }
206
pbap_client_phonebook_size_parser_init(pbap_client_phonebook_size_parser_t * phonebook_size_parer)207 static void pbap_client_phonebook_size_parser_init(pbap_client_phonebook_size_parser_t * phonebook_size_parer){
208 memset(phonebook_size_parer, 0, sizeof(pbap_client_phonebook_size_parser_t));
209 }
210
pbap_client_phonebook_size_parser_process_data(pbap_client_phonebook_size_parser_t * phonebook_size_parser,const uint8_t * data_buffer,uint16_t data_len)211 static void pbap_client_phonebook_size_parser_process_data(pbap_client_phonebook_size_parser_t * phonebook_size_parser, const uint8_t * data_buffer, uint16_t data_len){
212 while (data_len){
213 uint16_t bytes_to_consume = 1;
214 switch(phonebook_size_parser->state){
215 case PBAP_CLIENT_PHONEBOOK_SIZE_PARSER_STATE_INVALID:
216 return;
217 case PBAP_CLIENT_PHONEBOOK_SIZE_PARSER_STATE_W4_TYPE:
218 phonebook_size_parser->type = *data_buffer;
219 phonebook_size_parser->state = PBAP_CLIENT_PHONEBOOK_SIZE_PARSER_STATE_W4_LEN;
220 break;
221 case PBAP_CLIENT_PHONEBOOK_SIZE_PARSER_STATE_W4_LEN:
222 phonebook_size_parser->len = *data_buffer;
223 phonebook_size_parser->state = PBAP_CLIENT_PHONEBOOK_SIZE_PARSER_STATE_W4_VALUE;
224 switch (phonebook_size_parser->type){
225 case PBAP_APPLICATION_PARAMETER_PHONEBOOK_SIZE:
226 if (phonebook_size_parser->len != 2){
227 phonebook_size_parser->state = PBAP_CLIENT_PHONEBOOK_SIZE_PARSER_STATE_INVALID;
228 return;
229 }
230 break;
231 default:
232 break;
233 }
234 break;
235 case PBAP_CLIENT_PHONEBOOK_SIZE_PARSER_STATE_W4_VALUE:
236 bytes_to_consume = btstack_min(phonebook_size_parser->len - phonebook_size_parser->pos, data_len);
237 switch (phonebook_size_parser->type){
238 case PBAP_APPLICATION_PARAMETER_PHONEBOOK_SIZE:
239 memcpy(&phonebook_size_parser->size_buffer[phonebook_size_parser->pos], data_buffer, bytes_to_consume);
240 break;
241 default:
242 // ignore data
243 break;
244 }
245 phonebook_size_parser->pos += bytes_to_consume;
246 if (phonebook_size_parser->pos == phonebook_size_parser->len){
247 phonebook_size_parser->state = PBAP_CLIENT_PHONEBOOK_SIZE_PARSER_STATE_W4_TYPE;
248 switch (phonebook_size_parser->type){
249 case PBAP_APPLICATION_PARAMETER_PHONEBOOK_SIZE:
250 phonebook_size_parser->have_size = true;
251 break;
252 default:
253 break;
254 }
255 }
256 break;
257 default:
258 break;
259 }
260 data_buffer += bytes_to_consume;
261 data_len -= bytes_to_consume;
262 }
263 }
264
obex_auth_parser_init(pbap_client_obex_auth_parser_t * auth_parser)265 static void obex_auth_parser_init(pbap_client_obex_auth_parser_t * auth_parser){
266 memset(auth_parser, 0, sizeof(pbap_client_obex_auth_parser_t));
267 }
268
obex_auth_parser_process_data(pbap_client_obex_auth_parser_t * auth_parser,const uint8_t * data_buffer,uint16_t data_len)269 static void obex_auth_parser_process_data(pbap_client_obex_auth_parser_t * auth_parser, const uint8_t * data_buffer, uint16_t data_len){
270 while (data_len){
271 uint16_t bytes_to_consume = 1;
272 switch(auth_parser->state){
273 case OBEX_AUTH_PARSER_STATE_INVALID:
274 return;
275 case OBEX_AUTH_PARSER_STATE_W4_TYPE:
276 auth_parser->type = *data_buffer;
277 auth_parser->state = OBEX_AUTH_PARSER_STATE_W4_LEN;
278 break;
279 case OBEX_AUTH_PARSER_STATE_W4_LEN:
280 auth_parser->len = *data_buffer;
281 switch (auth_parser->type){
282 case 0:
283 if (auth_parser->len != 0x10){
284 auth_parser->state = OBEX_AUTH_PARSER_STATE_INVALID;
285 return;
286 }
287 break;
288 case 1:
289 if (auth_parser->len != 0x01){
290 auth_parser->state = OBEX_AUTH_PARSER_STATE_INVALID;
291 return;
292 }
293 break;
294 case 2:
295 // TODO: handle charset
296 // charset_code = challenge_data[i];
297 break;
298 default:
299 break;
300 }
301 auth_parser->state = OBEX_AUTH_PARSER_STATE_W4_VALUE;
302 break;
303 case OBEX_AUTH_PARSER_STATE_W4_VALUE:
304 bytes_to_consume = btstack_min(auth_parser->len - auth_parser->pos, data_len);
305 switch (auth_parser->type){
306 case 0:
307 memcpy(&auth_parser->authentication_nonce[auth_parser->pos], data_buffer, bytes_to_consume);
308 break;
309 case 1:
310 auth_parser->authentication_options = *data_buffer;
311 break;
312 default:
313 // ignore
314 break;
315 }
316 auth_parser->pos += bytes_to_consume;
317 if (auth_parser->pos == auth_parser->len){
318 auth_parser->state = OBEX_AUTH_PARSER_STATE_W4_TYPE;
319 }
320 break;
321 default:
322 btstack_unreachable();
323 break;
324 }
325 data_buffer += bytes_to_consume;
326 data_len -= bytes_to_consume;
327 }
328 }
329
pbap_client_yml_append_character(yxml_t * xml_parser,char * buffer,uint16_t buffer_size)330 static void pbap_client_yml_append_character(yxml_t * xml_parser, char * buffer, uint16_t buffer_size){
331 // "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."
332 uint16_t char_len = (uint16_t) strlen(xml_parser->data);
333 btstack_assert(char_len <= 4);
334 uint16_t dest_len = (uint16_t) strlen(buffer);
335 uint16_t zero_pos = dest_len + char_len;
336 if (zero_pos >= buffer_size) return;
337 memcpy(&buffer[dest_len], xml_parser->data, char_len);
338 buffer[zero_pos] = '\0';
339 }
340
pbap_client_process_vcard_list_body(pbap_client_t * client,const uint8_t * data,uint16_t data_len)341 static void pbap_client_process_vcard_list_body(pbap_client_t *client, const uint8_t *data, uint16_t data_len) {
342
343 while (data_len--) {
344 yxml_ret_t r = yxml_parse(&client->xml_parser, *data++);
345 switch (r) {
346 case YXML_ELEMSTART:
347 client->parser_card_found = strcmp("card", client->xml_parser.elem) == 0;
348 break;
349 case YXML_ELEMEND:
350 if (client->parser_card_found) {
351 pbap_client_emit_card_result_event(client, client->parser_name,
352 client->parser_handle);
353 }
354 client->parser_card_found = false;
355 break;
356 case YXML_ATTRSTART:
357 if (!client->parser_card_found) break;
358 if (strcmp("name", client->xml_parser.attr) == 0) {
359 client->parser_name_found = true;
360 client->parser_name[0] = 0;
361 break;
362 }
363 if (strcmp("handle", client->xml_parser.attr) == 0) {
364 client->parser_handle_found = true;
365 client->parser_handle[0] = 0;
366 break;
367 }
368 break;
369 case YXML_ATTRVAL:
370 if (client->parser_name_found) {
371 pbap_client_yml_append_character(&client->xml_parser,
372 client->parser_name,
373 sizeof(client->parser_name));
374 break;
375 }
376 if (client->parser_handle_found) {
377 pbap_client_yml_append_character(&client->xml_parser,
378 client->parser_handle,
379 sizeof(client->parser_handle));
380 break;
381 }
382 break;
383 case YXML_ATTREND:
384 client->parser_name_found = false;
385 client->parser_handle_found = false;
386 break;
387 default:
388 break;
389 }
390 }
391 }
392
pbap_client_parser_callback_connect(void * user_data,uint8_t header_id,uint16_t total_len,uint16_t data_offset,const uint8_t * data_buffer,uint16_t data_len)393 static void pbap_client_parser_callback_connect(void * user_data, uint8_t header_id, uint16_t total_len, uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len){
394 pbap_client_t * client = (pbap_client_t *) user_data;
395 switch (header_id){
396 case OBEX_HEADER_CONNECTION_ID:
397 if (obex_parser_header_store(client->obex_header_buffer, sizeof(client->obex_header_buffer), total_len, data_offset, data_buffer, data_len) == OBEX_PARSER_HEADER_COMPLETE){
398 goep_client_set_connection_id(client->goep_cid, big_endian_read_32(client->obex_header_buffer, 0));
399 }
400 break;
401 case OBEX_HEADER_AUTHENTICATION_CHALLENGE:
402 obex_auth_parser_process_data(&client->obex_auth_parser, data_buffer, data_len);
403 break;
404 default:
405 break;
406 }
407 }
408
pbap_client_parser_callback_get_phonebook_size(void * user_data,uint8_t header_id,uint16_t total_len,uint16_t data_offset,const uint8_t * data_buffer,uint16_t data_len)409 static void pbap_client_parser_callback_get_phonebook_size(void * user_data, uint8_t header_id, uint16_t total_len, uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len){
410 UNUSED(total_len);
411 UNUSED(data_offset);
412 pbap_client_t *client = (pbap_client_t *) user_data;
413 switch (header_id) {
414 case OBEX_HEADER_APPLICATION_PARAMETERS:
415 pbap_client_phonebook_size_parser_process_data(&client->phonebook_size_parser, data_buffer, data_len);
416 break;
417 default:
418 break;
419 }
420 }
421
pbap_client_parser_callback_get_operation(void * user_data,uint8_t header_id,uint16_t total_len,uint16_t data_offset,const uint8_t * data_buffer,uint16_t data_len)422 static void pbap_client_parser_callback_get_operation(void * user_data, uint8_t header_id, uint16_t total_len, uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len){
423 pbap_client_t *client = (pbap_client_t *) user_data;
424 switch (header_id) {
425 case OBEX_HEADER_SINGLE_RESPONSE_MODE:
426 obex_parser_header_store(&client->obex_srm.srm_value, 1, total_len, data_offset, data_buffer, data_len);
427 break;
428 case OBEX_HEADER_SINGLE_RESPONSE_MODE_PARAMETER:
429 obex_parser_header_store(&client->obex_srm.srmp_value, 1, total_len, data_offset, data_buffer, data_len);
430 break;
431 case OBEX_HEADER_BODY:
432 case OBEX_HEADER_END_OF_BODY:
433 switch(client->state){
434 case PBAP_CLIENT_W4_PHONEBOOK:
435 case PBAP_CLIENT_W4_GET_CARD_ENTRY_COMPLETE:
436 if (data_offset + data_len == total_len){
437 client->flow_wait_for_user = true;
438 }
439 client->client_handler(PBAP_DATA_PACKET, client->goep_cid, (uint8_t *) data_buffer, data_len);
440 break;
441 case PBAP_CLIENT_W4_GET_CARD_LIST_COMPLETE:
442 pbap_client_process_vcard_list_body(client, data_buffer, data_len);
443 break;
444 default:
445 btstack_unreachable();
446 break;
447 }
448 break;
449 default:
450 // ignore other headers
451 break;
452 }
453 }
454
pbap_client_application_params_add_vcard_selector(const pbap_client_t * client,uint8_t * application_parameters)455 static uint16_t pbap_client_application_params_add_vcard_selector(const pbap_client_t * client, uint8_t * application_parameters){
456 uint16_t pos = 0;
457 if (client->vcard_selector_supported){
458 // vCard Selector
459 if (client->vcard_selector){
460 application_parameters[pos++] = PBAP_APPLICATION_PARAMETER_VCARD_SELECTOR;
461 application_parameters[pos++] = 8;
462 memset(&application_parameters[pos], 0, 4);
463 pos += 4;
464 big_endian_store_32(application_parameters, pos, client->vcard_selector);
465 pos += 4;
466 }
467 // vCard Selector Operator
468 if (client->vcard_selector_operator != PBAP_VCARD_SELECTOR_OPERATOR_OR){
469 application_parameters[pos++] = PBAP_APPLICATION_PARAMETER_VCARD_SELECTOR_OPERATOR;
470 application_parameters[pos++] = 1;
471 application_parameters[pos++] = client->vcard_selector_operator;
472 }
473 }
474 return pos;
475 }
476
pbap_client_application_params_add_max_list_count(const pbap_client_t * client,uint8_t * application_parameters,uint16_t max_count)477 static uint16_t pbap_client_application_params_add_max_list_count(const pbap_client_t * client, uint8_t * application_parameters, uint16_t max_count){
478 UNUSED(client);
479 uint16_t pos = 0;
480 application_parameters[pos++] = PBAP_APPLICATION_PARAMETER_MAX_LIST_COUNT;
481 application_parameters[pos++] = 2;
482 big_endian_store_16(application_parameters, 2, max_count);
483 pos += 2;
484 return pos;
485 }
486
pbap_client_application_params_add_list_start_offset(const pbap_client_t * client,uint8_t * application_parameters,uint16_t list_start_offset)487 static uint16_t pbap_client_application_params_add_list_start_offset(const pbap_client_t * client, uint8_t * application_parameters, uint16_t list_start_offset){
488 uint16_t pos = 0;
489 if (client->list_start_offset != 0){
490 application_parameters[pos++] = PBAP_APPLICATION_PARAMETER_LIST_START_OFFSET;
491 application_parameters[pos++] = 2;
492 big_endian_store_16(application_parameters, 2, list_start_offset);
493 pos += 2;
494 }
495 return pos;
496 }
497
pbap_client_application_params_add_order(const pbap_client_t * client,uint8_t * application_parameters,uint8_t order)498 static uint16_t pbap_client_application_params_add_order(const pbap_client_t * client, uint8_t * application_parameters, uint8_t order){
499 uint16_t pos = 0;
500 if (client->order != 0){
501 application_parameters[pos++] = PBAP_APPLICATION_PARAMETER_ORDER;
502 application_parameters[pos++] = 1;
503 application_parameters[pos++] = order;
504 }
505 return pos;
506 }
507
pbap_client_application_params_add_search_property(const pbap_client_t * client,uint8_t * application_parameters,uint8_t search_property)508 static uint16_t pbap_client_application_params_add_search_property(const pbap_client_t * client, uint8_t * application_parameters, uint8_t search_property){
509 uint16_t pos = 0;
510 if (client->search_property != 0){
511 application_parameters[pos++] = PBAP_APPLICATION_PARAMETER_SEARCH_PROPERTY;
512 application_parameters[pos++] = 1;
513 application_parameters[pos++] = search_property;
514 }
515 return pos;
516 }
517
pbap_client_application_params_add_search_value(const pbap_client_t * client,uint8_t * application_parameters,const char * search_value)518 static uint16_t pbap_client_application_params_add_search_value(const pbap_client_t * client, uint8_t * application_parameters, const char* search_value){
519 uint16_t pos = 0;
520 if (client->search_value != 0){
521 uint32_t length = (uint32_t) strlen(search_value);
522 application_parameters[pos++] = PBAP_APPLICATION_PARAMETER_SEARCH_VALUE;
523 application_parameters[pos++] = length;
524 memcpy (&application_parameters[pos], search_value, length);
525 pos += length;
526 }
527 return pos;
528 }
529
530 // max size: PBAP_MAX_PHONE_NUMBER_LEN + 5
pbap_client_application_params_add_phone_number(const pbap_client_t * client,uint8_t * application_parameters)531 static uint16_t pbap_client_application_params_add_phone_number(const pbap_client_t * client, uint8_t * application_parameters){
532 uint16_t pos = 0;
533 if (client->phone_number){
534 // Search by phone number
535 uint16_t phone_number_len = btstack_min(PBAP_MAX_PHONE_NUMBER_LEN, (uint16_t) strlen(client->phone_number));
536 application_parameters[pos++] = PBAP_APPLICATION_PARAMETER_SEARCH_VALUE;
537 btstack_assert(phone_number_len <= 255);
538 application_parameters[pos++] = (uint8_t) phone_number_len;
539 (void)memcpy(&application_parameters[pos],
540 client->phone_number, phone_number_len);
541 pos += phone_number_len;
542 application_parameters[pos++] = PBAP_APPLICATION_PARAMETER_SEARCH_PROPERTY;
543 application_parameters[pos++] = 1;
544 application_parameters[pos++] = 0x01; // Number
545 }
546 return pos;
547 }
548
pbap_client_application_params_add_property_selector(const pbap_client_t * client,uint8_t * application_parameters)549 static uint16_t pbap_client_application_params_add_property_selector(const pbap_client_t * client, uint8_t * application_parameters){
550 // TODO: support format
551 uint16_t pos = 0;
552 uint32_t property_selector_lower = client->property_selector;
553 if (client->vcard_name != NULL){
554 if (strncmp(client->vcard_name, "X-BT-UID:", 9) == 0) {
555 property_selector_lower |= 1U << 31;
556 }
557 if (strncmp(client->vcard_name, "X-BT-UCI:", 9) == 0) {
558 property_selector_lower |= 1U << 30;
559 }
560 }
561 if (property_selector_lower != 0){
562 application_parameters[pos++] = PBAP_APPLICATION_PARAMETER_PROPERTY_SELECTOR;
563 application_parameters[pos++] = 8;
564 big_endian_store_32(application_parameters, pos, 0); // upper 32-bits are reserved/unused so far
565 pos += 4;
566 big_endian_store_32(application_parameters, pos, property_selector_lower);
567 pos += 4;
568 }
569 return pos;
570 }
571
572 // Mandatory if the PSE advertises a PbapSupportedFeatures attribute in its SDP record, else excluded.
pbap_client_application_parameters_add_supported_features(const pbap_client_t * client,uint8_t * application_parameters)573 static uint16_t pbap_client_application_parameters_add_supported_features(const pbap_client_t * client, uint8_t *application_parameters) {
574 uint16_t pos = 0;
575 if (goep_client_get_pbap_supported_features(client->goep_cid) != PBAP_FEATURES_NOT_PRESENT){
576 application_parameters[pos++] = PBAP_APPLICATION_PARAMETER_PBAP_SUPPORTED_FEATURES;
577 application_parameters[pos++] = 4;
578 big_endian_store_32(application_parameters, 2, pbap_client_supported_features);
579 pos += 4;
580 }
581 return pos;
582 }
583
pbap_client_add_application_parameters(const pbap_client_t * client,uint8_t * application_parameters,uint16_t len)584 static void pbap_client_add_application_parameters(const pbap_client_t * client, uint8_t * application_parameters, uint16_t len){
585 if (len > 0){
586 goep_client_header_add_application_parameters(client->goep_cid, &application_parameters[0], len);
587 }
588 }
589
pbap_client_prepare_srm_header(pbap_client_t * client)590 static void pbap_client_prepare_srm_header(pbap_client_t * client){
591 obex_srm_client_set_waiting(&client->obex_srm, client->flow_control_enabled);
592 obex_srm_client_prepare_header(&client->obex_srm, client->goep_cid);
593 }
594
pbap_client_prepare_get_operation(pbap_client_t * client)595 static void pbap_client_prepare_get_operation(pbap_client_t * client){
596 obex_parser_init_for_response(&client->obex_parser, OBEX_OPCODE_GET, pbap_client_parser_callback_get_operation, client);
597 obex_srm_client_reset_fields(&client->obex_srm);
598 client->obex_parser_waiting_for_response = true;
599 }
600
pbap_handle_can_send_now(pbap_client_t * pbap_client)601 static void pbap_handle_can_send_now(pbap_client_t *pbap_client) {
602 uint16_t path_element_start;
603 uint16_t path_element_len;
604 const char * path_element;
605 uint8_t application_parameters[PBAP_MAX_PHONE_NUMBER_LEN + 10];
606 uint8_t challenge_response[36];
607 uint16_t pos;
608
609 MD5_CTX md5_ctx;
610
611 if (pbap_client->abort_operation){
612 pbap_client->abort_operation = 0;
613 // prepare request
614 goep_client_request_create_abort(pbap_client->goep_cid);
615 // state
616 pbap_client->state = PBAP_CLIENT_W4_ABORT_COMPLETE;
617 // prepare response
618 obex_parser_init_for_response(&pbap_client->obex_parser, OBEX_OPCODE_ABORT, NULL, pbap_client);
619 obex_srm_client_init(&pbap_client->obex_srm);
620 pbap_client->obex_parser_waiting_for_response = true;
621 // send packet
622 goep_client_execute(pbap_client->goep_cid);
623 return;
624 }
625
626 switch (pbap_client->state){
627 case PBAP_CLIENT_W2_SEND_CONNECT_REQUEST:
628 // prepare request
629 goep_client_request_create_connect(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT);
630 goep_client_header_add_target(pbap_client->goep_cid, pbap_uuid, 16);
631 pos = 0;
632 pos += pbap_client_application_parameters_add_supported_features(pbap_client, &application_parameters[pos]);
633 pbap_client_add_application_parameters(pbap_client, application_parameters, pos);
634 // state
635 pbap_client->state = PBAP_CLIENT_W4_CONNECT_RESPONSE;
636 // prepare response
637 obex_parser_init_for_response(&pbap_client->obex_parser, OBEX_OPCODE_CONNECT, pbap_client_parser_callback_connect, pbap_client);
638 obex_auth_parser_init(&pbap_client->obex_auth_parser);
639 obex_srm_client_init(&pbap_client->obex_srm);
640 pbap_client->obex_parser_waiting_for_response = true;
641 // send packet
642 goep_client_execute(pbap_client->goep_cid);
643 break;
644 case PBAP_CLIENT_W2_SEND_AUTHENTICATED_CONNECT:
645 // prepare request
646 goep_client_request_create_connect(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT);
647 goep_client_header_add_target(pbap_client->goep_cid, pbap_uuid, 16);
648 // setup authentication challenge response
649 pos = 0;
650 challenge_response[pos++] = 0; // Tag Digest
651 challenge_response[pos++] = 16; // Len
652 // calculate md5
653 MD5_Init(&md5_ctx);
654 MD5_Update(&md5_ctx, pbap_client->obex_auth_parser.authentication_nonce, 16);
655 MD5_Update(&md5_ctx, &colon, 1);
656 MD5_Update(&md5_ctx, pbap_client->authentication_password, (uint16_t) strlen(pbap_client->authentication_password));
657 MD5_Final(&challenge_response[pos], &md5_ctx);
658 pos += 16;
659 challenge_response[pos++] = 2; // Tag Nonce
660 challenge_response[pos++] = 16; // Len
661 (void)memcpy(&challenge_response[pos], pbap_client->obex_auth_parser.authentication_nonce, 16);
662 pos += 16;
663 goep_client_header_add_challenge_response(pbap_client->goep_cid, challenge_response, pos);
664 // state
665 pbap_client->state = PBAP_CLIENT_W4_CONNECT_RESPONSE;
666 // prepare response
667 obex_parser_init_for_response(&pbap_client->obex_parser, OBEX_OPCODE_CONNECT, pbap_client_parser_callback_connect, pbap_client);
668 obex_srm_client_init(&pbap_client->obex_srm);
669 pbap_client->obex_parser_waiting_for_response = true;
670 // send packet
671 goep_client_execute(pbap_client->goep_cid);
672 break;
673 case PBAP_CLIENT_W2_SEND_DISCONNECT_REQUEST:
674 // prepare request
675 goep_client_request_create_disconnect(pbap_client->goep_cid);
676 // state
677 pbap_client->state = PBAP_CLIENT_W4_DISCONNECT_RESPONSE;
678 // prepare response
679 obex_parser_init_for_response(&pbap_client->obex_parser, OBEX_OPCODE_DISCONNECT, NULL, pbap_client);
680 obex_srm_client_init(&pbap_client->obex_srm);
681 pbap_client->obex_parser_waiting_for_response = true;
682 // send packet
683 goep_client_execute(pbap_client->goep_cid);
684 return;
685 case PBAP_CLIENT_W2_GET_PHONEBOOK_SIZE:
686 // prepare request
687 goep_client_request_create_get(pbap_client->goep_cid);
688 pbap_client_prepare_srm_header(pbap_client);
689 goep_client_header_add_name(pbap_client->goep_cid, pbap_client->phonebook_path);
690 goep_client_header_add_type(pbap_client->goep_cid, pbap_phonebook_type);
691
692 pos = 0;
693 pos += pbap_client_application_params_add_vcard_selector(pbap_client, &application_parameters[pos]);
694 // just get size
695 pos += pbap_client_application_params_add_max_list_count(pbap_client, &application_parameters[pos], 0);
696 pbap_client_add_application_parameters(pbap_client, application_parameters, pos);
697
698 // state
699 pbap_client->state = PBAP_CLIENT_W4_GET_PHONEBOOK_SIZE_COMPLETE;
700 // prepare response
701 obex_parser_init_for_response(&pbap_client->obex_parser, OBEX_OPCODE_GET, pbap_client_parser_callback_get_phonebook_size, pbap_client);
702 obex_srm_client_init(&pbap_client->obex_srm);
703 pbap_client_phonebook_size_parser_init(&pbap_client->phonebook_size_parser);
704 pbap_client->obex_parser_waiting_for_response = true;
705 // send packet
706 goep_client_execute(pbap_client->goep_cid);
707 break;
708 case PBAP_CLIENT_W2_PULL_PHONEBOOK:
709 // prepare request
710 goep_client_request_create_get(pbap_client->goep_cid);
711 if (pbap_client->request_number == 0){
712 obex_srm_client_init(&pbap_client->obex_srm);
713 pbap_client_prepare_srm_header(pbap_client);
714 goep_client_header_add_name(pbap_client->goep_cid, pbap_client->phonebook_path);
715 goep_client_header_add_type(pbap_client->goep_cid, pbap_phonebook_type);
716
717 pos = 0;
718 pos += pbap_client_application_params_add_property_selector(pbap_client, &application_parameters[pos]);
719 if (pbap_client->max_list_count){
720 pos += pbap_client_application_params_add_max_list_count(pbap_client, &application_parameters[pos], pbap_client->max_list_count);
721 }
722 pos += pbap_client_application_params_add_list_start_offset (pbap_client, &application_parameters[pos], pbap_client->list_start_offset);
723 pos += pbap_client_application_params_add_vcard_selector(pbap_client, &application_parameters[pos]);
724 pbap_client_add_application_parameters(pbap_client, application_parameters, pos);
725 } else {
726 pbap_client_prepare_srm_header(pbap_client);
727 }
728 // state
729 pbap_client->state = PBAP_CLIENT_W4_PHONEBOOK;
730 pbap_client->flow_next_triggered = 0;
731 pbap_client->flow_wait_for_user = false;
732 // prepare response
733 pbap_client_prepare_get_operation(pbap_client);
734 // send packet
735 pbap_client->request_number++;
736 goep_client_execute(pbap_client->goep_cid);
737 break;
738 case PBAP_CLIENT_W2_GET_CARD_LIST:
739 // prepare request
740 goep_client_request_create_get(pbap_client->goep_cid);
741 if (pbap_client->request_number == 0){
742 obex_srm_client_init(&pbap_client->obex_srm);
743 pbap_client_prepare_srm_header(pbap_client);
744 goep_client_header_add_name(pbap_client->goep_cid, pbap_client->phonebook_path);
745 goep_client_header_add_type(pbap_client->goep_cid, pbap_vcard_listing_type);
746
747 pos = 0;
748 pos += pbap_client_application_params_add_vcard_selector(pbap_client, &application_parameters[pos]);
749 pos += pbap_client_application_params_add_phone_number(pbap_client, &application_parameters[pos]);
750 if (pbap_client->search_value != NULL){
751 pos += pbap_client_application_params_add_search_value(pbap_client, &application_parameters[pos], pbap_client->search_value);
752 }
753 if (pbap_client->search_property){
754 pos += pbap_client_application_params_add_search_property(pbap_client, &application_parameters[pos], pbap_client->search_property);
755 }
756 if (pbap_client->order){
757 pos += pbap_client_application_params_add_order(pbap_client, &application_parameters[pos], pbap_client->order);
758 }
759 if (pbap_client->max_list_count){
760 pos += pbap_client_application_params_add_max_list_count(pbap_client, &application_parameters[pos], pbap_client->max_list_count);
761 }
762 if (pbap_client->list_start_offset){
763 pos += pbap_client_application_params_add_list_start_offset (pbap_client, &application_parameters[pos], pbap_client->list_start_offset);
764 }
765 pbap_client_add_application_parameters(pbap_client, application_parameters, pos);
766 } else {
767 pbap_client_prepare_srm_header(pbap_client);
768 }
769 // state
770 pbap_client->state = PBAP_CLIENT_W4_GET_CARD_LIST_COMPLETE;
771 // prepare response
772 pbap_client_prepare_get_operation(pbap_client);
773 // send packet
774 pbap_client->request_number++;
775 goep_client_execute(pbap_client->goep_cid);
776 break;
777 case PBAP_CLIENT_W2_GET_CARD_ENTRY:
778 // prepare request
779 goep_client_request_create_get(pbap_client->goep_cid);
780 if (pbap_client->request_number == 0){
781 obex_srm_client_init(&pbap_client->obex_srm);
782 pbap_client_prepare_srm_header(pbap_client);
783 goep_client_header_add_name(pbap_client->goep_cid, pbap_client->vcard_name);
784 goep_client_header_add_type(pbap_client->goep_cid, pbap_vcard_entry_type);
785
786 pos = 0;
787 pos += pbap_client_application_params_add_property_selector(pbap_client, &application_parameters[pos]);
788 pbap_client_add_application_parameters(pbap_client, application_parameters, pos);
789 } else {
790 pbap_client_prepare_srm_header(pbap_client);
791 }
792 // state
793 pbap_client->state = PBAP_CLIENT_W4_GET_CARD_ENTRY_COMPLETE;
794 // prepare response
795 pbap_client_prepare_get_operation(pbap_client);
796 // send packet
797 pbap_client->request_number++;
798 goep_client_execute(pbap_client->goep_cid);
799 break;
800 case PBAP_CLIENT_W2_SET_PATH_ROOT:
801 // prepare request
802 goep_client_request_create_set_path(pbap_client->goep_cid, 1 << 1); // Don’t create directory
803 goep_client_header_add_name(pbap_client->goep_cid, "");
804 // state
805 pbap_client->state = PBAP_CLIENT_W4_SET_PATH_ROOT_COMPLETE;
806 // prepare response
807 obex_parser_init_for_response(&pbap_client->obex_parser, OBEX_OPCODE_SETPATH, NULL, pbap_client);
808 obex_srm_client_init(&pbap_client->obex_srm);
809 pbap_client->obex_parser_waiting_for_response = true;
810 // send packet
811 goep_client_execute(pbap_client->goep_cid);
812 break;
813 case PBAP_CLIENT_W2_SET_PATH_ELEMENT:
814 // prepare request
815 // find '/' or '\0'
816 path_element_start = pbap_client->set_path_offset;
817 while ((pbap_client->current_folder[pbap_client->set_path_offset] != '\0') &&
818 (pbap_client->current_folder[pbap_client->set_path_offset] != '/')){
819 pbap_client->set_path_offset++;
820 }
821 path_element_len = pbap_client->set_path_offset-path_element_start;
822 path_element = (const char *) &pbap_client->current_folder[path_element_start];
823
824 // skip /
825 if (pbap_client->current_folder[pbap_client->set_path_offset] == '/'){
826 pbap_client->set_path_offset++;
827 }
828
829 goep_client_request_create_set_path(pbap_client->goep_cid, 1 << 1); // Don’t create directory
830 goep_client_header_add_name_prefix(pbap_client->goep_cid, path_element, path_element_len); // next element
831 // state
832 pbap_client->state = PBAP_CLIENT_W4_SET_PATH_ELEMENT_COMPLETE;
833 // prepare response
834 obex_parser_init_for_response(&pbap_client->obex_parser, OBEX_OPCODE_SETPATH, NULL, pbap_client);
835 obex_srm_client_init(&pbap_client->obex_srm);
836 pbap_client->obex_parser_waiting_for_response = true;
837 // send packet
838 goep_client_execute(pbap_client->goep_cid);
839 break;
840 default:
841 break;
842 }
843 }
844
pbap_packet_handler_hci(uint8_t * packet,uint16_t size)845 static void pbap_packet_handler_hci(uint8_t *packet, uint16_t size){
846 UNUSED(size);
847 uint8_t status;
848
849 pbap_client_t * client;
850
851 switch (hci_event_packet_get_type(packet)) {
852 case HCI_EVENT_GOEP_META:
853 switch (hci_event_goep_meta_get_subevent_code(packet)){
854 case GOEP_SUBEVENT_CONNECTION_OPENED:
855 client = pbap_client_for_cid(goep_subevent_connection_opened_get_goep_cid(packet));
856 btstack_assert(client != NULL);
857 status = goep_subevent_connection_opened_get_status(packet);
858 goep_subevent_connection_opened_get_bd_addr(packet, client->bd_addr);
859 if (status){
860 log_info("pbap: connection failed %u", status);
861 pbap_client_finalize(client);
862 pbap_client_emit_connected_event(client, status);
863 } else {
864 log_info("pbap: connection established");
865 client->con_handle = goep_subevent_connection_opened_get_con_handle(packet);
866 client->state = PBAP_CLIENT_W2_SEND_CONNECT_REQUEST;
867 goep_client_request_can_send_now(client->goep_cid);
868 }
869 break;
870 case GOEP_SUBEVENT_CONNECTION_CLOSED:
871 client = pbap_client_for_cid(goep_subevent_connection_closed_get_goep_cid(packet));
872 btstack_assert(client != NULL);
873 if (client->state > PBAP_CLIENT_CONNECTED){
874 pbap_client_emit_operation_complete_event(client, OBEX_DISCONNECTED);
875 }
876 pbap_client_finalize(client);
877 pbap_client_emit_connection_closed_event(client);
878 break;
879 case GOEP_SUBEVENT_CAN_SEND_NOW:
880 client = pbap_client_for_cid(goep_subevent_can_send_now_get_goep_cid(packet));
881 btstack_assert(client != NULL);
882 pbap_handle_can_send_now(client);
883 break;
884 default:
885 break;
886 }
887 break;
888 default:
889 break;
890 }
891 }
892
pbap_packet_handler_goep(pbap_client_t * client,uint8_t * packet,uint16_t size)893 static void pbap_packet_handler_goep(pbap_client_t *client, uint8_t *packet, uint16_t size) {
894
895 if (client->obex_parser_waiting_for_response == false) return;
896
897 obex_parser_object_state_t parser_state;
898 parser_state = obex_parser_process_data(&client->obex_parser, packet, size);
899 if (parser_state == OBEX_PARSER_OBJECT_STATE_COMPLETE){
900 client->obex_parser_waiting_for_response = false;
901 obex_parser_operation_info_t op_info;
902 obex_parser_get_operation_info(&client->obex_parser, &op_info);
903 switch (client->state){
904 case PBAP_CLIENT_W4_CONNECT_RESPONSE:
905 switch (op_info.response_code) {
906 case OBEX_RESP_SUCCESS:
907 client->state = PBAP_CLIENT_CONNECTED;
908 client->vcard_selector_supported = pbap_client_supported_features & goep_client_get_pbap_supported_features(client->goep_cid) & PBAP_SUPPORTED_FEATURES_VCARD_SELECTING;
909 pbap_client_emit_connected_event(client, ERROR_CODE_SUCCESS);
910 break;
911 case OBEX_RESP_UNAUTHORIZED:
912 client->state = PBAP_CLIENT_W4_USER_AUTHENTICATION;
913 pbap_client_emit_authentication_event(client, client->obex_auth_parser.authentication_options);
914 break;
915 default:
916 log_info("pbap: obex connect failed, result 0x%02x", packet[0]);
917 client->state = PBAP_CLIENT_INIT;
918 pbap_client_emit_connected_event(client, OBEX_CONNECT_FAILED);
919 break;
920 }
921 break;
922 case PBAP_CLIENT_W4_DISCONNECT_RESPONSE:
923 client->state = PBAP_CLIENT_CONNECTED;
924 goep_client_disconnect(client->goep_cid);
925 break;
926 case PBAP_CLIENT_W4_SET_PATH_ROOT_COMPLETE:
927 case PBAP_CLIENT_W4_SET_PATH_ELEMENT_COMPLETE:
928 switch (op_info.response_code) {
929 case OBEX_RESP_SUCCESS:
930 // more path?
931 if (client->current_folder[client->set_path_offset]) {
932 client->state = PBAP_CLIENT_W2_SET_PATH_ELEMENT;
933 goep_client_request_can_send_now(client->goep_cid);
934 } else {
935 client->current_folder = NULL;
936 client->state = PBAP_CLIENT_CONNECTED;
937 pbap_client_emit_operation_complete_event(client, ERROR_CODE_SUCCESS);
938 }
939 break;
940 case OBEX_RESP_NOT_FOUND:
941 client->state = PBAP_CLIENT_CONNECTED;
942 pbap_client_emit_operation_complete_event(client, OBEX_NOT_FOUND);
943 break;
944 default:
945 client->state = PBAP_CLIENT_CONNECTED;
946 pbap_client_emit_operation_complete_event(client, OBEX_UNKNOWN_ERROR);
947 break;
948 }
949 break;
950 case PBAP_CLIENT_W4_PHONEBOOK:
951 switch (op_info.response_code) {
952 case OBEX_RESP_CONTINUE:
953 obex_srm_client_handle_headers(&client->obex_srm);
954 if (obex_srm_client_is_srm_active(&client->obex_srm)) {
955 // prepare response
956 pbap_client_prepare_get_operation(client);
957 break;
958 }
959 client->state = PBAP_CLIENT_W2_PULL_PHONEBOOK;
960 if (!client->flow_control_enabled || !client->flow_wait_for_user ||
961 client->flow_next_triggered) {
962 goep_client_request_can_send_now(client->goep_cid);
963 }
964 break;
965 case OBEX_RESP_SUCCESS:
966 client->state = PBAP_CLIENT_CONNECTED;
967 pbap_client_emit_operation_complete_event(client, ERROR_CODE_SUCCESS);
968 break;
969 default:
970 log_info("unexpected response 0x%02x", packet[0]);
971 client->state = PBAP_CLIENT_CONNECTED;
972 pbap_client_emit_operation_complete_event(client, OBEX_UNKNOWN_ERROR);
973 break;
974 }
975 break;
976 case PBAP_CLIENT_W4_GET_PHONEBOOK_SIZE_COMPLETE:
977 switch (op_info.response_code) {
978 case OBEX_RESP_SUCCESS:
979 if (client->phonebook_size_parser.have_size) {
980 uint16_t phonebook_size = big_endian_read_16(client->phonebook_size_parser.size_buffer, 0);
981 client->state = PBAP_CLIENT_CONNECTED;
982 pbap_client_emit_phonebook_size_event(client, 0, phonebook_size);
983 break;
984 }
985 /* fall through */
986 default:
987 client->state = PBAP_CLIENT_CONNECTED;
988 pbap_client_emit_phonebook_size_event(client, OBEX_UNKNOWN_ERROR, 0);
989 break;
990 }
991 break;
992 case PBAP_CLIENT_W4_GET_CARD_LIST_COMPLETE:
993 switch (op_info.response_code) {
994 case OBEX_RESP_CONTINUE:
995 // handle continue
996 obex_srm_client_handle_headers(&client->obex_srm);
997 if (obex_srm_client_is_srm_active(&client->obex_srm)) {
998 // prepare response
999 pbap_client_prepare_get_operation(client);
1000 break;
1001 }
1002 client->state = PBAP_CLIENT_W2_GET_CARD_LIST;
1003 goep_client_request_can_send_now(client->goep_cid);
1004 break;
1005 case OBEX_RESP_SUCCESS:
1006 // done
1007 client->state = PBAP_CLIENT_CONNECTED;
1008 pbap_client_emit_operation_complete_event(client, ERROR_CODE_SUCCESS);
1009 break;
1010 case OBEX_RESP_NOT_ACCEPTABLE:
1011 client->state = PBAP_CLIENT_CONNECTED;
1012 pbap_client_emit_operation_complete_event(client, OBEX_NOT_ACCEPTABLE);
1013 break;
1014 default:
1015 log_info("unexpected response 0x%02x", packet[0]);
1016 client->state = PBAP_CLIENT_CONNECTED;
1017 pbap_client_emit_operation_complete_event(client, OBEX_UNKNOWN_ERROR);
1018 break;
1019 }
1020 break;
1021 case PBAP_CLIENT_W4_GET_CARD_ENTRY_COMPLETE:
1022 switch (op_info.response_code) {
1023 case OBEX_RESP_CONTINUE:
1024 obex_srm_client_handle_headers(&client->obex_srm);
1025 if (obex_srm_client_is_srm_active(&client->obex_srm)) {
1026 // prepare response
1027 pbap_client_prepare_get_operation(client);
1028 break;
1029 }
1030 client->state = PBAP_CLIENT_W2_GET_CARD_ENTRY;
1031 goep_client_request_can_send_now(client->goep_cid);
1032 break;
1033 case OBEX_RESP_SUCCESS:
1034 client->state = PBAP_CLIENT_CONNECTED;
1035 pbap_client_emit_operation_complete_event(client, ERROR_CODE_SUCCESS);
1036 break;
1037 case OBEX_RESP_NOT_ACCEPTABLE:
1038 client->state = PBAP_CLIENT_CONNECTED;
1039 pbap_client_emit_operation_complete_event(client, OBEX_NOT_ACCEPTABLE);
1040 break;
1041 default:
1042 log_info("unexpected response 0x%02x", packet[0]);
1043 client->state = PBAP_CLIENT_CONNECTED;
1044 pbap_client_emit_operation_complete_event(client, OBEX_UNKNOWN_ERROR);
1045 break;
1046 }
1047 break;
1048 case PBAP_CLIENT_W4_ABORT_COMPLETE:
1049 client->state = PBAP_CLIENT_CONNECTED;
1050 pbap_client_emit_operation_complete_event(client, OBEX_ABORTED);
1051 break;
1052 default:
1053 btstack_unreachable();
1054 break;
1055 }
1056 }
1057 }
1058
pbap_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)1059 static void pbap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
1060 UNUSED(channel); // ok: there is no channel
1061 UNUSED(size); // ok: handling own geop events
1062
1063 pbap_client_t * client;
1064
1065 switch (packet_type){
1066 case HCI_EVENT_PACKET:
1067 pbap_packet_handler_hci(packet, size);
1068 break;
1069 case GOEP_DATA_PACKET:
1070 client = pbap_client_for_cid(channel);
1071 btstack_assert(client != NULL);
1072 pbap_packet_handler_goep(client, packet, size);
1073 break;
1074 default:
1075 break;
1076 }
1077 }
1078
pbap_client_init(void)1079 void pbap_client_init(void){
1080 pbap_client_supported_features =
1081 PBAP_SUPPORTED_FEATURES_DOWNLOAD |
1082 PBAP_SUPPORTED_FEATURES_BROWSING |
1083 PBAP_SUPPORTED_FEATURES_DATABASE_IDENTIFIER |
1084 PBAP_SUPPORTED_FEATURES_FOLDER_VERSION_COUNTERS |
1085 PBAP_SUPPORTED_FEATURES_VCARD_SELECTING |
1086 PBAP_SUPPORTED_FEATURES_ENHANCED_MISSED_CALLS |
1087 PBAP_SUPPORTED_FEATURES_DEFAULT_CONTACT_IMAGE_FORMAT |
1088 PBAP_SUPPORTED_FEATURES_X_BT_UCI_VCARD_PROPERTY |
1089 PBAP_SUPPORTED_FEATURES_X_BT_UID_VCARD_PROPERTY |
1090 PBAP_SUPPORTED_FEATURES_CONTACT_REFERENCING;
1091
1092 pbap_client_singleton_used = false;
1093 }
1094
pbap_client_deinit(void)1095 void pbap_client_deinit(void){
1096 }
1097
pbap_client_connect(pbap_client_t * client,l2cap_ertm_config_t * l2cap_ertm_config,uint8_t * l2cap_ertm_buffer,uint16_t l2cap_ertm_buffer_size,btstack_packet_handler_t handler,bd_addr_t addr,uint16_t * out_cid)1098 uint8_t pbap_client_connect(pbap_client_t * client, l2cap_ertm_config_t *l2cap_ertm_config, uint8_t *l2cap_ertm_buffer,
1099 uint16_t l2cap_ertm_buffer_size, btstack_packet_handler_t handler, bd_addr_t addr, uint16_t * out_cid) {
1100 client->state = PBAP_CLIENT_W4_GOEP_CONNECTION;
1101 client->client_handler = handler;
1102 client->vcard_selector = 0;
1103 client->vcard_selector_operator = PBAP_VCARD_SELECTOR_OPERATOR_OR;
1104
1105 btstack_linked_list_add(&pbap_clients, (btstack_linked_item_t*) client);
1106
1107 uint8_t status = goep_client_connect(&client->goep_client, l2cap_ertm_config, l2cap_ertm_buffer, l2cap_ertm_buffer_size,
1108 &pbap_packet_handler, addr, BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS_PSE, 0, &client->goep_cid);
1109 *out_cid = client->goep_cid;
1110
1111 if (status) {
1112 pbap_client_finalize(client);
1113 }
1114 return status;
1115 }
1116
pbap_connect(btstack_packet_handler_t handler,bd_addr_t addr,uint16_t * out_cid)1117 uint8_t pbap_connect(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t * out_cid){
1118 static pbap_client_t pbap_client_singleton;
1119
1120 l2cap_ertm_config_t *l2cap_ertm_config = NULL;
1121 uint8_t *l2cap_ertm_buffer = NULL;
1122 uint16_t l2cap_ertm_buffer_size = 0;
1123
1124 #ifdef ENABLE_GOEP_L2CAP
1125 // singleton instance
1126 static uint8_t pbap_client_singleton_ertm_buffer[1000];
1127 static l2cap_ertm_config_t pbap_client_singleton_ertm_config = {
1128 1, // ertm mandatory
1129 2, // max transmit, some tests require > 1
1130 2000,
1131 12000,
1132 512, // l2cap ertm mtu
1133 2,
1134 2,
1135 1, // 16-bit FCS
1136 };
1137
1138 l2cap_ertm_config = &pbap_client_singleton_ertm_config;
1139 l2cap_ertm_buffer = pbap_client_singleton_ertm_buffer;
1140 l2cap_ertm_buffer_size = sizeof(pbap_client_singleton_ertm_buffer);
1141 #endif
1142
1143 if (pbap_client_singleton_used && pbap_client_singleton.state != PBAP_CLIENT_INIT){
1144 return BTSTACK_MEMORY_ALLOC_FAILED;
1145 }
1146
1147 pbap_client_singleton_used = true;
1148
1149 memset(&pbap_client_singleton, 0, sizeof(pbap_client_t));
1150 return pbap_client_connect(&pbap_client_singleton, l2cap_ertm_config, l2cap_ertm_buffer, l2cap_ertm_buffer_size, handler, addr, out_cid);
1151 }
1152
pbap_disconnect(uint16_t pbap_cid)1153 uint8_t pbap_disconnect(uint16_t pbap_cid){
1154 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1155 if (pbap_client == NULL){
1156 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1157 }
1158 if (pbap_client->state < PBAP_CLIENT_CONNECTED){
1159 return BTSTACK_BUSY;
1160 }
1161 pbap_client->state = PBAP_CLIENT_W2_SEND_DISCONNECT_REQUEST;
1162 goep_client_request_can_send_now(pbap_client->goep_cid);
1163 return ERROR_CODE_SUCCESS;
1164 }
1165
pbap_get_phonebook_size(uint16_t pbap_cid,const char * path)1166 uint8_t pbap_get_phonebook_size(uint16_t pbap_cid, const char * path){
1167 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1168 if (pbap_client == NULL){
1169 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1170 }
1171 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1172 return BTSTACK_BUSY;
1173 }
1174 pbap_client->state = PBAP_CLIENT_W2_GET_PHONEBOOK_SIZE;
1175 pbap_client->phonebook_path = path;
1176 pbap_client->request_number = 0;
1177 goep_client_request_can_send_now(pbap_client->goep_cid);
1178 return ERROR_CODE_SUCCESS;
1179 }
1180
pbap_pull_phonebook(uint16_t pbap_cid,const char * path)1181 uint8_t pbap_pull_phonebook(uint16_t pbap_cid, const char * path){
1182 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1183 if (pbap_client == NULL){
1184 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1185 }
1186 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1187 return BTSTACK_BUSY;
1188 }
1189 pbap_client->state = PBAP_CLIENT_W2_PULL_PHONEBOOK;
1190 pbap_client->phonebook_path = path;
1191 pbap_client->vcard_name = NULL;
1192 pbap_client->request_number = 0;
1193 goep_client_request_can_send_now(pbap_client->goep_cid);
1194 return ERROR_CODE_SUCCESS;
1195 }
1196
pbap_set_phonebook(uint16_t pbap_cid,const char * path)1197 uint8_t pbap_set_phonebook(uint16_t pbap_cid, const char * path){
1198 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1199 if (pbap_client == NULL){
1200 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1201 }
1202 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1203 return BTSTACK_BUSY;
1204 }
1205 pbap_client->state = PBAP_CLIENT_W2_SET_PATH_ROOT;
1206 pbap_client->current_folder = path;
1207 pbap_client->set_path_offset = 0;
1208 goep_client_request_can_send_now(pbap_client->goep_cid);
1209 return ERROR_CODE_SUCCESS;
1210 }
1211
pbap_authentication_password(uint16_t pbap_cid,const char * password)1212 uint8_t pbap_authentication_password(uint16_t pbap_cid, const char * password){
1213 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1214 if (pbap_client == NULL){
1215 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1216 }
1217 if (pbap_client->state != PBAP_CLIENT_W4_USER_AUTHENTICATION){
1218 return BTSTACK_BUSY;
1219 }
1220 pbap_client->state = PBAP_CLIENT_W2_SEND_AUTHENTICATED_CONNECT;
1221 pbap_client->authentication_password = password;
1222 goep_client_request_can_send_now(pbap_client->goep_cid);
1223 return ERROR_CODE_SUCCESS;
1224 }
1225
pbap_pull_vcard_listing(uint16_t pbap_cid,const char * path)1226 uint8_t pbap_pull_vcard_listing(uint16_t pbap_cid, const char * path){
1227 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1228 if (pbap_client == NULL){
1229 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1230 }
1231 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1232 return BTSTACK_BUSY;
1233 }
1234 pbap_client->state = PBAP_CLIENT_W2_GET_CARD_LIST;
1235 pbap_client->phonebook_path = path;
1236 pbap_client->phone_number = NULL;
1237 pbap_client->request_number = 0;
1238 pbap_client_vcard_listing_init_parser(pbap_client);
1239 goep_client_request_can_send_now(pbap_client->goep_cid);
1240 return ERROR_CODE_SUCCESS;
1241 }
1242
pbap_pull_vcard_entry(uint16_t pbap_cid,const char * path)1243 uint8_t pbap_pull_vcard_entry(uint16_t pbap_cid, const char * path){
1244 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1245 if (pbap_client == NULL){
1246 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1247 }
1248 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1249 return BTSTACK_BUSY;
1250 }
1251 pbap_client->state = PBAP_CLIENT_W2_GET_CARD_ENTRY;
1252 // pbap_client->phonebook_path = NULL;
1253 // pbap_client->phone_number = NULL;
1254 pbap_client->vcard_name = path;
1255 pbap_client->request_number = 0;
1256 goep_client_request_can_send_now(pbap_client->goep_cid);
1257 return ERROR_CODE_SUCCESS;
1258 }
1259
pbap_lookup_by_number(uint16_t pbap_cid,const char * phone_number)1260 uint8_t pbap_lookup_by_number(uint16_t pbap_cid, const char * phone_number){
1261 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1262 if (pbap_client == NULL){
1263 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1264 }
1265 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1266 return BTSTACK_BUSY;
1267 }
1268 pbap_client->state = PBAP_CLIENT_W2_GET_CARD_LIST;
1269 pbap_client->phonebook_path = pbap_vcard_listing_name;
1270 pbap_client->phone_number = phone_number;
1271 pbap_client->request_number = 0;
1272 pbap_client_vcard_listing_init_parser(pbap_client);
1273 goep_client_request_can_send_now(pbap_client->goep_cid);
1274 return ERROR_CODE_SUCCESS;
1275 }
1276
pbap_abort(uint16_t pbap_cid)1277 uint8_t pbap_abort(uint16_t pbap_cid){
1278 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1279 if (pbap_client == NULL){
1280 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1281 }
1282 if ((pbap_client->state < PBAP_CLIENT_CONNECTED) || (pbap_client->abort_operation != 0)){
1283 return ERROR_CODE_COMMAND_DISALLOWED;
1284 }
1285 log_info("abort current operation, state 0x%02x", pbap_client->state);
1286 pbap_client->abort_operation = 1;
1287 return ERROR_CODE_SUCCESS;
1288 }
1289
pbap_next_packet(uint16_t pbap_cid)1290 uint8_t pbap_next_packet(uint16_t pbap_cid){
1291 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1292 if (pbap_client == NULL){
1293 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1294 }
1295 if (pbap_client->flow_wait_for_user == false){
1296 return ERROR_CODE_SUCCESS;
1297 }
1298 switch (pbap_client->state){
1299 case PBAP_CLIENT_W2_PULL_PHONEBOOK:
1300 goep_client_request_can_send_now(pbap_client->goep_cid);
1301 break;
1302 case PBAP_CLIENT_W4_PHONEBOOK:
1303 pbap_client->flow_next_triggered = 1;
1304 break;
1305 default:
1306 break;
1307 }
1308 return ERROR_CODE_SUCCESS;
1309 }
1310
pbap_set_flow_control_mode(uint16_t pbap_cid,int enable)1311 uint8_t pbap_set_flow_control_mode(uint16_t pbap_cid, int enable){
1312 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1313 if (pbap_client == NULL){
1314 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1315 }
1316 switch (pbap_client->state){
1317 case PBAP_CLIENT_CONNECTED:
1318 case PBAP_CLIENT_W4_PHONEBOOK:
1319 pbap_client->flow_control_enabled = enable;
1320 return ERROR_CODE_SUCCESS;
1321 default:
1322 return BTSTACK_BUSY;
1323 }
1324 }
1325
pbap_set_vcard_selector(uint16_t pbap_cid,uint32_t vcard_selector)1326 uint8_t pbap_set_vcard_selector(uint16_t pbap_cid, uint32_t vcard_selector){
1327 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1328 if (pbap_client == NULL){
1329 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1330 }
1331 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1332 return BTSTACK_BUSY;
1333 }
1334 pbap_client->vcard_selector = vcard_selector;
1335 return ERROR_CODE_SUCCESS;
1336 }
1337
pbap_set_vcard_selector_operator(uint16_t pbap_cid,int vcard_selector_operator)1338 uint8_t pbap_set_vcard_selector_operator(uint16_t pbap_cid, int vcard_selector_operator){
1339 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1340 if (pbap_client == NULL){
1341 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1342 }
1343 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1344 return BTSTACK_BUSY;
1345 }
1346 pbap_client->vcard_selector_operator = vcard_selector_operator;
1347 return ERROR_CODE_SUCCESS;
1348 }
1349
pbap_set_property_selector(uint16_t pbap_cid,uint32_t property_selector)1350 uint8_t pbap_set_property_selector(uint16_t pbap_cid, uint32_t property_selector){
1351 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1352 if (pbap_client == NULL){
1353 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1354 }
1355 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1356 return BTSTACK_BUSY;
1357 }
1358 pbap_client->property_selector = property_selector;
1359 return ERROR_CODE_SUCCESS;
1360 }
1361
pbap_set_max_list_count(uint16_t pbap_cid,uint16_t max_list_count)1362 uint8_t pbap_set_max_list_count(uint16_t pbap_cid, uint16_t max_list_count){
1363 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1364 if (pbap_client == NULL){
1365 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1366 }
1367 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1368 return BTSTACK_BUSY;
1369 }
1370 pbap_client->max_list_count = max_list_count;
1371 return ERROR_CODE_SUCCESS;
1372 }
1373
pbap_set_list_start_offset(uint16_t pbap_cid,uint16_t list_start_offset)1374 uint8_t pbap_set_list_start_offset(uint16_t pbap_cid, uint16_t list_start_offset){
1375 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1376 if (pbap_client == NULL){
1377 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1378 }
1379 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1380 return BTSTACK_BUSY;
1381 }
1382 pbap_client->list_start_offset = list_start_offset;
1383 return ERROR_CODE_SUCCESS;
1384 }
1385
pbap_set_order(uint16_t pbap_cid,uint8_t order)1386 uint8_t pbap_set_order(uint16_t pbap_cid, uint8_t order){
1387 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1388 if (pbap_client == NULL){
1389 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1390 }
1391 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1392 return BTSTACK_BUSY;
1393 }
1394 pbap_client->order = order;
1395 return ERROR_CODE_SUCCESS;
1396 }
1397
pbap_set_search_property(uint16_t pbap_cid,uint8_t search_property)1398 uint8_t pbap_set_search_property(uint16_t pbap_cid, uint8_t search_property){
1399 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1400 if (pbap_client == NULL){
1401 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1402 }
1403 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1404 return BTSTACK_BUSY;
1405 }
1406 pbap_client->search_property = search_property;
1407 return ERROR_CODE_SUCCESS;
1408 }
1409
pbap_set_search_value(uint16_t pbap_cid,const char * search_value)1410 uint8_t pbap_set_search_value(uint16_t pbap_cid, const char * search_value){
1411 pbap_client_t * pbap_client = pbap_client_for_cid(pbap_cid);
1412 if (pbap_client == NULL){
1413 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1414 }
1415 if (pbap_client->state != PBAP_CLIENT_CONNECTED){
1416 return BTSTACK_BUSY;
1417 }
1418 pbap_client->search_value = search_value;
1419 return ERROR_CODE_SUCCESS;
1420 }
1421
pbap_client_create_sdp_record(uint8_t * service,uint32_t service_record_handle,const char * service_name)1422 void pbap_client_create_sdp_record(uint8_t *service, uint32_t service_record_handle, const char *service_name) {
1423 uint8_t* attribute;
1424 de_create_sequence(service);
1425
1426 // 0x0000 "Service Record Handle"
1427 de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE);
1428 de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle);
1429
1430 // 0x0001 "Service Class ID List"
1431 de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST);
1432 attribute = de_push_sequence(service);
1433 {
1434 de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS_PCE);
1435 }
1436 de_pop_sequence(service, attribute);
1437
1438 // 0x0009 "Bluetooth Profile Descriptor List"
1439 de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
1440 attribute = de_push_sequence(service);
1441 {
1442 uint8_t *pbapServerProfile = de_push_sequence(attribute);
1443 {
1444 de_add_number(pbapServerProfile, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS);
1445 de_add_number(pbapServerProfile, DE_UINT, DE_SIZE_16, 0x0102); // Verision 1.2
1446 }
1447 de_pop_sequence(attribute, pbapServerProfile);
1448 }
1449 de_pop_sequence(service, attribute);
1450
1451 // 0x0100 "Service Name"
1452 de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100);
1453 de_add_data(service, DE_STRING, (uint16_t) strlen(service_name), (uint8_t *) service_name);
1454 }