pbap_client.c (7dad9ff837d8c18e8c723011bb5dcf58a785baaa) pbap_client.c (3a7447a300fd649fa06763a9ebdb11596972740a)
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

--- 79 unchanged lines hidden (view full) ---

88typedef 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 //
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

--- 79 unchanged lines hidden (view full) ---

88typedef 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_PHONE_BOOK,
97 PBAP_W4_PHONE_BOOK,
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,
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,
102} pbap_state_t;
103
104typedef struct pbap_client {
105 pbap_state_t state;
106 uint16_t cid;
107 bd_addr_t bd_addr;
108 hci_con_handle_t con_handle;
109 uint8_t incoming;
110 uint16_t goep_cid;
111 btstack_packet_handler_t client_handler;
112 const char * current_folder;
113 uint16_t set_path_offset;
114} pbap_client_t;
115
116static pbap_client_t _pbap_client;
117static pbap_client_t * pbap_client = &_pbap_client;
118
104} pbap_state_t;
105
106typedef 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
118static pbap_client_t _pbap_client;
119static pbap_client_t * pbap_client = &_pbap_client;
120
119static inline void pbap_client_emit_connected_event(pbap_client_t * context, uint8_t status){
121static void pbap_client_emit_connected_event(pbap_client_t * context, uint8_t status){
120 uint8_t event[15];
121 int pos = 0;
122 event[pos++] = HCI_EVENT_PBAP_META;
123 pos++; // skip len
124 event[pos++] = PBAP_SUBEVENT_CONNECTION_OPENED;
125 little_endian_store_16(event,pos,context->cid);
126 pos+=2;
127 event[pos++] = status;
128 memcpy(&event[pos], context->bd_addr, 6);
129 pos += 6;
130 little_endian_store_16(event,pos,context->con_handle);
131 pos += 2;
132 event[pos++] = context->incoming;
133 event[1] = pos - 2;
134 if (pos != sizeof(event)) log_error("goep_client_emit_connected_event size %u", pos);
135 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
136}
137
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
138static inline void pbap_client_emit_connection_closed_event(pbap_client_t * context){
140static void pbap_client_emit_connection_closed_event(pbap_client_t * context){
139 uint8_t event[5];
140 int pos = 0;
141 event[pos++] = HCI_EVENT_PBAP_META;
142 pos++; // skip len
143 event[pos++] = PBAP_SUBEVENT_CONNECTION_CLOSED;
144 little_endian_store_16(event,pos,context->cid);
145 pos+=2;
146 event[1] = pos - 2;
147 if (pos != sizeof(event)) log_error("pbap_client_emit_connection_closed_event size %u", pos);
148 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
149}
150
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
151static inline void pbap_client_emit_operation_complete_event(pbap_client_t * context, uint8_t status){
153static void pbap_client_emit_operation_complete_event(pbap_client_t * context, uint8_t status){
152 uint8_t event[6];
153 int pos = 0;
154 event[pos++] = HCI_EVENT_PBAP_META;
155 pos++; // skip len
156 event[pos++] = PBAP_SUBEVENT_OPERATION_COMPLETED;
157 little_endian_store_16(event,pos,context->cid);
158 pos+=2;
159 event[pos++]= status;
160 event[1] = pos - 2;
161 if (pos != sizeof(event)) log_error("pbap_client_emit_can_send_now_event size %u", pos);
162 context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
163}
164
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
167static 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
165static void pbap_handle_can_send_now(void){
166 uint8_t path_element[20];
167 uint16_t path_element_start;
168 uint16_t path_element_len;
183static 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];
169
170 switch (pbap_client->state){
171 case PBAP_W2_SEND_CONNECT_REQUEST:
172 goep_client_create_connect_request(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT);
173 goep_client_add_header_target(pbap_client->goep_cid, 16, pbap_uuid);
174 // state
175 pbap_client->state = PBAP_W4_CONNECT_RESPONSE;
176 // send packet
177 goep_client_execute(pbap_client->goep_cid);
178 return;
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;
179 case PBAP_W2_PULL_PHONE_BOOK:
198 case PBAP_W2_PULL_PHONEBOOK:
199 case PBAP_W2_GET_PHONEBOOK_SIZE:
180 goep_client_create_get_request(pbap_client->goep_cid);
181 goep_client_add_header_type(pbap_client->goep_cid, pbap_type);
182 goep_client_add_header_name(pbap_client->goep_cid, pbap_name);
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);
183 // state
184 pbap_client->state = PBAP_W4_PHONE_BOOK;
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 }
185 // send packet
186 goep_client_execute(pbap_client->goep_cid);
187 break;
188 case PBAP_W2_SET_PATH_ROOT:
189 goep_client_create_set_path_request(pbap_client->goep_cid, 1 << 1); // Don’t create directory
190 // On Android 4.2 Cyanogenmod, using "" as path fails
191 // goep_client_add_header_name(pbap_client->goep_cid, ""); // empty == /
192 // state

--- 106 unchanged lines hidden (view full) ---

299 } else if (packet[0] == OBEX_RESP_NOT_FOUND){
300 pbap_client->state = PBAP_CONNECTED;
301 pbap_client_emit_operation_complete_event(pbap_client, OBEX_NOT_FOUND);
302 } else {
303 pbap_client->state = PBAP_CONNECTED;
304 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR);
305 }
306 break;
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

--- 106 unchanged lines hidden (view full) ---

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;
307 case PBAP_W4_PHONE_BOOK:
337 case PBAP_W4_PHONEBOOK:
308 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)){
309 uint8_t hi = obex_iterator_get_hi(&it);
310 if (hi == OBEX_HEADER_BODY || hi == OBEX_HEADER_END_OF_BODY){
311 uint16_t data_len = obex_iterator_get_data_len(&it);
312 const uint8_t * data = obex_iterator_get_data(&it);
313 pbap_client->client_handler(PBAP_DATA_PACKET, pbap_client->cid, (uint8_t *) data, data_len);
314 }
315 }
316 if (packet[0] == OBEX_RESP_CONTINUE){
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){
317 pbap_client->state = PBAP_W2_PULL_PHONE_BOOK;
347 pbap_client->state = PBAP_W2_PULL_PHONEBOOK;
318 goep_client_request_can_send_now(pbap_client->goep_cid);
319 } else if (packet[0] == OBEX_RESP_SUCCESS){
320 pbap_client->state = PBAP_CONNECTED;
321 pbap_client_emit_operation_complete_event(pbap_client, 0);
322 } else {
323 pbap_client->state = PBAP_CONNECTED;
324 pbap_client_emit_operation_complete_event(pbap_client, OBEX_UNKNOWN_ERROR);
325 }
326 break;
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, 0);
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;
327 default:
328 break;
329 }
330 break;
331 default:
332 break;
333 }
334}

--- 16 unchanged lines hidden (view full) ---

351
352uint8_t pbap_disconnect(uint16_t pbap_cid){
353 UNUSED(pbap_cid);
354 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
355 goep_client_disconnect(pbap_client->goep_cid);
356 return 0;
357}
358
387 default:
388 break;
389 }
390 break;
391 default:
392 break;
393 }
394}

--- 16 unchanged lines hidden (view full) ---

411
412uint8_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
419uint8_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
359uint8_t pbap_pull_phonebook(uint16_t pbap_cid){
360 UNUSED(pbap_cid);
361 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
427uint8_t pbap_pull_phonebook(uint16_t pbap_cid){
428 UNUSED(pbap_cid);
429 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
362 pbap_client->state = PBAP_W2_PULL_PHONE_BOOK;
363 goep_client_request_can_send_now(pbap_client->goep_cid);
430 pbap_client->state = PBAP_W2_PULL_PHONEBOOK;
431 goep_client_request_can_send_now(pbap_client->goep_cid);
364 return 0;
365}
366
367uint8_t pbap_set_phonebook(uint16_t pbap_cid, const char * path){
368 UNUSED(pbap_cid);
369 if (pbap_client->state != PBAP_CONNECTED) return BTSTACK_BUSY;
370 pbap_client->state = PBAP_W2_SET_PATH_ROOT;
371 pbap_client->current_folder = path;
372 pbap_client->set_path_offset = 0;
432 return 0;
433}
434
435uint8_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;
373 goep_client_request_can_send_now(pbap_client->goep_cid);
441 goep_client_request_can_send_now(pbap_client->goep_cid);
374 return 0;
375}
442 return 0;
443}