ANCS User Guide
Introduction to ANCS
ANCS is a notification service protocol for iOS devices, allowing Bluetooth accessories (such as watches) to receive and manage notifications from iOS, achieving cross-device notification alerts and interaction. For more details, please refer to: Apple Notification Center Service (ANCS) Specification
ANCS comprises three core characteristics, each responsible for different data interaction functions:
-
Notification Source Primarily used to push basic information about notifications, including events such as arrival, update, and removal. The push content includes (in order):
EventID: Event type (new notification, modified, removed, etc.)
EventFlags: Notification status flags (e.g., high priority, silent, etc.)
CategoryID: Message category (e.g., incoming call, SMS, calendar, etc.)
CategoryCount: Count of similar notifications
NotificationUID: Unique identifier for the notification (used for subsequent detail queries)
Control Point The client (watch) interacts with the iOS device by writing commands, serving as the entry point for obtaining notification attributes, app attributes, and executing actions.
-
Data Source The channel through which the iOS device proactively sends detailed content or operation results, commonly including:
Detailed notification attributes: After the watch initiates a 'Get Notification Details' command through the Control Point, iOS returns the required field content via the Data Source.
App attribute information: After the watch initiates a command through the Control Point, iOS returns the app's attributes (such as the name) via the Data Source.
Notification operation feedback: After the watch initiates an operation command (such as clear, open) through the Control Point, iOS returns the operation result via the Data Source.
SDK-based ANCS Call Process
-
Add ANCS service initialization in
app_ble_service_init()-
Call
ancs_init()andclient_init()to complete the ANCS client initialization.client_init(1); ancs_init(1);
-
-
Add ANCS message type handling in
watch_handle_io_message()-
Support ANCS message dispatch by calling
ancs_handle_msg().#if F_BT_ANCS_CLIENT_SUPPORT case IO_MSG_TYPE_ANCS: { ancs_handle_msg(p_watch_msg); } break; #endif
-
-
Add ANCS service discovery in
app_handle_authen_state_evt()-
After pairing/authentication is complete, trigger ANCS service discovery via
ancs_start_discovery().#if F_BT_ANCS_CLIENT_SUPPORT ancs_start_discovery(conn_id); #endif
-
-
After discovering the service, subscribe to Notification Source and Data Source in
ancs_client_cb()-
Subscribe to Notification Source:
case ANCS_WRITE_DATA_SOURCE_NOTIFY_ENABLE: APP_PRINT_INFO0("ANCS_WRITE_DATA_SOURCE_NOTIFY_ENABLE"); ancs_set_notification_source_notify(conn_id, true);//Used by application, to set the notification flag of notification source. break;
-
Subscribe to Data Source:
case DISC_ANCS_DONE: APP_PRINT_INFO0("ANCS BLE Client CB: discover procedure done."); ancs_set_data_source_notify(conn_id, true);//Used by application, to set the notification flag of data source. break;
-
-
After successful subscription, parse Notification Source data.
-
The mobile phone pushes Notification Source, use
app_parse_notification_source_data()to parse the content.case ANCS_FROM_NOTIFICATION_SOURCE: APP_PRINT_INFO2("ANCS_FROM_NOTIFICATION_SOURCE: conn_id %d, length %d", conn_id, p_cb_data->cb_content.notify_data.value_size); app_parse_notification_source_data(conn_id, p_cb_data->cb_content.notify_data.p_value, p_cb_data->cb_content.notify_data.value_size); break;
-
In the parsing function, message filtering and categorization can be implemented based on CategoryID.
APP_PRINT_INFO5("app_parse_notification_source_data: event_id %d, event_flags 0x%02x, category_id %d, category_count %d, notification_uid 0x%08x", ns_data.event_id, ns_data.event_flags, ns_data.category_id, ns_data.category_count, ns_data.notification_uid ); APP_PRINT_INFO5("event_flags: silent %d, important %d, pre-existing %d, positive action %d, negative action %d ", ns_data.event_flags & NS_EVENT_FLAG_SILENT, ns_data.event_flags & NS_EVENT_FLAG_IMPORTANT, ns_data.event_flags & NS_EVENT_FLAG_PRE_EXISTING, ns_data.event_flags & NS_EVENT_FLAG_POSITIVE_ACTION, ns_data.event_flags & NS_EVENT_FLAG_NEGATIVE_ACTION );
-
-
Parse the Data Source content.
-
After receiving the required Data Source data, use
app_parse_data_soucre_notifications()for further processing.case ANCS_FROM_DATA_SOURCE: APP_PRINT_INFO2("ANCS_FROM_DATA_SOURCE: conn_id %d, length %d", conn_id, p_cb_data->cb_content.notify_data.value_size); app_parse_data_soucre_notifications(conn_id, p_cb_data->cb_content.notify_data.p_value, p_cb_data->cb_content.notify_data.value_size); break;
-
The complete data can be obtained in
app_parse_data_soucre_notifications(), and the processing flow can be chosen based on the parsing state.APP_PRINT_INFO2("ANCS_FROM_DATA_SOURCE: conn_id %d, len =%d", conn_id, len); #if F_BT_ANCS_CLIENT_DEBUG APP_PRINT_INFO1("data = %b", TRACE_BINARY(len, p_data)); #endif T_APP_ANCS_LINK *p_ancs_link = &ancs_link_table[conn_id]; #if F_BT_ANCS_CLIENT_DEBUG APP_PRINT_INFO1("m_parse_state %d", p_ancs_link->m_parse_state); #endif // Determine whether to parse notification_attribute or app_attribute based on m_parse_state if (p_ancs_link->m_parse_state < DS_PARSE_GET_APP_COMMAND_ID) { app_parse_notification_attribute(p_ancs_link, p_data, len); } #if F_BT_ANCS_GET_APP_ATTR else { app_parse_app_attribute(p_ancs_link, p_data, len); } #endif
-
app_parse_data_source_notifications()distinguishes between two parsing processes based on m_parse_state:-
Notification Attribute Parsing
-
Calls
app_parse_notification_attribute(), parsing command_id, notification_uid, attribute_id, attribute_len, and attribute_data step by step.//command id case DS_PARSE_GET_NOTIFICATION_COMMAND_ID: p_ancs_link->notification_attr.command_id = p_data[i]; p_ancs_link->m_parse_state = DS_PARSE_UID1; APP_PRINT_INFO1("parse notify attr: command_id %d", p_ancs_link->notification_attr.command_id); break; //notification uid case DS_PARSE_UID1: p_ancs_link->notification_attr.notification_uid[0] = p_data[i]; p_ancs_link->m_parse_state = DS_PARSE_UID2; APP_PRINT_INFO1("parse notify attr: notification_uid[0] %d", p_ancs_link->notification_attr.notification_uid[0]); break; case DS_PARSE_UID2: p_ancs_link->notification_attr.notification_uid[1] = p_data[i]; p_ancs_link->m_parse_state = DS_PARSE_UID3; APP_PRINT_INFO1("parse notify attr: notification_uid[1] %d", p_ancs_link->notification_attr.notification_uid[1]); break; case DS_PARSE_UID3: p_ancs_link->notification_attr.notification_uid[2] = p_data[i]; p_ancs_link->m_parse_state = DS_PARSE_UID4; APP_PRINT_INFO1("parse notify attr: notification_uid[2] %d", p_ancs_link->notification_attr.notification_uid[2]); break; case DS_PARSE_UID4: p_ancs_link->notification_attr.notification_uid[3] = p_data[i]; p_ancs_link->m_parse_state = DS_PARSE_ATTRIBUTE_ID; APP_PRINT_INFO1("parse notify attr: notification_uid[3] %d", p_ancs_link->notification_attr.notification_uid[3]); break; //attribute id/attribute len case DS_PARSE_ATTRIBUTE_ID: p_ancs_link->notification_attr.attribute_id = p_data[i]; p_ancs_link->m_parse_state = DS_PARSE_ATTRIBUTE_LEN1; break; case DS_PARSE_ATTRIBUTE_LEN1: p_ancs_link->notification_attr.attribute_len = p_data[i]; p_ancs_link->m_parse_state = DS_PARSE_ATTRIBUTE_LEN2; break; case DS_PARSE_ATTRIBUTE_LEN2: p_ancs_link->notification_attr.attribute_len |= (p_data[i] << 8); p_ancs_link->m_parse_state = DS_PARSE_ATTRIBUTE_READY; p_ancs_link->ptr = p_ancs_link->notification_attr.data; p_ancs_link->current_len = 0; #if F_BT_ANCS_CLIENT_DEBUG APP_PRINT_INFO2("parse notify attr: attribute_id %d, attribute_len %d", p_ancs_link->notification_attr.attribute_id, p_ancs_link->notification_attr.attribute_len ); #endif //attribute data case DS_PARSE_ATTRIBUTE_READY: *p_ancs_link->ptr++ = p_data[i]; p_ancs_link->current_len++; if (p_ancs_link->current_len == p_ancs_link->notification_attr.attribute_len) { /*An attribute is always a string whose length in bytes is provided in the tuple but that is not NULL-terminated.*/ *p_ancs_link->ptr++ = 0; #if F_BT_ANCS_CLIENT_DEBUG APP_PRINT_INFO1("parse notify attr: data %b", TRACE_BINARY(p_ancs_link->notification_attr.attribute_len, p_ancs_link->notification_attr.data)); #endif app_handle_notification_attribute_data (p_ancs_link); } break;
-
Use
app_handle_notification_attribute_data()to further parse and display content, such as title, body, etc.// Different attributes can display different data information typedef enum { DS_NOTIFICATION_ATTR_ID_APP_IDENTIFIER = 0, /**< Format: UTF-8 strings */ DS_NOTIFICATION_ATTR_ID_TITLE = 1, /**<Format: UTF-8 strings. Needs to be followed by a 2-bytes max length parameter */ DS_NOTIFICATION_ATTR_ID_SUB_TITLE = 2, /**<Format: UTF-8 strings. Needs to be followed by a 2-bytes max length parameter */ DS_NOTIFICATION_ATTR_ID_MESSAGE = 3, /**<Format: UTF-8 strings. Needs to be followed by a 2-bytes max length parameter */ DS_NOTIFICATION_ATTR_ID_MESSAGE_SIZE = 4, /**<The format of the DS_NOTIFICATION_ATTR_ID_MESSAGE_SIZE constant is a string that represents the integral value of the message size. */ DS_NOTIFICATION_ATTR_ID_DATE = 5, /**<The format of the DS_NOTIFICATION_ATTR_ID_DATE constant is a string that uses the Unicode Technical Standard (UTS) #35 date format pattern yyyyMMdd'T'HHmmSS. */ DS_NOTIFICATION_ATTR_ID_POSITIVE_ACTION_LABEL = 6, /**<Format: UTF-8 strings. The label used to describe the positive action that can be performed on the iOS notification. */ DS_NOTIFICATION_ATTR_ID_NEGATIVE_ACTION_LABEL = 7, /**<Format: UTF-8 strings. The label used to describe the negative action that can be performed on the iOS notification. */ DS_NOTIFICATION_ATTR_ID_RESERVED = 255 } T_DS_NOTIFICATION_ATTR_ID;
Note
The type of data information displayed can be determined based on attribute_id.
-
-
App Attribute Parsing
Call
app_parse_app_attribute()to parse the app attribute data in the Data Source, similar logic toapp_parse_notification_attribute().
-
-