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__ "ancs_client.c" 39 40 #include "btstack_config.h" 41 42 #include <stdint.h> 43 #include <string.h> 44 45 #include "ble/gatt-service/ancs_client.h" 46 47 #include "ble/core.h" 48 #include "ble/gatt_client.h" 49 #include "ble/sm.h" 50 #include "btstack_debug.h" 51 #include "btstack_event.h" 52 #include "gap.h" 53 54 // ancs_client.h Start 55 typedef enum ancs_chunk_parser_state { 56 W4_ATTRIBUTE_ID, 57 W4_ATTRIBUTE_LEN, 58 W4_ATTRIBUTE_COMPLETE, 59 } ancs_chunk_parser_state_t; 60 61 typedef enum { 62 TC_IDLE, 63 TC_W4_ENCRYPTED_CONNECTION, 64 TC_W2_QUERY_SERVICE, 65 TC_W4_SERVICE_RESULT, 66 TC_W2_QUERY_CARACTERISTIC, 67 TC_W4_CHARACTERISTIC_RESULT, 68 TC_W2_SUBSCRIBE_DATA_SOURCE, 69 TC_W4_DATA_SOURCE_SUBSCRIBED, 70 TC_W2_ENABLE_NOTIFICATION, 71 TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED, 72 TC_SUBSCRIBED, 73 TC_W4_DISCONNECT 74 } tc_state_t; 75 76 static uint32_t ancs_notification_uid; 77 static hci_con_handle_t gc_handle; 78 static gatt_client_notification_t ancs_notification_source_notification; 79 static gatt_client_notification_t ancs_data_source_notification; 80 static int ancs_service_found; 81 static gatt_client_service_t ancs_service; 82 static gatt_client_characteristic_t ancs_notification_source_characteristic; 83 static gatt_client_characteristic_t ancs_control_point_characteristic; 84 static gatt_client_characteristic_t ancs_data_source_characteristic; 85 static int ancs_characteristcs; 86 static tc_state_t tc_state = TC_IDLE; 87 88 static ancs_chunk_parser_state_t chunk_parser_state; 89 static uint8_t ancs_notification_buffer[50]; 90 static uint16_t ancs_bytes_received; 91 static uint16_t ancs_bytes_needed; 92 static uint8_t ancs_attribute_id; 93 static uint16_t ancs_attribute_len; 94 95 static btstack_packet_handler_t client_handler; 96 static btstack_packet_callback_registration_t hci_event_callback_registration; 97 static btstack_context_callback_registration_t ancs_client_handle_can_send_now; 98 99 static void ancs_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 100 101 void ancs_client_register_callback(btstack_packet_handler_t handler){ 102 client_handler = handler; 103 } 104 105 static uint8_t ancs_client_request_send_gatt_query(void){ 106 uint8_t status = gatt_client_request_to_send_gatt_query(&ancs_client_handle_can_send_now, gc_handle); 107 if (status != ERROR_CODE_SUCCESS){ 108 tc_state = TC_IDLE; 109 gc_handle = HCI_CON_HANDLE_INVALID; 110 } 111 return status; 112 } 113 114 static void notify_client_text(int event_type){ 115 if (!client_handler) return; 116 uint8_t event[7 + sizeof(ancs_notification_buffer) + 1]; 117 event[0] = HCI_EVENT_ANCS_META; 118 event[1] = 5u + ancs_attribute_len; 119 event[2] = event_type; 120 little_endian_store_16(event, 3, gc_handle); 121 little_endian_store_16(event, 5, ancs_attribute_id); 122 (void)memcpy(&event[7], ancs_notification_buffer, ancs_attribute_len); 123 // we're nice 124 event[7u+ancs_attribute_len] = 0u; 125 (*client_handler)(HCI_EVENT_PACKET, 0u, event, event[1u] + 2u); 126 } 127 128 static void notify_client_simple(int event_type){ 129 if (!client_handler) return; 130 uint8_t event[5]; 131 event[0] = HCI_EVENT_ANCS_META; 132 event[1] = 3; 133 event[2] = event_type; 134 little_endian_store_16(event, 3, gc_handle); 135 (*client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 136 } 137 138 static void ancs_chunk_parser_init(void){ 139 // skip comand id and notification uid 140 chunk_parser_state = W4_ATTRIBUTE_ID; 141 ancs_bytes_received = 0; 142 ancs_bytes_needed = 6; 143 } 144 145 const char * ancs_client_attribute_name_for_id(int id){ 146 static const char * ancs_attribute_names[] = { 147 "AppIdentifier", 148 "IDTitle", 149 "IDSubtitle", 150 "IDMessage", 151 "IDMessageSize", 152 "IDDate" 153 }; 154 155 static const uint16_t ANCS_ATTRIBUTE_NAMES_COUNT = sizeof(ancs_attribute_names) / sizeof(char *); 156 157 if (id >= ANCS_ATTRIBUTE_NAMES_COUNT) return NULL; 158 return ancs_attribute_names[id]; 159 } 160 161 static void ancs_chunk_parser_handle_byte(uint8_t data){ 162 ancs_notification_buffer[ancs_bytes_received++] = data; 163 if (ancs_bytes_received < ancs_bytes_needed) return; 164 switch (chunk_parser_state){ 165 case W4_ATTRIBUTE_ID: 166 ancs_attribute_id = ancs_notification_buffer[ancs_bytes_received-1u]; 167 ancs_bytes_received = 0; 168 ancs_bytes_needed = 2; 169 chunk_parser_state = W4_ATTRIBUTE_LEN; 170 break; 171 case W4_ATTRIBUTE_LEN: 172 ancs_attribute_len = little_endian_read_16(ancs_notification_buffer, ancs_bytes_received-2u); 173 ancs_bytes_received = 0; 174 ancs_bytes_needed = ancs_attribute_len; 175 if (ancs_attribute_len == 0u) { 176 ancs_bytes_needed = 1; 177 chunk_parser_state = W4_ATTRIBUTE_ID; 178 break; 179 } 180 chunk_parser_state = W4_ATTRIBUTE_COMPLETE; 181 break; 182 case W4_ATTRIBUTE_COMPLETE: 183 ancs_notification_buffer[ancs_bytes_received] = 0; 184 notify_client_text(ANCS_SUBEVENT_CLIENT_NOTIFICATION); 185 ancs_bytes_received = 0; 186 ancs_bytes_needed = 1; 187 chunk_parser_state = W4_ATTRIBUTE_ID; 188 break; 189 default: 190 btstack_unreachable(); 191 break; 192 } 193 } 194 195 static void ancs_client_handle_notification(uint16_t value_handle, const uint8_t * value, uint16_t value_length){ 196 197 log_info("ANCS Notification, value handle %u", value_handle); 198 199 if (value_handle == ancs_data_source_characteristic.value_handle){ 200 int i; 201 for (i=0;i<value_length;i++) { 202 ancs_chunk_parser_handle_byte(value[i]); 203 } 204 } else if (value_handle == ancs_notification_source_characteristic.value_handle){ 205 ancs_notification_uid = little_endian_read_32(value, 4); 206 log_info("Notification received: EventID %02x, EventFlags %02x, CategoryID %02x, CategoryCount %u, UID %04x", 207 value[0], value[1], value[2], value[3], (int) ancs_notification_uid); 208 static uint8_t get_notification_attributes[] = {0, 0,0,0,0, 0, 1,32,0, 2,32,0, 3,32,0, 4, 5}; 209 little_endian_store_32(get_notification_attributes, 1, ancs_notification_uid); 210 ancs_notification_uid = 0; 211 ancs_chunk_parser_init(); 212 gatt_client_write_value_of_characteristic(ancs_client_handle_gatt_client_event, gc_handle, ancs_control_point_characteristic.value_handle, 213 sizeof(get_notification_attributes), get_notification_attributes); 214 } else { 215 log_info("Unknown Source: "); 216 log_info_hexdump(value , value_length); 217 } 218 } 219 220 static void ancs_client_send_next_query(void * context){ 221 UNUSED(context); 222 static const uint8_t ancs_service_uuid[] = {0x79,0x05,0xF4,0x31,0xB5,0xCE,0x4E,0x99,0xA4,0x0F,0x4B,0x1E,0x12,0x2D,0x00,0xD0}; 223 224 switch(tc_state){ 225 case TC_W2_QUERY_SERVICE: 226 tc_state = TC_W4_SERVICE_RESULT; 227 (void) gatt_client_discover_primary_services_by_uuid128(ancs_client_handle_gatt_client_event, gc_handle, ancs_service_uuid); 228 break; 229 230 case TC_W2_QUERY_CARACTERISTIC: 231 tc_state = TC_W4_CHARACTERISTIC_RESULT; 232 log_info("ANCS Client - Discover characteristics for ANCS SERVICE "); 233 gatt_client_discover_characteristics_for_service(ancs_client_handle_gatt_client_event, gc_handle, &ancs_service); 234 break; 235 236 case TC_W2_ENABLE_NOTIFICATION: 237 tc_state = TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED; 238 gatt_client_listen_for_characteristic_value_updates(&ancs_notification_source_notification, &ancs_client_handle_gatt_client_event, gc_handle, &ancs_notification_source_characteristic); 239 gatt_client_write_client_characteristic_configuration(ancs_client_handle_gatt_client_event, gc_handle, &ancs_notification_source_characteristic, 240 GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 241 break; 242 243 case TC_W2_SUBSCRIBE_DATA_SOURCE: 244 tc_state = TC_W4_DATA_SOURCE_SUBSCRIBED; 245 gatt_client_listen_for_characteristic_value_updates(&ancs_data_source_notification, &ancs_client_handle_gatt_client_event, gc_handle, &ancs_data_source_characteristic); 246 gatt_client_write_client_characteristic_configuration(ancs_client_handle_gatt_client_event, gc_handle, &ancs_data_source_characteristic, 247 GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 248 break; 249 250 default: 251 break; 252 } 253 } 254 255 static void ancs_client_handle_gatt_client_event_in_w4_service_result(uint8_t* packet) { 256 switch(hci_event_packet_get_type(packet)){ 257 case GATT_EVENT_SERVICE_QUERY_RESULT: 258 gatt_event_service_query_result_get_service(packet, &ancs_service); 259 ancs_service_found = 1; 260 break; 261 case GATT_EVENT_QUERY_COMPLETE: 262 if (!ancs_service_found){ 263 log_info("ANCS Service not found"); 264 tc_state = TC_IDLE; 265 break; 266 } 267 tc_state = TC_W2_QUERY_CARACTERISTIC; 268 break; 269 default: 270 break; 271 } 272 } 273 274 static void ancs_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 275 276 UNUSED(packet_type); 277 UNUSED(channel); 278 UNUSED(size); 279 280 static const uint8_t ancs_notification_source_uuid[] = {0x9F,0xBF,0x12,0x0D,0x63,0x01,0x42,0xD9,0x8C,0x58,0x25,0xE6,0x99,0xA2,0x1D,0xBD}; 281 static const uint8_t ancs_control_point_uuid[] = {0x69,0xD1,0xD8,0xF3,0x45,0xE1,0x49,0xA8,0x98,0x21,0x9B,0xBD,0xFD,0xAA,0xD9,0xD9}; 282 static const uint8_t ancs_data_source_uuid[] = {0x22,0xEA,0xC6,0xE9,0x24,0xD6,0x4B,0xB5,0xBE,0x44,0xB3,0x6A,0xCE,0x7C,0x7B,0xFB}; 283 284 gatt_client_characteristic_t characteristic; 285 286 switch(tc_state){ 287 case TC_W4_SERVICE_RESULT: 288 ancs_client_handle_gatt_client_event_in_w4_service_result(packet); 289 break; 290 291 case TC_W4_CHARACTERISTIC_RESULT: 292 switch(hci_event_packet_get_type(packet)){ 293 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 294 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 295 if (memcmp(characteristic.uuid128, ancs_notification_source_uuid, 16) == 0){ 296 log_info("ANCS Notification Source found, attribute handle %u", characteristic.value_handle); 297 ancs_notification_source_characteristic = characteristic; 298 ancs_characteristcs++; 299 break; 300 } 301 if (memcmp(characteristic.uuid128, ancs_control_point_uuid, 16) == 0){ 302 log_info("ANCS Control Point found, attribute handle %u", characteristic.value_handle); 303 ancs_control_point_characteristic = characteristic; 304 ancs_characteristcs++; 305 break; 306 } 307 if (memcmp(characteristic.uuid128, ancs_data_source_uuid, 16) == 0){ 308 log_info("ANCS Data Source found, attribute handle %u", characteristic.value_handle); 309 ancs_data_source_characteristic = characteristic; 310 ancs_characteristcs++; 311 break; 312 } 313 break; 314 case GATT_EVENT_QUERY_COMPLETE: 315 log_info("ANCS Characteristcs count %u", ancs_characteristcs); 316 tc_state = TC_W2_ENABLE_NOTIFICATION; 317 break; 318 default: 319 break; 320 } 321 break; 322 323 case TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED: 324 switch(hci_event_packet_get_type(packet)){ 325 case GATT_EVENT_QUERY_COMPLETE: 326 log_info("ANCS Notification Source subscribed"); 327 tc_state = TC_W2_SUBSCRIBE_DATA_SOURCE; 328 329 break; 330 default: 331 break; 332 } 333 break; 334 335 case TC_W4_DATA_SOURCE_SUBSCRIBED: 336 switch(hci_event_packet_get_type(packet)){ 337 case GATT_EVENT_QUERY_COMPLETE: 338 log_info("ANCS Data Source subscribed"); 339 tc_state = TC_SUBSCRIBED; 340 notify_client_simple(ANCS_SUBEVENT_CLIENT_CONNECTED); 341 break; 342 default: 343 break; 344 } 345 break; 346 347 case TC_SUBSCRIBED: 348 switch(hci_event_packet_get_type(packet)){ 349 case GATT_EVENT_NOTIFICATION: 350 ancs_client_handle_notification( 351 gatt_event_notification_get_value_handle(packet), 352 gatt_event_notification_get_value(packet), 353 gatt_event_notification_get_value_length(packet) 354 ); 355 break; 356 case GATT_EVENT_INDICATION: 357 ancs_client_handle_notification( 358 gatt_event_indication_get_value_handle(packet), 359 gatt_event_indication_get_value(packet), 360 gatt_event_indication_get_value_length(packet) 361 ); 362 break; 363 default: 364 break; 365 } 366 break; 367 368 default: 369 break; 370 } 371 372 uint8_t status; 373 switch(tc_state){ 374 case TC_W2_QUERY_SERVICE: 375 case TC_W2_QUERY_CARACTERISTIC: 376 case TC_W2_ENABLE_NOTIFICATION: 377 case TC_W2_SUBSCRIBE_DATA_SOURCE: 378 status = gatt_client_request_to_send_gatt_query(&ancs_client_handle_can_send_now, gc_handle); 379 if (status != ERROR_CODE_SUCCESS){ 380 notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED); 381 } 382 break; 383 384 default: 385 break; 386 } 387 } 388 389 static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 390 391 UNUSED(packet_type); // ok: only hci events 392 UNUSED(channel); // ok: there is no channel 393 UNUSED(size); // ok: fixed format events read from HCI buffer 394 395 int connection_encrypted; 396 uint8_t status; 397 398 // handle connect / disconncet events first 399 switch (hci_event_packet_get_type(packet)) { 400 case HCI_EVENT_META_GAP: 401 switch (hci_event_gap_meta_get_subevent_code(packet)) { 402 case GAP_SUBEVENT_LE_CONNECTION_COMPLETE: 403 gc_handle = gap_subevent_le_connection_complete_get_connection_handle(packet); 404 log_info("Connection handle 0x%04x, request encryption", gc_handle); 405 406 // we need to be paired to enable notifications 407 tc_state = TC_W4_ENCRYPTED_CONNECTION; 408 ancs_service_found = false; 409 sm_request_pairing(gc_handle); 410 break; 411 default: 412 break; 413 } 414 return; 415 416 case HCI_EVENT_ENCRYPTION_CHANGE: 417 case HCI_EVENT_ENCRYPTION_CHANGE_V2: 418 if (gc_handle != hci_event_encryption_change_get_connection_handle(packet)) return; 419 connection_encrypted = hci_event_encryption_change_get_encryption_enabled(packet); 420 log_info("Encryption state change: %u", connection_encrypted); 421 if (!connection_encrypted) return; 422 if (tc_state != TC_W4_ENCRYPTED_CONNECTION) return; 423 424 // let's start 425 log_info("\nANCS Client - CONNECTED, discover ANCS service"); 426 tc_state = TC_W2_QUERY_SERVICE; 427 status = ancs_client_request_send_gatt_query(); 428 if (status != ERROR_CODE_SUCCESS){ 429 notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED); 430 } 431 return; 432 433 case HCI_EVENT_DISCONNECTION_COMPLETE: 434 if (hci_event_disconnection_complete_get_connection_handle(packet) != gc_handle) break; 435 if (tc_state == TC_SUBSCRIBED){ 436 notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED); 437 } 438 tc_state = TC_IDLE; 439 gc_handle = HCI_CON_HANDLE_INVALID; 440 return; 441 442 default: 443 break; 444 } 445 } 446 447 void ancs_client_init(void){ 448 hci_event_callback_registration.callback = &handle_hci_event; 449 hci_add_event_handler(&hci_event_callback_registration); 450 ancs_client_handle_can_send_now.callback = &ancs_client_send_next_query; 451 } 452 453 // unit test only 454 #if defined __cplusplus 455 extern "C" 456 #endif 457 void ancs_client_set_invalid_parser_state(void); 458 void ancs_client_set_invalid_parser_state(void){ 459 chunk_parser_state = (ancs_chunk_parser_state_t) 0x17; 460 } 461