ANCS 使用说明
ANCS 简介
ANCS 是 iOS 设备的消息通知服务协议,允许蓝牙配件(如手表)接收和管理 iOS 端的通知,实现跨设备的通知提醒与交互。详细信息可参见: Apple 通知中心服务(ANCS)规范
ANCS 包含三个核心特征,分别负责不同的数据交互功能:
-
Notification Source 主要用于推送通知的基本信息,包括通知的到达、更新与移除等事件。推送内容包括(按顺序):
EventID:事件类型(新通知、已修改、已移除等)
EventFlags:通知状态位(如是否高优先、静音等)
CategoryID:消息分类(如来电、短信、日历等)
CategoryCount:同类通知计数
NotificationUID:通知唯一标识(用以后续查询详情)
Control Point 客户端(手表)通过写命令方式与 iOS 设备交互,是获取通知属性、App 属性和执行操作的入口。
-
Data Source iOS 设备用于主动下发详细内容或操作结果的通道,常见包括:
通知属性详细内容:手表通过 Control Point 发起“获取通知详情”命令后,iOS 通过 Data Source 返回所需字段内容。
App 属性信息:手表通过 Control Point 发起命令后,iOS 通过 Data Source 返回 App 的属性(如名称)。
通知操作反馈:手表向 Control Point 发起执行操作命令(如清除、打开)后,iOS 通过 Data Source 返回操作结果。
基于 SDK 的 ANCS 调用流程
-
在
app_ble_service_init()
中添加 ANCS 服务初始化-
调用
ancs_init()
和client_init()
完成 ANCS 客户端初始化。client_init(1); ancs_init(1);
-
-
在
watch_handle_io_message()
中添加 ANCS 消息类型处理-
支持 ANCS 消息分发,调用
ancs_handle_msg()
。#if F_BT_ANCS_CLIENT_SUPPORT case IO_MSG_TYPE_ANCS: { ancs_handle_msg(p_watch_msg); } break; #endif
-
-
在
app_handle_authen_state_evt()
中添加 ANCS 服务发现-
配对/认证完成后,通过
ancs_start_discovery()
触发 ANCS 服务发现。#if F_BT_ANCS_CLIENT_SUPPORT ancs_start_discovery(conn_id); #endif
-
-
发现服务后,在
ancs_client_cb()
订阅 Notification Source 和 Data Source-
订阅 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;
-
订阅 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;
-
-
订阅成功后,解析 Notification Source 数据
-
手机端推送 Notification Source,使用
app_parse_notification_source_data()
解析内容。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;
-
在解析函数内可根据 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: slient %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 );
-
-
解析 Data Source 内容
-
收到所需 Data Source 数据后,使用
app_parse_data_source_notifications()
进一步处理。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_source_notifications(conn_id, p_cb_data->cb_content.notify_data.p_value, p_cb_data->cb_content.notify_data.value_size); break;
-
可在
app_parse_data_source_notifications()
获取完整数据并根据解析状态选择处理流程。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 //根据 m_parse_state 来判断是进行 notification_attribute 的解析,还是进行 app_attribute 解析 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()
会根据 m_parse_state 区分两类解析流程:-
Notification Attribute 解析
-
调用
app_parse_notification_attribute()
,分步骤解析 command_id、notification_uid、attribute_id、attribute_len 及 attribute_data。//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;
-
用
app_handle_notification_attribute_data()
进一步解析展示内容,如标题、正文等。//不同 attribute 可以展示出不同的数据信息 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;
备注
可根据 attribute_id 判断展示的数据信息类型。
-
-
App Attribute 解析
调用
app_parse_app_attribute()
对 Data Source 中的 app 属性数据进行解析,逻辑与app_parse_notification_attribute()
类似。
-
-