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__ "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/att_db.h" 48 #include "ble/core.h" 49 #include "ble/gatt_client.h" 50 #include "ble/sm.h" 51 #include "btstack_debug.h" 52 #include "btstack_event.h" 53 #include "btstack_run_loop.h" 54 #include "gap.h" 55 56 // ancs_client.h Start 57 typedef enum ancs_chunk_parser_state { 58 W4_ATTRIBUTE_ID, 59 W4_ATTRIBUTE_LEN, 60 W4_ATTRIBUTE_COMPLETE, 61 } ancs_chunk_parser_state_t; 62 63 typedef enum { 64 TC_IDLE, 65 TC_W4_ENCRYPTED_CONNECTION, 66 TC_W4_SERVICE_RESULT, 67 TC_W4_CHARACTERISTIC_RESULT, 68 TC_W4_DATA_SOURCE_SUBSCRIBED, 69 TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED, 70 TC_SUBSCRIBED, 71 TC_W4_DISCONNECT 72 } tc_state_t; 73 74 static uint32_t ancs_notification_uid; 75 static uint16_t gc_handle; 76 static gatt_client_notification_t ancs_notification_source_notification; 77 static gatt_client_notification_t ancs_data_source_notification; 78 static int ancs_service_found; 79 static gatt_client_service_t ancs_service; 80 static gatt_client_characteristic_t ancs_notification_source_characteristic; 81 static gatt_client_characteristic_t ancs_control_point_characteristic; 82 static gatt_client_characteristic_t ancs_data_source_characteristic; 83 static int ancs_characteristcs; 84 static tc_state_t tc_state = TC_IDLE; 85 86 static ancs_chunk_parser_state_t chunk_parser_state; 87 static uint8_t ancs_notification_buffer[50]; 88 static uint16_t ancs_bytes_received; 89 static uint16_t ancs_bytes_needed; 90 static uint8_t ancs_attribute_id; 91 static uint16_t ancs_attribute_len; 92 93 static btstack_packet_handler_t client_handler; 94 static btstack_packet_callback_registration_t hci_event_callback_registration; 95 96 void ancs_client_register_callback(btstack_packet_handler_t handler){ 97 client_handler = handler; 98 } 99 100 static void notify_client_text(int event_type){ 101 if (!client_handler) return; 102 uint8_t event[7 + sizeof(ancs_notification_buffer) + 1]; 103 event[0] = HCI_EVENT_ANCS_META; 104 event[1] = 5u + ancs_attribute_len; 105 event[2] = event_type; 106 little_endian_store_16(event, 3, gc_handle); 107 little_endian_store_16(event, 5, ancs_attribute_id); 108 (void)memcpy(&event[7], ancs_notification_buffer, ancs_attribute_len); 109 // we're nice 110 event[7u+ancs_attribute_len] = 0u; 111 (*client_handler)(HCI_EVENT_PACKET, 0u, event, event[1u] + 2u); 112 } 113 114 static void notify_client_simple(int event_type){ 115 if (!client_handler) return; 116 uint8_t event[5]; 117 event[0] = HCI_EVENT_ANCS_META; 118 event[1] = 3; 119 event[2] = event_type; 120 little_endian_store_16(event, 3, gc_handle); 121 (*client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 122 } 123 124 static void ancs_chunk_parser_init(void){ 125 chunk_parser_state = W4_ATTRIBUTE_ID; 126 ancs_bytes_received = 0; 127 ancs_bytes_needed = 6; 128 } 129 130 const char * ancs_client_attribute_name_for_id(int id){ 131 static const char * ancs_attribute_names[] = { 132 "AppIdentifier", 133 "IDTitle", 134 "IDSubtitle", 135 "IDMessage", 136 "IDMessageSize", 137 "IDDate" 138 }; 139 140 static const uint16_t ANCS_ATTRBUTE_NAMES_COUNT = sizeof(ancs_attribute_names) / sizeof(char *); 141 142 if (id >= ANCS_ATTRBUTE_NAMES_COUNT) return NULL; 143 return ancs_attribute_names[id]; 144 } 145 146 static void ancs_chunk_parser_handle_byte(uint8_t data){ 147 ancs_notification_buffer[ancs_bytes_received++] = data; 148 if (ancs_bytes_received < ancs_bytes_needed) return; 149 switch (chunk_parser_state){ 150 case W4_ATTRIBUTE_ID: 151 ancs_attribute_id = ancs_notification_buffer[ancs_bytes_received-1u]; 152 ancs_bytes_received = 0; 153 ancs_bytes_needed = 2; 154 chunk_parser_state = W4_ATTRIBUTE_LEN; 155 break; 156 case W4_ATTRIBUTE_LEN: 157 ancs_attribute_len = little_endian_read_16(ancs_notification_buffer, ancs_bytes_received-2u); 158 ancs_bytes_received = 0; 159 ancs_bytes_needed = ancs_attribute_len; 160 if (ancs_attribute_len == 0u) { 161 ancs_bytes_needed = 1; 162 chunk_parser_state = W4_ATTRIBUTE_ID; 163 break; 164 } 165 chunk_parser_state = W4_ATTRIBUTE_COMPLETE; 166 break; 167 case W4_ATTRIBUTE_COMPLETE: 168 ancs_notification_buffer[ancs_bytes_received] = 0; 169 notify_client_text(ANCS_SUBEVENT_CLIENT_NOTIFICATION); 170 ancs_bytes_received = 0; 171 ancs_bytes_needed = 1; 172 chunk_parser_state = W4_ATTRIBUTE_ID; 173 break; 174 default: 175 btstack_assert(false); 176 break; 177 } 178 } 179 180 static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 181 182 UNUSED(packet_type); // ok: only hci events 183 UNUSED(channel); // ok: there is no channel 184 UNUSED(size); // ok: fixed format events read from HCI buffer 185 186 static const uint8_t ancs_service_uuid[] = {0x79,0x05,0xF4,0x31,0xB5,0xCE,0x4E,0x99,0xA4,0x0F,0x4B,0x1E,0x12,0x2D,0x00,0xD0}; 187 static const uint8_t ancs_notification_source_uuid[] = {0x9F,0xBF,0x12,0x0D,0x63,0x01,0x42,0xD9,0x8C,0x58,0x25,0xE6,0x99,0xA2,0x1D,0xBD}; 188 static const uint8_t ancs_control_point_uuid[] = {0x69,0xD1,0xD8,0xF3,0x45,0xE1,0x49,0xA8,0x98,0x21,0x9B,0xBD,0xFD,0xAA,0xD9,0xD9}; 189 static const uint8_t ancs_data_source_uuid[] = {0x22,0xEA,0xC6,0xE9,0x24,0xD6,0x4B,0xB5,0xBE,0x44,0xB3,0x6A,0xCE,0x7C,0x7B,0xFB}; 190 191 192 int connection_encrypted; 193 194 // handle connect / disconncet events first 195 switch (hci_event_packet_get_type(packet)) { 196 case HCI_EVENT_LE_META: 197 switch (packet[2]) { 198 case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: 199 gc_handle = little_endian_read_16(packet, 4); 200 log_info("Connection handle 0x%04x, request encryption", gc_handle); 201 202 // we need to be paired to enable notifications 203 tc_state = TC_W4_ENCRYPTED_CONNECTION; 204 sm_request_pairing(gc_handle); 205 break; 206 default: 207 break; 208 } 209 return; 210 211 case HCI_EVENT_ENCRYPTION_CHANGE: 212 if (gc_handle != little_endian_read_16(packet, 3)) return; 213 connection_encrypted = packet[5]; 214 log_info("Encryption state change: %u", connection_encrypted); 215 if (!connection_encrypted) return; 216 if (tc_state != TC_W4_ENCRYPTED_CONNECTION) return; 217 218 // let's start 219 log_info("\nANCS Client - CONNECTED, discover ANCS service"); 220 tc_state = TC_W4_SERVICE_RESULT; 221 gatt_client_discover_primary_services_by_uuid128(handle_hci_event, gc_handle, ancs_service_uuid); 222 return; 223 224 case HCI_EVENT_DISCONNECTION_COMPLETE: 225 if (hci_event_disconnection_complete_get_connection_handle(packet) != gc_handle) break; 226 if (tc_state == TC_SUBSCRIBED){ 227 notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED); 228 } 229 tc_state = TC_IDLE; 230 gc_handle = 0; 231 return; 232 233 default: 234 break; 235 } 236 237 gatt_client_characteristic_t characteristic; 238 uint8_t * value; 239 uint16_t value_handle; 240 uint16_t value_length; 241 242 switch(tc_state){ 243 case TC_W4_SERVICE_RESULT: 244 switch(hci_event_packet_get_type(packet)){ 245 case GATT_EVENT_SERVICE_QUERY_RESULT: 246 gatt_event_service_query_result_get_service(packet, &ancs_service); 247 ancs_service_found = 1; 248 break; 249 case GATT_EVENT_QUERY_COMPLETE: 250 if (!ancs_service_found){ 251 log_info("ANCS Service not found"); 252 tc_state = TC_IDLE; 253 break; 254 } 255 tc_state = TC_W4_CHARACTERISTIC_RESULT; 256 log_info("ANCS Client - Discover characteristics for ANCS SERVICE "); 257 gatt_client_discover_characteristics_for_service(handle_hci_event, gc_handle, &ancs_service); 258 break; 259 default: 260 break; 261 } 262 break; 263 264 case TC_W4_CHARACTERISTIC_RESULT: 265 switch(hci_event_packet_get_type(packet)){ 266 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 267 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 268 if (memcmp(characteristic.uuid128, ancs_notification_source_uuid, 16) == 0){ 269 log_info("ANCS Notification Source found, attribute handle %u", characteristic.value_handle); 270 ancs_notification_source_characteristic = characteristic; 271 ancs_characteristcs++; 272 break; 273 } 274 if (memcmp(characteristic.uuid128, ancs_control_point_uuid, 16) == 0){ 275 log_info("ANCS Control Point found, attribute handle %u", characteristic.value_handle); 276 ancs_control_point_characteristic = characteristic; 277 ancs_characteristcs++; 278 break; 279 } 280 if (memcmp(characteristic.uuid128, ancs_data_source_uuid, 16) == 0){ 281 log_info("ANCS Data Source found, attribute handle %u", characteristic.value_handle); 282 ancs_data_source_characteristic = characteristic; 283 ancs_characteristcs++; 284 break; 285 } 286 break; 287 case GATT_EVENT_QUERY_COMPLETE: 288 log_info("ANCS Characteristcs count %u", ancs_characteristcs); 289 tc_state = TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED; 290 gatt_client_listen_for_characteristic_value_updates(&ancs_notification_source_notification, &handle_hci_event, gc_handle, &ancs_notification_source_characteristic); 291 gatt_client_write_client_characteristic_configuration(handle_hci_event, gc_handle, &ancs_notification_source_characteristic, 292 GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 293 break; 294 default: 295 break; 296 } 297 break; 298 case TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED: 299 switch(hci_event_packet_get_type(packet)){ 300 case GATT_EVENT_QUERY_COMPLETE: 301 log_info("ANCS Notification Source subscribed"); 302 tc_state = TC_W4_DATA_SOURCE_SUBSCRIBED; 303 gatt_client_listen_for_characteristic_value_updates(&ancs_data_source_notification, &handle_hci_event, gc_handle, &ancs_data_source_characteristic); 304 gatt_client_write_client_characteristic_configuration(handle_hci_event, gc_handle, &ancs_data_source_characteristic, 305 GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 306 break; 307 default: 308 break; 309 } 310 break; 311 case TC_W4_DATA_SOURCE_SUBSCRIBED: 312 switch(hci_event_packet_get_type(packet)){ 313 case GATT_EVENT_QUERY_COMPLETE: 314 log_info("ANCS Data Source subscribed"); 315 tc_state = TC_SUBSCRIBED; 316 notify_client_simple(ANCS_SUBEVENT_CLIENT_CONNECTED); 317 break; 318 default: 319 break; 320 } 321 break; 322 case TC_SUBSCRIBED: 323 if ((hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) && (hci_event_packet_get_type(packet) != GATT_EVENT_INDICATION) ) break; 324 325 value_handle = little_endian_read_16(packet, 4); 326 value_length = little_endian_read_16(packet, 6); 327 value = &packet[8]; 328 329 log_info("ANCS Notification, value handle %u", value_handle); 330 331 if (value_handle == ancs_data_source_characteristic.value_handle){ 332 int i; 333 for (i=0;i<value_length;i++) { 334 ancs_chunk_parser_handle_byte(value[i]); 335 } 336 } else if (value_handle == ancs_notification_source_characteristic.value_handle){ 337 ancs_notification_uid = little_endian_read_32(value, 4); 338 log_info("Notification received: EventID %02x, EventFlags %02x, CategoryID %02x, CategoryCount %u, UID %04x", 339 value[0], value[1], value[2], value[3], (int) ancs_notification_uid); 340 static uint8_t get_notification_attributes[] = {0, 0,0,0,0, 0, 1,32,0, 2,32,0, 3,32,0, 4, 5}; 341 little_endian_store_32(get_notification_attributes, 1, ancs_notification_uid); 342 ancs_notification_uid = 0; 343 ancs_chunk_parser_init(); 344 gatt_client_write_value_of_characteristic(handle_hci_event, gc_handle, ancs_control_point_characteristic.value_handle, 345 sizeof(get_notification_attributes), get_notification_attributes); 346 } else { 347 log_info("Unknown Source: "); 348 log_info_hexdump(value , value_length); 349 } 350 break; 351 default: 352 break; 353 } 354 // app_run(); 355 } 356 357 void ancs_client_init(void){ 358 hci_event_callback_registration.callback = &handle_hci_event; 359 hci_add_event_handler(&hci_event_callback_registration); 360 } 361