xref: /btstack/src/ble/gatt-service/ancs_client.c (revision 9d1eff910dbc58b2017f81f08fad23fcf87ee1eb)
16bdecec7SMatthias Ringwald /*
26bdecec7SMatthias Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
36bdecec7SMatthias Ringwald  *
46bdecec7SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
56bdecec7SMatthias Ringwald  * modification, are permitted provided that the following conditions
66bdecec7SMatthias Ringwald  * are met:
76bdecec7SMatthias Ringwald  *
86bdecec7SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
96bdecec7SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
106bdecec7SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
116bdecec7SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
126bdecec7SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
136bdecec7SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
146bdecec7SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
156bdecec7SMatthias Ringwald  *    from this software without specific prior written permission.
166bdecec7SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
176bdecec7SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
186bdecec7SMatthias Ringwald  *    monetary gain.
196bdecec7SMatthias Ringwald  *
206bdecec7SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
216bdecec7SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
226bdecec7SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
256bdecec7SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
266bdecec7SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
276bdecec7SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
286bdecec7SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
296bdecec7SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
306bdecec7SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
316bdecec7SMatthias Ringwald  * SUCH DAMAGE.
326bdecec7SMatthias Ringwald  *
336bdecec7SMatthias Ringwald  * Please inquire about commercial licensing options at
346bdecec7SMatthias Ringwald  * [email protected]
356bdecec7SMatthias Ringwald  *
366bdecec7SMatthias Ringwald  */
376bdecec7SMatthias Ringwald 
386bdecec7SMatthias Ringwald #define BTSTACK_FILE__ "ancs_client.c"
396bdecec7SMatthias Ringwald 
406bdecec7SMatthias Ringwald #include "btstack_config.h"
416bdecec7SMatthias Ringwald 
426bdecec7SMatthias Ringwald #include <stdint.h>
436bdecec7SMatthias Ringwald #include <string.h>
446bdecec7SMatthias Ringwald 
456bdecec7SMatthias Ringwald #include "ble/gatt-service/ancs_client.h"
466bdecec7SMatthias Ringwald 
476bdecec7SMatthias Ringwald #include "ble/core.h"
486bdecec7SMatthias Ringwald #include "ble/gatt_client.h"
496bdecec7SMatthias Ringwald #include "ble/sm.h"
506bdecec7SMatthias Ringwald #include "btstack_debug.h"
516bdecec7SMatthias Ringwald #include "btstack_event.h"
526bdecec7SMatthias Ringwald #include "gap.h"
536bdecec7SMatthias Ringwald 
546bdecec7SMatthias Ringwald // ancs_client.h Start
556bdecec7SMatthias Ringwald typedef enum ancs_chunk_parser_state {
566bdecec7SMatthias Ringwald     W4_ATTRIBUTE_ID,
576bdecec7SMatthias Ringwald     W4_ATTRIBUTE_LEN,
586bdecec7SMatthias Ringwald     W4_ATTRIBUTE_COMPLETE,
596bdecec7SMatthias Ringwald } ancs_chunk_parser_state_t;
606bdecec7SMatthias Ringwald 
616bdecec7SMatthias Ringwald typedef enum {
626bdecec7SMatthias Ringwald     TC_IDLE,
636bdecec7SMatthias Ringwald     TC_W4_ENCRYPTED_CONNECTION,
646bdecec7SMatthias Ringwald     TC_W4_SERVICE_RESULT,
656bdecec7SMatthias Ringwald     TC_W4_CHARACTERISTIC_RESULT,
666bdecec7SMatthias Ringwald     TC_W4_DATA_SOURCE_SUBSCRIBED,
676bdecec7SMatthias Ringwald     TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED,
686bdecec7SMatthias Ringwald     TC_SUBSCRIBED,
696bdecec7SMatthias Ringwald     TC_W4_DISCONNECT
706bdecec7SMatthias Ringwald } tc_state_t;
716bdecec7SMatthias Ringwald 
726bdecec7SMatthias Ringwald static uint32_t ancs_notification_uid;
736bdecec7SMatthias Ringwald static uint16_t gc_handle;
746bdecec7SMatthias Ringwald static gatt_client_notification_t ancs_notification_source_notification;
756bdecec7SMatthias Ringwald static gatt_client_notification_t ancs_data_source_notification;
766bdecec7SMatthias Ringwald static int ancs_service_found;
776bdecec7SMatthias Ringwald static gatt_client_service_t  ancs_service;
786bdecec7SMatthias Ringwald static gatt_client_characteristic_t ancs_notification_source_characteristic;
796bdecec7SMatthias Ringwald static gatt_client_characteristic_t ancs_control_point_characteristic;
806bdecec7SMatthias Ringwald static gatt_client_characteristic_t ancs_data_source_characteristic;
816bdecec7SMatthias Ringwald static int ancs_characteristcs;
826bdecec7SMatthias Ringwald static tc_state_t tc_state = TC_IDLE;
836bdecec7SMatthias Ringwald 
846bdecec7SMatthias Ringwald static ancs_chunk_parser_state_t chunk_parser_state;
856bdecec7SMatthias Ringwald static uint8_t  ancs_notification_buffer[50];
866bdecec7SMatthias Ringwald static uint16_t ancs_bytes_received;
876bdecec7SMatthias Ringwald static uint16_t ancs_bytes_needed;
886bdecec7SMatthias Ringwald static uint8_t  ancs_attribute_id;
896bdecec7SMatthias Ringwald static uint16_t ancs_attribute_len;
906bdecec7SMatthias Ringwald 
916bdecec7SMatthias Ringwald static btstack_packet_handler_t client_handler;
926bdecec7SMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration;
936bdecec7SMatthias Ringwald 
94e115a09fSMatthias Ringwald static void ancs_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
95e115a09fSMatthias Ringwald 
966bdecec7SMatthias Ringwald void ancs_client_register_callback(btstack_packet_handler_t handler){
976bdecec7SMatthias Ringwald     client_handler = handler;
986bdecec7SMatthias Ringwald }
996bdecec7SMatthias Ringwald 
1006bdecec7SMatthias Ringwald static void notify_client_text(int event_type){
1016bdecec7SMatthias Ringwald     if (!client_handler) return;
1026bdecec7SMatthias Ringwald     uint8_t event[7 + sizeof(ancs_notification_buffer) + 1];
1036bdecec7SMatthias Ringwald     event[0] = HCI_EVENT_ANCS_META;
1046bdecec7SMatthias Ringwald     event[1] = 5u + ancs_attribute_len;
1056bdecec7SMatthias Ringwald     event[2] = event_type;
1066bdecec7SMatthias Ringwald     little_endian_store_16(event, 3, gc_handle);
1076bdecec7SMatthias Ringwald     little_endian_store_16(event, 5, ancs_attribute_id);
1086bdecec7SMatthias Ringwald     (void)memcpy(&event[7], ancs_notification_buffer, ancs_attribute_len);
1096bdecec7SMatthias Ringwald     // we're nice
1106bdecec7SMatthias Ringwald     event[7u+ancs_attribute_len] = 0u;
1116bdecec7SMatthias Ringwald     (*client_handler)(HCI_EVENT_PACKET, 0u, event, event[1u] + 2u);
1126bdecec7SMatthias Ringwald }
1136bdecec7SMatthias Ringwald 
1146bdecec7SMatthias Ringwald static void notify_client_simple(int event_type){
1156bdecec7SMatthias Ringwald     if (!client_handler) return;
1166bdecec7SMatthias Ringwald     uint8_t event[5];
1176bdecec7SMatthias Ringwald     event[0] = HCI_EVENT_ANCS_META;
1186bdecec7SMatthias Ringwald     event[1] = 3;
1196bdecec7SMatthias Ringwald     event[2] = event_type;
1206bdecec7SMatthias Ringwald     little_endian_store_16(event, 3, gc_handle);
1216bdecec7SMatthias Ringwald     (*client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1226bdecec7SMatthias Ringwald }
1236bdecec7SMatthias Ringwald 
1246bdecec7SMatthias Ringwald static void ancs_chunk_parser_init(void){
12541866fd3SMatthias Ringwald     // skip comand id and notification uid
1266bdecec7SMatthias Ringwald     chunk_parser_state = W4_ATTRIBUTE_ID;
1276bdecec7SMatthias Ringwald     ancs_bytes_received = 0;
1286bdecec7SMatthias Ringwald     ancs_bytes_needed = 6;
1296bdecec7SMatthias Ringwald }
1306bdecec7SMatthias Ringwald 
1316bdecec7SMatthias Ringwald const char * ancs_client_attribute_name_for_id(int id){
1326bdecec7SMatthias Ringwald     static const char * ancs_attribute_names[] = {
1336bdecec7SMatthias Ringwald             "AppIdentifier",
1346bdecec7SMatthias Ringwald             "IDTitle",
1356bdecec7SMatthias Ringwald             "IDSubtitle",
1366bdecec7SMatthias Ringwald             "IDMessage",
1376bdecec7SMatthias Ringwald             "IDMessageSize",
1386bdecec7SMatthias Ringwald             "IDDate"
1396bdecec7SMatthias Ringwald     };
1406bdecec7SMatthias Ringwald 
14141866fd3SMatthias Ringwald     static const uint16_t ANCS_ATTRIBUTE_NAMES_COUNT = sizeof(ancs_attribute_names) / sizeof(char *);
1426bdecec7SMatthias Ringwald 
14341866fd3SMatthias Ringwald     if (id >= ANCS_ATTRIBUTE_NAMES_COUNT) return NULL;
1446bdecec7SMatthias Ringwald     return ancs_attribute_names[id];
1456bdecec7SMatthias Ringwald }
1466bdecec7SMatthias Ringwald 
1476bdecec7SMatthias Ringwald static void ancs_chunk_parser_handle_byte(uint8_t data){
1486bdecec7SMatthias Ringwald     ancs_notification_buffer[ancs_bytes_received++] = data;
1496bdecec7SMatthias Ringwald     if (ancs_bytes_received < ancs_bytes_needed) return;
1506bdecec7SMatthias Ringwald     switch (chunk_parser_state){
1516bdecec7SMatthias Ringwald         case W4_ATTRIBUTE_ID:
1526bdecec7SMatthias Ringwald             ancs_attribute_id   = ancs_notification_buffer[ancs_bytes_received-1u];
1536bdecec7SMatthias Ringwald             ancs_bytes_received = 0;
1546bdecec7SMatthias Ringwald             ancs_bytes_needed   = 2;
1556bdecec7SMatthias Ringwald             chunk_parser_state  = W4_ATTRIBUTE_LEN;
1566bdecec7SMatthias Ringwald             break;
1576bdecec7SMatthias Ringwald         case W4_ATTRIBUTE_LEN:
1586bdecec7SMatthias Ringwald             ancs_attribute_len  = little_endian_read_16(ancs_notification_buffer, ancs_bytes_received-2u);
1596bdecec7SMatthias Ringwald             ancs_bytes_received = 0;
1606bdecec7SMatthias Ringwald             ancs_bytes_needed   = ancs_attribute_len;
1616bdecec7SMatthias Ringwald             if (ancs_attribute_len == 0u) {
1626bdecec7SMatthias Ringwald                 ancs_bytes_needed   = 1;
1636bdecec7SMatthias Ringwald                 chunk_parser_state  = W4_ATTRIBUTE_ID;
1646bdecec7SMatthias Ringwald                 break;
1656bdecec7SMatthias Ringwald             }
1666bdecec7SMatthias Ringwald             chunk_parser_state  = W4_ATTRIBUTE_COMPLETE;
1676bdecec7SMatthias Ringwald             break;
1686bdecec7SMatthias Ringwald         case W4_ATTRIBUTE_COMPLETE:
1696bdecec7SMatthias Ringwald             ancs_notification_buffer[ancs_bytes_received] = 0;
1706bdecec7SMatthias Ringwald             notify_client_text(ANCS_SUBEVENT_CLIENT_NOTIFICATION);
1716bdecec7SMatthias Ringwald             ancs_bytes_received = 0;
1726bdecec7SMatthias Ringwald             ancs_bytes_needed   = 1;
1736bdecec7SMatthias Ringwald             chunk_parser_state  = W4_ATTRIBUTE_ID;
1746bdecec7SMatthias Ringwald             break;
1756bdecec7SMatthias Ringwald         default:
17641866fd3SMatthias Ringwald             btstack_unreachable();
1776bdecec7SMatthias Ringwald             break;
1786bdecec7SMatthias Ringwald     }
1796bdecec7SMatthias Ringwald }
1806bdecec7SMatthias Ringwald 
181e115a09fSMatthias Ringwald static void ancs_client_handle_notification(uint8_t * packet, uint16_t size){
1824ddcd9eaSMatthias Ringwald     UNUSED(size);
1834ddcd9eaSMatthias Ringwald 
184e115a09fSMatthias Ringwald     uint16_t  value_handle = little_endian_read_16(packet, 4);
185e115a09fSMatthias Ringwald     uint16_t  value_length = little_endian_read_16(packet, 6);
186e115a09fSMatthias Ringwald     uint8_t * value = &packet[8];
1876bdecec7SMatthias Ringwald 
188e115a09fSMatthias Ringwald     log_info("ANCS Notification, value handle %u", value_handle);
1896bdecec7SMatthias Ringwald 
190e115a09fSMatthias Ringwald     if (value_handle == ancs_data_source_characteristic.value_handle){
191e115a09fSMatthias Ringwald         int i;
192e115a09fSMatthias Ringwald         for (i=0;i<value_length;i++) {
193e115a09fSMatthias Ringwald             ancs_chunk_parser_handle_byte(value[i]);
194e115a09fSMatthias Ringwald         }
195e115a09fSMatthias Ringwald     } else if (value_handle == ancs_notification_source_characteristic.value_handle){
196e115a09fSMatthias Ringwald         ancs_notification_uid = little_endian_read_32(value, 4);
197e115a09fSMatthias Ringwald         log_info("Notification received: EventID %02x, EventFlags %02x, CategoryID %02x, CategoryCount %u, UID %04x",
198e115a09fSMatthias Ringwald                  value[0], value[1], value[2], value[3], (int) ancs_notification_uid);
199e115a09fSMatthias Ringwald         static uint8_t get_notification_attributes[] = {0, 0,0,0,0,  0,  1,32,0,  2,32,0, 3,32,0, 4, 5};
200e115a09fSMatthias Ringwald         little_endian_store_32(get_notification_attributes, 1, ancs_notification_uid);
201e115a09fSMatthias Ringwald         ancs_notification_uid = 0;
202e115a09fSMatthias Ringwald         ancs_chunk_parser_init();
203e115a09fSMatthias Ringwald         gatt_client_write_value_of_characteristic(ancs_client_handle_gatt_client_event, gc_handle, ancs_control_point_characteristic.value_handle,
204e115a09fSMatthias Ringwald                                                   sizeof(get_notification_attributes), get_notification_attributes);
205e115a09fSMatthias Ringwald     } else {
206e115a09fSMatthias Ringwald         log_info("Unknown Source: ");
207e115a09fSMatthias Ringwald         log_info_hexdump(value , value_length);
208e115a09fSMatthias Ringwald     }
209e115a09fSMatthias Ringwald }
210e115a09fSMatthias Ringwald 
211e115a09fSMatthias Ringwald static void ancs_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
212e115a09fSMatthias Ringwald 
2134ddcd9eaSMatthias Ringwald     UNUSED(packet_type);
2144ddcd9eaSMatthias Ringwald     UNUSED(channel);
2154ddcd9eaSMatthias Ringwald 
2166bdecec7SMatthias Ringwald     static const uint8_t ancs_notification_source_uuid[] = {0x9F,0xBF,0x12,0x0D,0x63,0x01,0x42,0xD9,0x8C,0x58,0x25,0xE6,0x99,0xA2,0x1D,0xBD};
2176bdecec7SMatthias Ringwald     static const uint8_t ancs_control_point_uuid[] =       {0x69,0xD1,0xD8,0xF3,0x45,0xE1,0x49,0xA8,0x98,0x21,0x9B,0xBD,0xFD,0xAA,0xD9,0xD9};
2186bdecec7SMatthias Ringwald     static const uint8_t ancs_data_source_uuid[] =         {0x22,0xEA,0xC6,0xE9,0x24,0xD6,0x4B,0xB5,0xBE,0x44,0xB3,0x6A,0xCE,0x7C,0x7B,0xFB};
2196bdecec7SMatthias Ringwald 
2206bdecec7SMatthias Ringwald     gatt_client_characteristic_t characteristic;
2216bdecec7SMatthias Ringwald 
2226bdecec7SMatthias Ringwald     switch(tc_state){
2236bdecec7SMatthias Ringwald         case TC_W4_SERVICE_RESULT:
2246bdecec7SMatthias Ringwald             switch(hci_event_packet_get_type(packet)){
2256bdecec7SMatthias Ringwald                 case GATT_EVENT_SERVICE_QUERY_RESULT:
2266bdecec7SMatthias Ringwald                     gatt_event_service_query_result_get_service(packet, &ancs_service);
2276bdecec7SMatthias Ringwald                     ancs_service_found = 1;
2286bdecec7SMatthias Ringwald                     break;
2296bdecec7SMatthias Ringwald                 case GATT_EVENT_QUERY_COMPLETE:
2306bdecec7SMatthias Ringwald                     if (!ancs_service_found){
2316bdecec7SMatthias Ringwald                         log_info("ANCS Service not found");
2326bdecec7SMatthias Ringwald                         tc_state = TC_IDLE;
2336bdecec7SMatthias Ringwald                         break;
2346bdecec7SMatthias Ringwald                     }
2356bdecec7SMatthias Ringwald                     tc_state = TC_W4_CHARACTERISTIC_RESULT;
2366bdecec7SMatthias Ringwald                     log_info("ANCS Client - Discover characteristics for ANCS SERVICE ");
237e115a09fSMatthias Ringwald                     gatt_client_discover_characteristics_for_service(ancs_client_handle_gatt_client_event, gc_handle, &ancs_service);
2386bdecec7SMatthias Ringwald                     break;
2396bdecec7SMatthias Ringwald                 default:
2406bdecec7SMatthias Ringwald                     break;
2416bdecec7SMatthias Ringwald             }
2426bdecec7SMatthias Ringwald             break;
2436bdecec7SMatthias Ringwald 
2446bdecec7SMatthias Ringwald         case TC_W4_CHARACTERISTIC_RESULT:
2456bdecec7SMatthias Ringwald             switch(hci_event_packet_get_type(packet)){
2466bdecec7SMatthias Ringwald                 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
2476bdecec7SMatthias Ringwald                     gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
2486bdecec7SMatthias Ringwald                     if (memcmp(characteristic.uuid128, ancs_notification_source_uuid, 16) == 0){
2496bdecec7SMatthias Ringwald                         log_info("ANCS Notification Source found, attribute handle %u", characteristic.value_handle);
2506bdecec7SMatthias Ringwald                         ancs_notification_source_characteristic = characteristic;
2516bdecec7SMatthias Ringwald                         ancs_characteristcs++;
2526bdecec7SMatthias Ringwald                         break;
2536bdecec7SMatthias Ringwald                     }
2546bdecec7SMatthias Ringwald                     if (memcmp(characteristic.uuid128, ancs_control_point_uuid, 16) == 0){
2556bdecec7SMatthias Ringwald                         log_info("ANCS Control Point found, attribute handle %u", characteristic.value_handle);
2566bdecec7SMatthias Ringwald                         ancs_control_point_characteristic = characteristic;
2576bdecec7SMatthias Ringwald                         ancs_characteristcs++;
2586bdecec7SMatthias Ringwald                         break;
2596bdecec7SMatthias Ringwald                     }
2606bdecec7SMatthias Ringwald                     if (memcmp(characteristic.uuid128, ancs_data_source_uuid, 16) == 0){
2616bdecec7SMatthias Ringwald                         log_info("ANCS Data Source found, attribute handle %u", characteristic.value_handle);
2626bdecec7SMatthias Ringwald                         ancs_data_source_characteristic = characteristic;
2636bdecec7SMatthias Ringwald                         ancs_characteristcs++;
2646bdecec7SMatthias Ringwald                         break;
2656bdecec7SMatthias Ringwald                     }
2666bdecec7SMatthias Ringwald                     break;
2676bdecec7SMatthias Ringwald                 case GATT_EVENT_QUERY_COMPLETE:
2686bdecec7SMatthias Ringwald                     log_info("ANCS Characteristcs count %u", ancs_characteristcs);
2696bdecec7SMatthias Ringwald                     tc_state = TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED;
270e115a09fSMatthias Ringwald                     gatt_client_listen_for_characteristic_value_updates(&ancs_notification_source_notification, &ancs_client_handle_gatt_client_event, gc_handle, &ancs_notification_source_characteristic);
271e115a09fSMatthias Ringwald                     gatt_client_write_client_characteristic_configuration(ancs_client_handle_gatt_client_event, gc_handle, &ancs_notification_source_characteristic,
2726bdecec7SMatthias Ringwald                                                                           GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
2736bdecec7SMatthias Ringwald                     break;
2746bdecec7SMatthias Ringwald                 default:
2756bdecec7SMatthias Ringwald                     break;
2766bdecec7SMatthias Ringwald             }
2776bdecec7SMatthias Ringwald             break;
2786bdecec7SMatthias Ringwald         case TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED:
2796bdecec7SMatthias Ringwald             switch(hci_event_packet_get_type(packet)){
2806bdecec7SMatthias Ringwald                 case GATT_EVENT_QUERY_COMPLETE:
2816bdecec7SMatthias Ringwald                     log_info("ANCS Notification Source subscribed");
2826bdecec7SMatthias Ringwald                     tc_state = TC_W4_DATA_SOURCE_SUBSCRIBED;
283e115a09fSMatthias Ringwald                     gatt_client_listen_for_characteristic_value_updates(&ancs_data_source_notification, &ancs_client_handle_gatt_client_event, gc_handle, &ancs_data_source_characteristic);
284e115a09fSMatthias Ringwald                     gatt_client_write_client_characteristic_configuration(ancs_client_handle_gatt_client_event, gc_handle, &ancs_data_source_characteristic,
2856bdecec7SMatthias Ringwald                                                                           GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
2866bdecec7SMatthias Ringwald                     break;
2876bdecec7SMatthias Ringwald                 default:
2886bdecec7SMatthias Ringwald                     break;
2896bdecec7SMatthias Ringwald             }
2906bdecec7SMatthias Ringwald             break;
2916bdecec7SMatthias Ringwald         case TC_W4_DATA_SOURCE_SUBSCRIBED:
2926bdecec7SMatthias Ringwald             switch(hci_event_packet_get_type(packet)){
2936bdecec7SMatthias Ringwald                 case GATT_EVENT_QUERY_COMPLETE:
2946bdecec7SMatthias Ringwald                     log_info("ANCS Data Source subscribed");
2956bdecec7SMatthias Ringwald                     tc_state = TC_SUBSCRIBED;
2966bdecec7SMatthias Ringwald                     notify_client_simple(ANCS_SUBEVENT_CLIENT_CONNECTED);
2976bdecec7SMatthias Ringwald                     break;
2986bdecec7SMatthias Ringwald                 default:
2996bdecec7SMatthias Ringwald                     break;
3006bdecec7SMatthias Ringwald             }
3016bdecec7SMatthias Ringwald             break;
3026bdecec7SMatthias Ringwald         case TC_SUBSCRIBED:
30341866fd3SMatthias Ringwald             switch(hci_event_packet_get_type(packet)){
30441866fd3SMatthias Ringwald                 case GATT_EVENT_NOTIFICATION:
30541866fd3SMatthias Ringwald                 case GATT_EVENT_INDICATION:
306e115a09fSMatthias Ringwald                     ancs_client_handle_notification(packet, size);
3076bdecec7SMatthias Ringwald                     break;
3086bdecec7SMatthias Ringwald                 default:
3096bdecec7SMatthias Ringwald                     break;
3106bdecec7SMatthias Ringwald             }
31141866fd3SMatthias Ringwald             break;
31241866fd3SMatthias Ringwald         default:
31341866fd3SMatthias Ringwald             break;
31441866fd3SMatthias Ringwald     }
315e115a09fSMatthias Ringwald }
316e115a09fSMatthias Ringwald 
317e115a09fSMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
318e115a09fSMatthias Ringwald 
319e115a09fSMatthias Ringwald     UNUSED(packet_type); // ok: only hci events
320e115a09fSMatthias Ringwald     UNUSED(channel);     // ok: there is no channel
321e115a09fSMatthias Ringwald     UNUSED(size);        // ok: fixed format events read from HCI buffer
322e115a09fSMatthias Ringwald 
323e115a09fSMatthias Ringwald     static const uint8_t ancs_service_uuid[] =             {0x79,0x05,0xF4,0x31,0xB5,0xCE,0x4E,0x99,0xA4,0x0F,0x4B,0x1E,0x12,0x2D,0x00,0xD0};
324e115a09fSMatthias Ringwald 
325e115a09fSMatthias Ringwald     int connection_encrypted;
326e115a09fSMatthias Ringwald 
327e115a09fSMatthias Ringwald     // handle connect / disconncet events first
328e115a09fSMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
329*9d1eff91SMatthias Ringwald         case HCI_EVENT_META_GAP:
330*9d1eff91SMatthias Ringwald             switch (hci_event_gap_meta_get_subevent_code(packet)) {
331*9d1eff91SMatthias Ringwald                 case GAP_SUBEVENT_LE_CONNECTION_COMPLETE:
332*9d1eff91SMatthias Ringwald                     gc_handle = gap_subevent_le_connection_complete_get_connection_handle(packet);
333e115a09fSMatthias Ringwald                     log_info("Connection handle 0x%04x, request encryption", gc_handle);
334e115a09fSMatthias Ringwald 
335e115a09fSMatthias Ringwald                     // we need to be paired to enable notifications
336e115a09fSMatthias Ringwald                     tc_state = TC_W4_ENCRYPTED_CONNECTION;
337e115a09fSMatthias Ringwald                     ancs_service_found = false;
338e115a09fSMatthias Ringwald                     sm_request_pairing(gc_handle);
339e115a09fSMatthias Ringwald                     break;
340e115a09fSMatthias Ringwald                 default:
341e115a09fSMatthias Ringwald                     break;
342e115a09fSMatthias Ringwald             }
343e115a09fSMatthias Ringwald             return;
344e115a09fSMatthias Ringwald 
345e115a09fSMatthias Ringwald         case HCI_EVENT_ENCRYPTION_CHANGE:
3468601e696SMatthias Ringwald         case HCI_EVENT_ENCRYPTION_CHANGE_V2:
3478601e696SMatthias Ringwald             if (gc_handle != hci_event_encryption_change_get_connection_handle(packet)) return;
3488601e696SMatthias Ringwald             connection_encrypted = hci_event_encryption_change_get_encryption_enabled(packet);
349e115a09fSMatthias Ringwald             log_info("Encryption state change: %u", connection_encrypted);
350e115a09fSMatthias Ringwald             if (!connection_encrypted) return;
351e115a09fSMatthias Ringwald             if (tc_state != TC_W4_ENCRYPTED_CONNECTION) return;
352e115a09fSMatthias Ringwald 
353e115a09fSMatthias Ringwald             // let's start
354e115a09fSMatthias Ringwald             log_info("\nANCS Client - CONNECTED, discover ANCS service");
355e115a09fSMatthias Ringwald             tc_state = TC_W4_SERVICE_RESULT;
356e115a09fSMatthias Ringwald             gatt_client_discover_primary_services_by_uuid128(ancs_client_handle_gatt_client_event, gc_handle, ancs_service_uuid);
357e115a09fSMatthias Ringwald             return;
358e115a09fSMatthias Ringwald 
359e115a09fSMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
360e115a09fSMatthias Ringwald             if (hci_event_disconnection_complete_get_connection_handle(packet) != gc_handle) break;
361e115a09fSMatthias Ringwald             if (tc_state == TC_SUBSCRIBED){
362e115a09fSMatthias Ringwald                 notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED);
363e115a09fSMatthias Ringwald             }
364e115a09fSMatthias Ringwald             tc_state = TC_IDLE;
365e115a09fSMatthias Ringwald             gc_handle = 0;
366e115a09fSMatthias Ringwald             return;
367e115a09fSMatthias Ringwald 
368e115a09fSMatthias Ringwald         default:
369e115a09fSMatthias Ringwald             break;
370e115a09fSMatthias Ringwald     }
3716bdecec7SMatthias Ringwald }
3726bdecec7SMatthias Ringwald 
3736bdecec7SMatthias Ringwald void ancs_client_init(void){
3746bdecec7SMatthias Ringwald     hci_event_callback_registration.callback = &handle_hci_event;
3756bdecec7SMatthias Ringwald     hci_add_event_handler(&hci_event_callback_registration);
3766bdecec7SMatthias Ringwald }
37741866fd3SMatthias Ringwald 
37841866fd3SMatthias Ringwald // unit test only
37941866fd3SMatthias Ringwald #if defined __cplusplus
38041866fd3SMatthias Ringwald extern "C"
38141866fd3SMatthias Ringwald #endif
38241866fd3SMatthias Ringwald void ancs_client_set_invalid_parser_state(void);
38341866fd3SMatthias Ringwald void ancs_client_set_invalid_parser_state(void){
38441866fd3SMatthias Ringwald     chunk_parser_state = (ancs_chunk_parser_state_t) 0x17;
38541866fd3SMatthias Ringwald }
386