irpas技术客

蓝牙开发之 IOS ANCS_nixgnauhcuy_蓝牙ancs

大大的周 5842

目录 什么是 ANCSANCS 介绍(Introduction)ANCS 字节序和字节码(Endianness and String Encoding)依赖性(Dependencies)术语(Terminology) ANCS 理解ANCS 特征(Characteristic)通知源(Notification Source)iOS 通知的生命周期 控制点(Control Point)和数据源(Data Source)获取通知属性获取应用程序属性 执行通知操作通知动作 其他会话(Session)属性获取和缓存(Attribute Fetching and Caching)错误代码(Error Codes) 示例结语

个人博客

所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 nixgnauhcuy’s blog!

如需转载,请标明出处!

2022-04-12:更新 蓝牙开发之 IOS AMS,有兴趣的可以点击链接进入~

2022-04-08:更新接收推送效果 上图演示了,接收微信通知和 QQ 通知的展示,短信来电等我就不一一展示了。

什么是 ANCS ANCS 介绍(Introduction)

ANCS 全称 Apple Notification Center Service(苹果通知中心服务),是提供给 BLE 设备的一种获取 IOS 设备通知的方法。ANCS围绕三个原则进行设计:简单,高效和可扩展性。

以下引用自 ANCS 规范

The purpose of the Apple Notification Center Service (ANCS) is to give Bluetooth accessories (that connect to iOS devices through a Bluetooth low-energy link) a simple and convenient way to access many kinds of notifications that are generated on iOS devices.

The ANCS is designed around three principles: simplicity, efficiency and scalability. As a result, accessories ranging from simple LEDs to powerful “companion” devices with large displays can find the service useful.


ANCS 字节序和字节码(Endianness and String Encoding)

除非另有规定,否则通过 ANCS 传输的所有数值都应是小端格式。 除非另有规定,否则通过 ANCS 传输的所有字符串值都应是 UTF-8 编码的 unicode 字符所组成的。


依赖性(Dependencies)

除了标准的通用属性配置文件(GATT)子程序集外,ANCS 没有任何依赖性。作为 GATT 客户端的设备在使用 ANCS 时,可以自由访问和使用 iOS 设备提供的其他服务。

术语(Terminology)

苹果通知中心服务应称为 ANCS。

ANCS 服务的发布者(也就是我们的 iOS 设备)应被称为通知提供者(NP)。

ANCS 服务的任何客户端(也就是我们的蓝牙设备)应被称为通知消费者(NC)。

在 iOS 通知中心显示在 iOS 设备上的通知,应称为 iOS 通知。

GATT 特性作为异步消息发送的通知,应称为 GATT 通知。


ANCS 理解

上面的对于什么是 ANCS,基本上都是其 ANCS 规范中所提及的,不过不能在水文水下去了(在水下去怕读者打我😄),我们来谈谈对它的理解。

首先 ANCS 最重要的是它的 N 和 S,它告诉了我们,它是一个通知服务,它是基于 GATT 封装的服务,和我们的 nus、hrs 等服务都是一个档次的,只不过它与 IOS 和设备间制定了自己的一套协议,我们要使用它就必须遵守它们之间的规则来办事。

那为什么我们用 nrf connect、LightBlue 等蓝牙工具没办法看到 ANCS 相关的UUID呢?👉这就和 IOS 的机制有关了,IOS 对安全的考虑比安卓要严格的多,IOS 是沙盒机制,限制了每个应用不得翻越自己的围墙去访问别的存储空间的内容,即使能访问也需要授权才可以,所以接触的 IOS 工程师告诉我,他们没办法通过蓝牙去获取其他应用的通知,所以 IOS 提供了这样一个通知服务,但是它是私有的,并不会主动的广播,所以我们用蓝牙工具是看不到这个服务的,并且如果你也是蓝牙的开发者并且你也在折腾 ANCS,那么要清楚,IOS 工程师并不需要在他们的代码中处理 ANCS 相关的代码,我们在蓝牙中做好对 ANCS 的支持和响应,剩下的就是我们蓝牙设备与 IOS 设备的事了。(IOS 开发的工程师们,如果觉得我理解不到位,请轻点喷我,并麻烦指正我的错误😭,感激不尽🙏!)

ANCS 主服务(Service) UUID 为 7905F431-B5CE-4E99-A40F-4B1E122D00D0

ANCS 服务下的三个特征(Characteristic) UUID: 通知源(Notification Source) UUID: 9FBF120D-6301-42D9-8C58-25E699A21DBD(notifiable) 控制点(Control Point) UUID: 69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9(writeable with response) 数据源(Data Source) UUID: 22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB(notifiable)

上述特征都需要授权才能访问,并且通知源特性是必须要有的,而其他两个控制点和数据源是可选的。ANCS中存在的特征可能比上面列出的三个更多。也就是说,NC 可以忽略任何它不识别的特性。一个 NP 上只能存在一个 ANCS 的实例。由于 iOS 的特性,不能保证 ANCS 始终存在。因此,NC 应寻找并订阅 GATT 服务的 Service Changed 特性,以便随时监控 ANCS 可能的发布和未发布的消息。

这里再扩展一下个人见解,我们实际操作蓝牙服务时都是操作其句柄,这与文件系统类似,打个比方,ANCS 的主服务我们看作文件系统的磁盘,ANCS 的三个特征我们把它看作文件系统的文件例如 a.txt、b.txt、c.txt 三个文件,那么 ANCS 的属性我们把它看作对 a.txt 文件操作时的读和写的属性,是不是极其地相似,我们 open 磁盘后再 open 文件,用获得的句柄对这个文件就有了可读可写的属性,就可以对 txt 文本中写入内容和读出内容。这点是不是和蓝牙服务这一块极其相似。(当然,这只是我个人的观点,方便各位理解,不一定对,如果有误😭,麻烦指正,不胜感激🙏!)


ANCS 特征(Characteristic) 通知源(Notification Source)

Notification Source 这个特征值是用于通知我们的蓝牙设备这一消费者(NC),下面则是得到通知的场景:

NP 上有新的 IOS 通知NP 上修改了 IOS 通知NP 上删除了 IOS 通知

一旦 NC 监控了通知源特征,那么 GATT 通知就可以立即发送。因此,我们在监控这个特征之前,应该处于可以正确接收和处理这些消息的状态。

通过通知源特征传递的 GATT 通知包含以下信息:

EventID:此字段通知设备是否添加、修改或删除了给定的 iOS 通知。此字段的枚举值在EventID Values中定义。

EventID Values:

EventIDNotificationAdded = 0 - iOS 通知已添加EventIDNotificationModified = 1 - iOS通知已修改EventIDNotificationRemoved = 2 - iOS通知已删除 EventFlags:一个位掩码,其设置位通过 iOS 通知将其特殊性通知给 NC。例如,如果将 iOS 通知视为“重要”,则 NC 可能希望显示一个更积极的用户界面(UI),以确保正确警告用户。此字段的枚举位在 EventFlags 中定义。

EventFlags:

EventFlagSilent = (1 << 0) - 通知的优先级较低EventFlagImportant = (1 << 1) - 通知具有较高的优先级EventFlagPreExisting = (1 << 2) - 该通知已存在EventFlagPositiveAction = (1 << 3) - 通知具有可以采取的积极行动EventFlagNegativeAction = (1 << 4) - 通知具有可以采取的负面行动 CategoryID:一个数值,提供对 iOS 通知进行分类的类别。NP 将尽最大努力为每个iOS 通知提供准确的类别。此字段的枚举值在 CategoryID 值中定义。

CategoryID:

CategoryIDOther = 0 - iOS 通知属于“其他”类别CategoryIDIncomingCall = 1 - iOS 通知属于“来电”类别CategoryIDMissedCall = 2 - iOS 通知属于“未接电话”类别CategoryIDVoicemail= 3 - iOS 通知属于“语音邮件”类别CategoryIDSocial = 4 - iOS 通知属于“社交”类别CategoryIDSchedule = 5 - iOS 通知属于“时间表”类别CategoryIDEmail = 6 - iOS 通知属于“电子邮件”类别CategoryIDNews = 7 - iOS 通知属于“新闻”类别CategoryIDHealthAndFitness = 8 - iOS 通知属于“健康和健身”类别CategoryIDBusinessAndFinance = 9 - iOS 通知属于“商务和金融”类别CategoryIDLocation = 10 - iOS 通知属于“位置”类别CategoryIDEntertainment = 11 - iOS 通知属于“娱乐”类别 CategoryCount:给定类别中当前活动的 iOS 通知数。例如,如果用户的电子邮件收件箱中有两封未读电子邮件,并且有一封新电子邮件被推送到用户的 iOS 设备,则 CategoryCount 的值为3。NotificationUID:一个 32 位数值,它是iOS通知的唯一标识符(UID)。该值可用作发送到“控制点”特征以与 iOS 通知进行交互的命令中的句柄。

根据上边对通知源特征的解析,我们可以应用于手环手表,显示当前手机的通知信息,并且过滤我们需要的通知类别等作用。


iOS 通知的生命周期

通过 NP 生成的通知源特性 GATT 通知的格式,我们可以隐式推断出 iOS 通知的生存期,如下图:

从图里可以看出通知的生命周期,当我们 IOS 手机收到通知后会触发 Notification Added,如果不主动点开,它将一直保留在通知列表中,这个时候如果该通知是可以被修改的,那么当你修改了该通知后,将触发 Notification Modified,但也有一些是不可修改的,那么改事件不一定会被触发,但是删除是必定的,当你点开通知或者通知刚来你就点开了,那么就会触发 Notification Removed,那么这个通知的生命就到了尽头。所以为了爱护通知的生命,你可以一直不关它,让它存活在你的通知列表里,hhhh😄。


控制点(Control Point)和数据源(Data Source)

NC 可能要与 iOS 通知进行交互。它可能希望获取和它有关的更多信息,包括其内容,或者可能要对其执行操作。那么这些属性的检索是通过控制点和数据源特征执行的。

NC 可以通过将特定命令写入控制点特征来发出检索有关 iOS 通知的更多信息的请求。如果对控制点特征写入成功,NP 将通过数据源特征上的 GATT 通知流得到响应。

NC 可以通过将特定命令写入控制点特征来对 iOS 通知执行预定动作。有关操作和 iOS 通知的更多信息,请参见“执行通知操作”。


获取通知属性

使用“获取通知属性”命令,NC 可以检索特定 iOS 通知的属性。“获取通知属性”命令的格式如图所示。

获取通知属性命令的格式包含以下信息:

CommandID:应设置为 0(CommandIDGetAppAttributes)。NotificationUID: 32 位数值,代表客户端需要其信息的 iOS 通知的 UID。AttributeIDs: NC 想要检索的属性列表。某些属性后可能需要一个 16 位长度的参数,该参数指定 NC 希望检索的属性的最大字节数。

使用了获取,那么必然有响应,“获取通知属性”的响应的格式如图所示。

获取通知属性响应的格式包含以下信息:

CommandID:应设置为 0(CommandIDGetAppAttributes)。NotificationUID: 32 位数值,是以下属性对应的 iOS 通知的 UID。AttributeList: AttributeID / 16 位长度 / Attribute 元组的列表。属性始终是一个字符串,该字符串的长度(以字节为单位)在元组中提供,但不以 NULL 终止。如果 iOS 通知的请求属性为空或缺失,则其长度设置为 0。元组始终与“获取通知属性”命令的 AttributeID 顺序相同。。
获取应用程序属性

“获取应用程序属性”命令允许 NC 检索安装在 NP 上的特定应用程序的属性。“获取应用程序属性”命令的格式如图所示。

获取应用程序属性的格式包含以下信息:

CommandID:应设置为1(CommandIDGetAppAttributes)。AppIdentifier:客户端需要有关其信息的应用程序的字符串标识符。该字符串必须以 NULL 终止。AttributeIDs: NC 想要检索的属性列表。

同样的,使用了获取,那么必然有响应,“获取应用程序属性”的响应的格式如图所示。

其响应的格式则包含以下信息:

CommandID:应设置为1(CommandIDGetAppAttributes)。AppIdentifier:以下属性对应的应用程序的字符串标识符。该字符串以 NULL 终止。AttributeList: AttributeID / 16 位长度 / Attribute 元组的列表。属性始终是一个字符串,该字符串的长度(以字节为单位)在元组中提供,但不以 NULL 终止。如果应用程序的请求属性为空或缺失,则其长度设置为 0。元组始终与“获取应用程序属性”命令的 AttributeID 顺序相同。

👉注意:上面无论是通知属性还是应用程序属性如果响应大于协商的 GATT 最大传输单位(MTU),则 NP 将其分为多个片段。NC 必须通过拼接每个片段来重组响应。当收到每个请求属性的完整元组时,响应完成。

上面这两个获取通知属性和应用程序属性有什么作用呢?当我们收到通知源特征反馈给我们的信息时,我们只能知道它属于那类别的信息,但是我们不能知道他们具体的内容是什么,但是通过这两个获取接口,我们可以获取相应的通知属性,拿微信💬举例,我们可以获取其发送的通知的用户名和内容,这样我们就可以显示在我们的手环或者手表上。


执行通知操作

执行通知动作命令允许 NC 对特定的 iOS 通知执行预定的动作。执行通知操作命令包含以下字段:

字节数名称描述1CommandID设定为2(CommandIDPerformNotificationAction)2-5NotificationUID32位数值,代表客户端要在其上执行操作的 iOS 通知的 UID。6ActionIDNC 希望对 iOS 通知执行的所需操作。

发出此命令时,无论成功与否,都不会在数据源特征上生成任何数据。

通知动作

从 iOS 8.0 开始,NP 可以将与 iOS 通知关联的潜在操作通知 NC。然后,NC 可以代表用户请求 NP 执行与特定 iOS 通知关联的操作。 通过检测 EventFlags 由通知源特征生成的 GATT 通知字段中是否存在设置标志,可以向 NC 通知有关 iOS 通知的可执行操作:

EventFlagPositiveAction: 存在一个积极的动作,并且与此 iOS 通知相关联。EventFlagNegativeAction: 存在一个负面的动作,并且与此 iOS 通知相关联。

NP 代表 NC 执行的实际操作由 NP 决定,并且取决于它们执行的 iOS 通知而有所不同。例如,对来电通知执行肯定动作可能会应答,而执行否定动作可能会拒绝。

NC 既不能假设也不能试图预先猜测在 iOS 通知上执行的确切操作,因为这些操作是基于其无法获得的信息(例如,NP 实现的 ANCS 版本)。NP 保证积极和负面的行动与不会令用户感到意外的结果相关联。

如果存在于 iOS 通知中,则正面和负面的动作可能会以复选标记,X 标记或通常与确认和关闭相关的颜色(例如绿色和红色)向用户表示。

NC可以通过检索 iOS 8.0 中引入的新通知属性来检索简要描述与 iOS 通知关联的实际操作的标签:

NotificationAttributeIDPositiveActionLabel:用于描述可以对 iOS 通知执行的积极操作的标签。NotificationAttributeIDNegativeActionLabel:用于描述可以对 iOS 通知执行的负面操作的标签。

通知操作,可以用于我们手表手环来接听或者挂断当前 IOS 设备的来电📱。


其他 会话(Session)

ANCS 会话在 NC 订阅 NP 上的通知源特性时开始,在 NC 取消订阅同一特性或断开与 NP 的连接时结束。由于 ANCS 不是被设计成一个完整的同步服务,因此它不会跟踪各会话的状态。因此,NC 和 NP 之间交换的所有标识符(如 NotificationUID 和 AppIdentifier)和所有数据仅在特定会话中有效。

当某一特定会话结束时,NC 应删除其在该会话期间收集和存储的任何标识符和数据。当一个新地会话开始时,NP 会尽力告知 NC 系统上任何现有的 iOS 通知。NC 可以使用这些信息来构建一个模型,用于会话的剩余部分。


属性获取和缓存(Attribute Fetching and Caching)

强烈建议 NC 只在需要时才获取属性,或者是为了响应用户的操作。例如,如果一个 NC 选择在一个简单的列表中显示活动的 iOS 通知,并且在用户选择时只显示特定 iOS 通知的详细信息,那么这个 iOS 通知的属性的检索可以被延迟的触发。

在会话过程中,强烈建议 NC 为它遇到的每个应用标识符建立一个 App Attributes 的缓存。建立这个缓存可以让 NC 避免多次检索相同地不可改变的 App 属性,从而节省时间并保护电池。


错误代码(Error Codes)

写入控制点特性时,NC 可能会收到以下特定于 ANCS 的错误代码:

未知命令 (0xA0): NP 无法识别 commandID。无效的命令 (0xA1): 该命令的格式不正确。无效的参数 (0xA2): 参数之一(例如 NotificationUID)未引用 NP 上的现有对象。操作失败 (0xA3): 未执行操作。

如果 NP 响应错误,它将不会在数据源特性上为相应命令生成任何 GATT 通知。


示例

这是 ANCS 规范上的示例,拿来分析下。

当设备(NC)连接配对了手机(NP)之后,设备发现 ANCS 服务和特征,我们需要订阅通知源并且可以根据自己的需求来选择是否需要控制点和通知源。当手机上有新的通知时,会通过通知源通知我们的设备。

同样的当设备(NC)连接配对了手机(NP)之后,设备发现 ANCS 服务和特征并且订阅了相应的特征值。当手机接收到来电时,会发送该通知的 UID(图上标识了)和其他信息(如,消息类别等,图上未标识)给设备。我们可以根据自己的需要,当需要获取通知的具体内容时,我们可以通过获取通知属性的指令来获取相应通知的内容。图上获取属性为 Title、Message、AppID,手机收到后,同样地会返回相应的通知属性给设备。我们可以根据反馈的内容,对这些内容进行处理(例如显示等)。


结语

关于蓝牙的 IOS ANCS 的总结就分享到这里了,如果在文中发现有误,欢迎指出,我会及时修改的,提前感谢🙏!ANCS 规范其实已经总结的很清楚了,有兴趣的小伙伴也可以看看!



1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #蓝牙ancs #蓝牙开发 #ancs #相关总结