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 // ***************************************************************************** 41 // 42 #if 0 43 0x0000 = uint32(65542), 44 // BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS_PSE 45 0x0001 = { uuid16(11 2f) }, 46 // BLUETOOTH_PROTOCOL_L2CAP, BLUETOOTH_PROTOCOL_RFCOMM, BLUETOOTH_PROTOCOL_OBEX 47 0x0004 = { { uuid16(01 00) }, { uuid16(00 03), uint8(19) }, { uuid16(00 08) } } 48 0x0005 = { uuid16(10 02) }, 49 // BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS, v1.01 = 0x101 50 0x0009 = { { uuid16(11 30), uint16(257) } }, 51 0x0100 = string(OBEX Phonebook Access Server 52 // BLUETOOTH_ATTRIBUTE_SUPPORTED_FEATURES -- should be 0x317 BLUETOOTH_ATTRIBUTE_PBAP_SUPPORTED_FEATURES? 53 0x0311 = uint8(3), 54 // BLUETOOTH_ATTRIBUTE_SUPPORTED_REPOSITORIES 55 0x0314 = uint8(1), 56 #endif 57 // 58 // ***************************************************************************** 59 60 #include "btstack_config.h" 61 62 #include <stdint.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 67 #include "hci_cmd.h" 68 #include "btstack_run_loop.h" 69 #include "btstack_debug.h" 70 #include "hci.h" 71 #include "btstack_memory.h" 72 #include "hci_dump.h" 73 #include "l2cap.h" 74 #include "bluetooth_sdp.h" 75 #include "classic/sdp_client_rfcomm.h" 76 #include "btstack_event.h" 77 78 #include "classic/obex.h" 79 #include "classic/obex_iterator.h" 80 #include "classic/goep_client.h" 81 #include "classic/pbap_client.h" 82 83 // 796135f0-f0c5-11d8-0966- 0800200c9a66 84 uint8_t pbap_uuid[] = { 0x79, 0x61, 0x35, 0xf0, 0xf0, 0xc5, 0x11, 0xd8, 0x09, 0x66, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66}; 85 const char * pbap_type = "x-bt/phonebook"; 86 const char * pbap_name = "pb.vcf"; 87 88 typedef enum { 89 PBAP_INIT = 0, 90 PBAP_W4_GOEP_CONNECTION, 91 PBAP_W2_SEND_CONNECT_REQUEST, 92 PBAP_W4_CONNECT_RESPONSE, 93 PBAP_CONNECT_RESPONSE_RECEIVED, 94 PBAP_CONNECTED, 95 // 96 PBAP_W2_PULL_PHONEBOOK, 97 PBAP_W4_PHONEBOOK, 98 PBAP_W2_SET_PATH_ROOT, 99 PBAP_W4_SET_PATH_ROOT_COMPLETE, 100 PBAP_W2_SET_PATH_ELEMENT, 101 PBAP_W4_SET_PATH_ELEMENT_COMPLETE, 102 PBAP_W2_GET_PHONEBOOK_SIZE, 103 PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE, 104 } pbap_state_t; 105 106 typedef struct pbap_client { 107 pbap_state_t state; 108 uint16_t cid; 109 bd_addr_t bd_addr; 110 hci_con_handle_t con_handle; 111 uint8_t incoming; 112 uint16_t goep_cid; 113 btstack_packet_handler_t client_handler; 114 const char * current_folder; 115 uint16_t set_path_offset; 116 } pbap_client_t; 117 118 static pbap_client_t _pbap_client; 119 static pbap_client_t * pbap_client = &_pbap_client; 120 121 static void pbap_client_emit_connected_event(pbap_client_t * context, uint8_t status){ 122 uint8_t event[15]; 123 int pos = 0; 124 event[pos++] = HCI_EVENT_PBAP_META; 125 pos++; // skip len 126 event[pos++] = PBAP_SUBEVENT_CONNECTION_OPENED; 127 little_endian_store_16(event,pos,context->cid); 128 pos+=2; 129 event[pos++] = status; 130 memcpy(&event[pos], context->bd_addr, 6); 131 pos += 6; 132 little_endian_store_16(event,pos,context->con_handle); 133 pos += 2; 134 event[pos++] = context->incoming; 135 event[1] = pos - 2; 136 if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos); 137 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 138 } 139 140 static void pbap_client_emit_connection_closed_event(pbap_client_t * context){ 141 uint8_t event[5]; 142 int pos = 0; 143 event[pos++] = HCI_EVENT_PBAP_META; 144 pos++; // skip len 145 event[pos++] = PBAP_SUBEVENT_CONNECTION_CLOSED; 146 little_endian_store_16(event,pos,context->cid); 147 pos+=2; 148 event[1] = pos - 2; 149 if (pos != sizeof(event)) log_error("pbap_client_emit_connection_closed_event size %u", pos); 150 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 151 } 152 153 static void pbap_client_emit_operation_complete_event(pbap_client_t * context, uint8_t status){ 154 uint8_t event[6]; 155 int pos = 0; 156 event[pos++] = HCI_EVENT_PBAP_META; 157 pos++; // skip len 158 event[pos++] = PBAP_SUBEVENT_OPERATION_COMPLETED; 159 little_endian_store_16(event,pos,context->cid); 160 pos+=2; 161 event[pos++]= status; 162 event[1] = pos - 2; 163 if (pos != sizeof(event)) log_error("pbap_client_emit_can_send_now_event size %u", pos); 164 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 165 } 166 167 static void pbap_client_emit_phonebook_size_event(pbap_client_t * context, uint8_t status, uint16_t phonebook_size){ 168 uint8_t event[8]; 169 int pos = 0; 170 event[pos++] = HCI_EVENT_PBAP_META; 171 pos++; // skip len 172 event[pos++] = PBAP_SUBEVENT_PHONEBOOK_SIZE; 173 little_endian_store_16(event,pos,context->cid); 174 pos+=2; 175 event[pos++] = status; 176 little_endian_store_16(event,pos, phonebook_size); 177 pos+=2; 178 event[1] = pos - 2; 179 if (pos != sizeof(event)) log_error("pbap_client_emit_phonebook_size_event size %u", pos); 180 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos); 181 } 182 183 static void pbap_handle_can_send_now(void){ 184 uint8_t path_element[20]; 185 uint16_t path_element_start; 186 uint16_t path_element_len; 187 uint8_t application_parameters[20]; 188 189 switch (pbap_client->state){ 190 case PBAP_W2_SEND_CONNECT_REQUEST: 191 goep_client_create_connect_request(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT); 192 goep_client_add_header_target(pbap_client->goep_cid, 16, pbap_uuid); 193 // state 194 pbap_client->state = PBAP_W4_CONNECT_RESPONSE; 195 // send packet 196 goep_client_execute(pbap_client->goep_cid); 197 return; 198 case PBAP_W2_PULL_PHONEBOOK: 199 case PBAP_W2_GET_PHONEBOOK_SIZE: 200 goep_client_create_get_request(pbap_client->goep_cid); 201 goep_client_add_header_type(pbap_client->goep_cid, pbap_type); 202 goep_client_add_header_name(pbap_client->goep_cid, pbap_name); 203 if (pbap_client->state == PBAP_W2_GET_PHONEBOOK_SIZE){ 204 // Regular TLV wih 1-byte len 205 application_parameters[0] = PBAP_APPLICATION_PARAMETER_MAX_LIST_COUNT; 206 application_parameters[1] = 2; 207 big_endian_store_16(application_parameters, 2, 0); 208 goep_client_add_header_application_parameters(pbap_client->goep_cid, 4, &application_parameters[0]); 209 // state 210 pbap_client->state = PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE; 211 } else { 212 // state 213 pbap_client->state = PBAP_W4_PHONEBOOK; 214 } 215 // send packet 216 goep_client_execute(pbap_client->goep_cid); 217 break; 218 case PBAP_W2_SET_PATH_ROOT: 219 goep_client_create_set_path_request(pbap_client->goep_cid, 1 << 1); // Don’t create directory 220 // On Android 4.2 Cyanogenmod, using "" as path fails 221 // goep_client_add_header_name(pbap_client->goep_cid, ""); // empty == / 222 // state 223 pbap_client->state = PBAP_W4_SET_PATH_ROOT_COMPLETE; 224 // send packet 225 goep_client_execute(pbap_client->goep_cid); 226 break; 227 case PBAP_W2_SET_PATH_ELEMENT: 228 // find '/' or '\0' 229 path_element_start = pbap_client->set_path_offset; 230 while (pbap_client->current_folder[pbap_client->set_path_offset] != '\0' && 231 pbap_client->current_folder[pbap_client->set_path_offset] != '/'){ 232 pbap_client->set_path_offset++; 233 } 234 // skip / 235 if (pbap_client->current_folder[pbap_client->set_path_offset] == '/'){ 236 pbap_client->set_path_offset++; 237 } 238 path_element_len = pbap_client->set_path_offset-path_element_start; 239 memcpy(path_element, &pbap_client->current_folder[path_element_start], path_element_len); 240 path_element[path_element_len] = 0; 241 242 goep_client_create_set_path_request(pbap_client->goep_cid, 1 << 1); // Don’t create directory 243 goep_client_add_header_name(pbap_client->goep_cid, (const char *) path_element); // next element 244 // state 245 pbap_client->state = PBAP_W4_SET_PATH_ELEMENT_COMPLETE; 246 // send packet 247 goep_client_execute(pbap_client->goep_cid); 248 break; 249 default: 250 break; 251 } 252 } 253 254 static void pbap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 255 256 UNUSED(channel); // ok: there is no channel 257 UNUSED(size); // ok: handling own geop events 258 259 obex_iterator_t it; 260 uint8_t status; 261 switch (packet_type){ 262 case HCI_EVENT_PACKET: 263 switch (hci_event_packet_get_type(packet)) { 264 case HCI_EVENT_GOEP_META: 265 switch (hci_event_goep_meta_get_subevent_code(packet)){ 266 case GOEP_SUBEVENT_CONNECTION_OPENED: 267 status = goep_subevent_connection_opened_get_status(packet); 268 pbap_client->con_handle = goep_subevent_connection_opened_get_con_handle(packet); 269 pbap_client->incoming = goep_subevent_connection_opened_get_incoming(packet); 270 goep_subevent_connection_opened_get_bd_addr(packet, pbap_client->bd_addr); 271 if (status){ 272 log_info("pbap: connection failed %u", status); 273 pbap_client->state = PBAP_INIT; 274 pbap_client_emit_connected_event(pbap_client, status); 275 } else { 276 log_info("pbap: connection established"); 277 pbap_client->goep_cid = goep_subevent_connection_opened_get_goep_cid(packet); 278 pbap_client->state = PBAP_W2_SEND_CONNECT_REQUEST; 279 goep_client_request_can_send_now(pbap_client->goep_cid); 280 } 281 break; 282 case GOEP_SUBEVENT_CONNECTION_CLOSED: 283 if (pbap_client->state != PBAP_CONNECTED){ 284 pbap_client_emit_operation_complete_event(pbap_client, OBEX_DISCONNECTED); 285 } 286 pbap_client->state = PBAP_INIT; 287 pbap_client_emit_connection_closed_event(pbap_client); 288 break; 289 case GOEP_SUBEVENT_CAN_SEND_NOW: 290 pbap_handle_can_send_now(); 291 break; 292 } 293 break; 294 default: 295 break; 296 } 297 break; 298 case GOEP_DATA_PACKET: 299 // TODO: handle chunked data 300 #if 0 301 obex_dump_packet(goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); 302 #endif 303 switch (pbap_client->state){ 304 case PBAP_W4_CONNECT_RESPONSE: 305 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)){ 306 uint8_t hi = obex_iterator_get_hi(&it); 307 if (hi == OBEX_HEADER_CONNECTION_ID){ 308 goep_client_set_connection_id(pbap_client->goep_cid, obex_iterator_get_data_32(&it)); 309 } 310 } 311 if (packet[0] == OBEX_RESP_SUCCESS){ 312 pbap_client->state = PBAP_CONNECTED; 313 pbap_client_emit_connected_event(pbap_client, 0); 314 } else { 315 log_info("pbap: obex connect failed, result 0x%02x", packet[0]); 316 pbap_client->state = PBAP_INIT; 317 pbap_client_emit_connected_event(pbap_client, OBEX_CONNECT_FAILED); 318 } 319 break; 320 case PBAP_W4_SET_PATH_ROOT_COMPLETE: 321 case PBAP_W4_SET_PATH_ELEMENT_COMPLETE: 322 if (packet[0] == OBEX_RESP_SUCCESS){ 323 if (pbap_client->current_folder){ 324 pbap_client->state = PBAP_W2_SET_PATH_ELEMENT; 325 goep_client_request_can_send_now(pbap_client->goep_cid); 326 } else { 327 pbap_client_emit_operation_complete_event(pbap_client, 0); 328 } 329 } else if (packet[0] == OBEX_RESP_NOT_FOUND){ 330 pbap_client->state = PBAP_CONNECTED; 331 pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_FOUND); 332 } else { 333 pbap_client->state = PBAP_CONNECTED; 334 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR); 335 } 336 break; 337 case PBAP_W4_PHONEBOOK: 338 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)){ 339 uint8_t hi = obex_iterator_get_hi(&it); 340 if (hi == OBEX_HEADER_BODY || hi == OBEX_HEADER_END_OF_BODY){ 341 uint16_t data_len = obex_iterator_get_data_len(&it); 342 const uint8_t * data = obex_iterator_get_data(&it); 343 pbap_client->client_handler(PBAP_DATA_PACKET, pbap_client->cid, (uint8_t *) data, data_len); 344 } 345 } 346 if (packet[0] == OBEX_RESP_CONTINUE){ 347 pbap_client->state = PBAP_W2_PULL_PHONEBOOK; 348 goep_client_request_can_send_now(pbap_client->goep_cid); 349 } else if (packet[0] == OBEX_RESP_SUCCESS){ 350 pbap_client->state = PBAP_CONNECTED; 351 pbap_client_emit_operation_complete_event(pbap_client, 0); 352 } else { 353 pbap_client->state = PBAP_CONNECTED; 354 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR); 355 } 356 break; 357 case PBAP_W4_GET_PHONEBOOK_SIZE_COMPLETE: 358 pbap_client->state = PBAP_CONNECTED; 359 if (packet[0] == OBEX_RESP_SUCCESS){ 360 int have_size = 0; 361 uint16_t phonebook_size; 362 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)){ 363 uint8_t hi = obex_iterator_get_hi(&it); 364 if (hi == OBEX_HEADER_APPLICATION_PARAMETERS){ 365 uint16_t data_len = obex_iterator_get_data_len(&it); 366 const uint8_t * data = obex_iterator_get_data(&it); 367 // iterate over application headers (TLV with 1 bytes len) 368 unsigned int i = 0; 369 while (i<data_len){ 370 uint8_t tag = data[i++]; 371 uint8_t len = data[i++]; 372 if (tag == PBAP_APPLICATION_PARAMETER_PHONEBOOK_SIZE && len == 2){ 373 have_size = 1; 374 phonebook_size = big_endian_read_16(data, i); 375 } 376 i+=len; 377 } 378 } 379 } 380 if (have_size){ 381 pbap_client_emit_phonebook_size_event(pbap_client, 0, phonebook_size); 382 break; 383 } 384 } 385 pbap_client_emit_phonebook_size_event(pbap_client, OBEX_UNKNOWN_ERROR, 0); 386 break; 387 default: 388 break; 389 } 390 break; 391 default: 392 break; 393 } 394 } 395 396 void pbap_client_init(void){ 397 memset(pbap_client, 0, sizeof(pbap_client_t)); 398 pbap_client->state = PBAP_INIT; 399 pbap_client->cid = 1; 400 } 401 402 uint8_t pbap_connect(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t * out_cid){ 403 if (pbap_client->state != PBAP_INIT) return BTSTACK_MEMORY_ALLOC_FAILED; 404 pbap_client->state = PBAP_W4_GOEP_CONNECTION; 405 pbap_client->client_handler = handler; 406 uint8_t err = goep_client_create_connection(&pbap_packet_handler, addr, BLUETOOTH_SERVICE_CLASS_PHONEBOOK_ACCESS_PSE, &pbap_client->goep_cid); 407 *out_cid = pbap_client->cid; 408 if (err) return err; 409 return 0; 410 } 411 412 uint8_t pbap_disconnect(uint16_t pbap_cid){ 413 UNUSED(pbap_cid); 414 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 415 goep_client_disconnect(pbap_client->goep_cid); 416 return 0; 417 } 418 419 uint8_t pbap_get_phonebook_size(uint16_t pbap_cid){ 420 UNUSED(pbap_cid); 421 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 422 pbap_client->state = PBAP_W2_GET_PHONEBOOK_SIZE; 423 goep_client_request_can_send_now(pbap_client->goep_cid); 424 return 0; 425 } 426 427 uint8_t pbap_pull_phonebook(uint16_t pbap_cid){ 428 UNUSED(pbap_cid); 429 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 430 pbap_client->state = PBAP_W2_PULL_PHONEBOOK; 431 goep_client_request_can_send_now(pbap_client->goep_cid); 432 return 0; 433 } 434 435 uint8_t pbap_set_phonebook(uint16_t pbap_cid, const char * path){ 436 UNUSED(pbap_cid); 437 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY; 438 pbap_client->state = PBAP_W2_SET_PATH_ROOT; 439 pbap_client->current_folder = path; 440 pbap_client->set_path_offset = 0; 441 goep_client_request_can_send_now(pbap_client->goep_cid); 442 return 0; 443 } 444