如何从手机层面分析ANCS消息通知问题

8,774 阅读9分钟

背景

  1. 分析通知不可达时候,“三板斧”:检测手表和手机是否连接、通知开关是否打开、手表是否配对(系统设置是否允许通知);无法继续分析。
  2. 上述 1 操作之后,提供给固件端同学查看,多数情况以“手机没有推给手表”为理由,无法继续分析。
  3. QA复现出一个通知不可达的场景,我们无法有效分析内部问题,还是继续上述 1 和 2操作,时不时破坏手机环境导致问题被跳过,没有真正找到相关原因。

分析流程

提供一种灰盒的方式来分析消息通知问题,流程如下:

  1. 用户遇到问题,首先触发诊断手机日志 (eg. sysdiagnose_2023.05.09_16-01-38+0800_iPhone-OS_iPhone_20C65.tar.gz),上传诊断日志(详细操作步骤见下面)

  2. 使用控制台打开 system_logs.logarchive 日志,过滤 BTLEServer 以及 bluetoothd 两个主要进程信息。

    1. 首先检查BTLEServer数据源是否收到 ANCAlertSource变化

      1. 如果没有收到,先检查NC以及Springboard有没有收到来自apsd推送的消息
      2. 如果NC和Springboard收到消息,那么数据源订阅存在问题,可以关键字过滤subscribe以及 ANCS Authorization Manager 等,确认ANCS特征订阅是否存在问题
      3. 上述均没有,基本判断apsd未收到来自apple的推送
    2. 收到ANCAlertSource数据源,再次检查当前手机状态

      1. 主要检查DND,suppressionState等状态,或者结合visible字段以及isSilent字段查看
    3. 上述正常后,检查BTLEServer有无 Added ** notification记录

    4. 此时通知数据已准备完毕,检查bluetoothd进程是否发送 sending notification to <device>

  3. 对于iOS系统黑盒的情况下,可以结合下面反汇编的 os log记录来筛选当前手机对于消息通知的操作情况。

可分析出来的结果

  1. 手表和手机ANCS授权订阅数据源过程以及error日志
  2. 消息通知如何流程发送到手表的 (基本流程: apsd -> BTLEServer -> bluetoothd -> 手表) ,可以排除类似“手机没有推给手表”的问题
  3. 当前手机状态是否允许通知 (eg. DNDMode, 或者其他消极状态, 是否静默推送)
  4. 【延伸】连接问题、配对问题、蓝牙抓包数据均可分析,借助反汇编的os log日志记录

详细操作

如何上传手机诊断日志

不同于手机在正常使用过程中产生的Crash Log(崩溃日志)会被日志系统自动保存记录。Sysdiagnose(系统诊断日志)不是由操作系统自动记录的,必须由用户手动触发才可生成。

推荐方式(辅助触控)

借助手机端的“辅助触控”功能生成系统诊断日志。具体的操作步骤需在手机端完成,具体如下:

  1. 设置-辅助功能-触控-轻点两下-分析。当“触控”功能被激活后桌面上会出现辅助触控的“小圆点”。此时可点击两次系统便会自动收集诊断日志并储存。
  1. 触发后等待约几分钟,看到如下情况代表诊断完毕.
  1. 在系统设置-> 隐私与安全 -> 分析与改进 -> 分析数据 中找到 sysdiagnose 开头文件,分享上传即可.

备选方式(组合按键)

手机开机状态下,同时按住音量上+音量下+电源键持续一段时间(iOS sysdiagnose 最多可能需要 10 分钟才能完成。),当感觉到手机发出震动的时候松开全部按键即可生成系统诊断日志。当然一次尝试有可能会失败,需反复重复以上操作直至成功。iOS_Logs/sysdiagnose_Logging_Instructions.pdf

[拓展] 诊断日志TCC文件

TCC.db为SQLite数据库文件,用于记录应用程序权限。比如微信在当前系统下为其赋予的权限,如读取电话本、蓝牙、FaceID、相机等权限。


如何查看和分析诊断日志(以消息通知为例)

下面是正常推送情况,异常情况case较多,后续可以参考反汇编os log日志记录来分析。

申明:

  1. 这里主要以 BTLEServer 以及 bluetoothd 两个主要进程为例,从数据源订阅到状态监测,再到最终推送的流程。
  2. 其他进程如 SpringBoard 或者 runningboardd 进程可辅助分析,再或者可以直接使用待分析的APP包名过滤信息来辅助分析 (eg. com.bytedance.ee.lark)
  3. 如果遇到其他异常情况,可以参考下面反汇编的os log日志记录来相应分析。

注意:

部分系统日志输出 <private> 原因如下:

eg. os_log(log, "Message: %s", dynamicString);

动态字符串format不添加public的情况下,根据日志系统的规则会打印出 <private>

基于iOS14+日志分析

下面是一次正常推送消息的流程,只展示了BTLEServer数据源监听服务,以及bluetoothd蓝牙设备通信进程

存在推送不到达的case包括:

  1. 没有接收到apsd数据源
  2. 检测手机状态不正常而没有成功添加通知数据
  3. 蓝牙设备通信进程没有发送 等情况

/* 监听到ANCAlertSource数据源变化 */
默认        17:54:21.485452+0800        BTLEServer        Observer for ANCAlertSource: Delivering ADD bulletin: 6A3996A0-338F-46AB-97CB-143FDA00D0D0
默认        17:54:21.597829+0800        BTLEServer        [com.apple.BTLEServer.ANCS:FCC79084-A831-499C-8FFB-905587ED265A] Processing state request
/* 检测当前手机状态是否允许添加通知数据;主要见suppressionState以及visible状态; iOS16表达方式有差异,会显示一些额外的属性,因为从iOS15开始支持专注模式 */
默认        17:54:21.597882+0800        BTLEServer        [com.apple.BTLEServer.ANCS:FCC79084-A831-499C-8FFB-905587ED265A] Got current state, state=<DNDState: 0x10504bf90; suppressionState: inactive; activeModeAssertionMetadata: (
); userVisibleTransitionDate: 4001-01-01 00:00:00 +0000; userVisibleTransitionLifetimeType: none>
/* 状态检测无误,添加通知数据;iOS14中除了自带的电话,短信,邮件等,默认是 Other ,后面接的数字代表是第几条通知 */
默认        17:54:21.626373+0800        BTLEServer        Added "Other" notification #4 (<private>) from <private>
默认        17:54:21.717959+0800        BTLEServer        Received "Get Notification Attributes" command for notification #4
/* 开始触发蓝牙通信进程对蓝牙设备消息推送 */
默认        17:54:21.718681+0800        bluetoothd        Sending XPC message "CBMsgIdSetAttributes" to session "com.apple.BTLEServer-peripheral-174-9"
默认        17:54:21.718718+0800        bluetoothd        Received XPC message "CBMsgIdRespondToRequest" from session "com.apple.BTLEServer-peripheral-174-9"
默认        17:54:21.718784+0800        bluetoothd        Received XPC message "CBMsgIdSendValueNotification" from session "com.apple.BTLEServer-peripheral-174-9"
/* 推送给蓝牙设备的日志,截止此处手机推送给设备流程正常。 目前iOS14中不展示蓝牙设备的uuid,但是iOS16中,uuid作为 {public} 属性开放展示了 */
默认        17:54:21.718841+0800        bluetoothd        Sending notification to device "<private>"

基于iOS16+日志分析


/* 监听到ANCAlertSource数据源变化 */
默认        11:33:25.893517+0800        BTLEServer        Observer for ANCAlertSource: Delivering ADD bulletin: AC78006D-8F1E-4A01-8609-7B0C7B8545CC
/* 接收到通知数据部分属性 */
默认        11:33:25.897814+0800        BTLEServer        [com.apple.BTLEServer.ANCS:6247AE7C-17A7-4FF6-880C-1A1FA9EB3E28] Resolved event, details=<DNDMutableClientEventDetails: 0xa1091e0b0; identifier: 'lark_message_7231020703674613761'; bundleIdentifier:: com.bytedance.ee.lark; type: Direct-Message; urgency: Default; sender: <DNDMutableContactHandle: 0xa1092bf30; contactIdentifier: (null); type: unknown; value: 406542428ACB1FC3;>; threadIdentifier: 1EE943ACF4B0C644; filterCritera: (null); notifyAnyway: 0; behavior: Default> behavior=<DNDClientEventBehavior: 0xa10d1ff60; eventDetails: <DNDClientEventDetails: 0xa10d42020; identifier: 'lark_message_7231020703674613761'; bundleIdentifier:: com.bytedance.ee.lark; type: Direct-Message; urgency: Default; sender: <DNDMutableContactHandle: 0xa10daf210; contactIdentifier: (null); type: unknown; value: 406542428ACB1FC3;>; threadIdentifier: 1EE943ACF4B0C644; filterCritera: (null); notifyAnyway: 0; behavior: Default>; interruptionSuppression: none; resolutionReason: disabled; activeModeUUID: (null)>
/* 检测状态 DND 以及是否 silent静默 */
默认        11:33:25.897969+0800        BTLEServer        appIdentifier <private> DNDSuppress=none isSilent=N
/* 此处和iOS14 的Other有区别,应该是iOS16中对于第三方APP做了分类 */
默认        11:33:25.898221+0800        BTLEServer        Added "Business & Finance" notification #345 (<private>) from <private>
/* 开始发送给蓝牙设备 */
默认        11:33:25.898730+0800        bluetoothd        Received XPC message "CBMsgIdSendValueNotification" from session "com.apple.BTLEServer-peripheral-11372-522"
/* 此处发送给指定订阅的蓝牙设备,展示了uuid */
默认        11:33:25.899127+0800        bluetoothd        Sending notification to device "C3994E16-FE3F-6A41-BD14-380C87EFCB11"

相关反汇编进程os log日志记录

MachO进程文件来源于 iPhoneSE iOS14.4 越狱手机,文件路径 /usr/sbin/**

相关系统日志记录

  1. bluetoothd
// 有选择的删减

Sending ancsAuthChanged to %d for device %@
statedump: \t\t         \toptions: connectAlrts:%c disconAlrts:%c notifAlrts:%c delay:%lu ObjDisc:%c TrsprtBrdge:%c noLeGATT:%c ANCS:%c hide:%c
Device '%@' has the tag 'RequiresANCSAuth', Check if the current bundleIdentifier is present in the device's bundle identifier list
Device '%@' does not have the tag 'RequiresANCSAuth', not adding it to the authorization queue.
Received 'deviceSubscribedForANCSNotification' listener for device '%@'
Device '%@' already has the tag 'RequiresANCSAuth'
Device '%@' does not have the tag 'RequiresANCSAuth'. Setting the tag.
Received 'appRequestedANCSAuthorizationForDevice' listener for device '%@' and bundleIdentifier '%@'
deviceSubscribedNotification Notifying listeners that %s is interested in %@
Display ANCS Authorization alert for %@ and bundle identifier '%@'
User responded with unknown response. Not unsetting tag 'RequiresANCSAuth'. Will ask again later
Push AWD stats ANCS for %{public}s : triggerLoc: %u, prevPermission: %u, currPermission: %u, deviceCategory = %u, connectedTransport = %u
statedump: ---------------- ANCS Authorization Manager ----------------
statedump:    RequiresANCSAuth: %s
statedump:    ANCSAuthorized: %s
statedump:    ANCSUnauthorized: %s
Device %@ has subscribed to ANCSNotificationSource


Sending pair stauts response to magnet device for aacp device "%s", pairStatus = "%s"
sending pair status request to "%s" for magnet device "%s" 
sending pair status request to "%s"  for aacp device "%s" 
sending pair status request to magnet for device "%s"
sending pair status request to AACP device "%s"
sending store link key request to magnet link for device "%s" with version %d
sending store link key request to AACP device "%s"
  1. BTLEServer
After subscription from %@ for ANCS, active centrals count %ld
Central %@ unsubscribed from ANCS. Active centrals count %ld
Central %@ ancsAuthorization changed to %d. Active centrals after %@ %ld
Central %@ subscribed to NotificationSourceCharacteristic
Central %@ unsubscribed from ANCS. Active centrals count %ld
Central "%@" is now subscribed to characteristic "%@"
Central "%@" is now unsubscribed from characteristic "%@"

Received "Get Notification Attributes" command for notification #%u
Received "Perform Notification Action" command for notification #%u
%@ "%@" notification #%u (%@) from %@


Sending 'connect classic device' message for device "%@"
Sending 'disconnect classic device' message for device "%@"
Sending 'unexpected disconnection' message for device "%@"
Sending 'classic pair state request' message for device "%@"
Sending 'classic pair state response' message for device "%@"
Sending 'store classic link key request' message for device "%@"
Sending 'store classic link key request extended' message for device "%@"
Sending 'store classic link key response' message for device "%@"
Sending 'store classic device settings' message for device "%@", devName = "%@", doubleTap = %@
Sending 'notifiy primary bud side' message for device "%@"
Sending 'LEA easy pair request' message for device "%@"
Sending 'LEA easy pair response' message for device "%@"
Sending 'LEA store bonding info request' message for device "%@"
Sending 'LEA store bonding info response' message for device "%@"

伪代码展现

关键的 BTLEServer 其中的两个类 ANCAlertSourceANCService

// 开启关闭方法
-[ANCService startNotifications]        
-[ANCService stopNotifications]

// 接收ANCS订阅代理
-[ANCService peripheralManager:didReceiveWriteRequests:]
-[ANCService peripheralManager:central:didSubscribeToCharacteristic:]
-[ANCService peripheralManager:central:didUnsubscribeFromCharacteristic:]
-[ANCService peripheralManager:central:didUpdateANCSAuthorization:]

// 收到消息通知
-[ANCService alertAdded:isPreExisting:]
-[ANCService alertModified:]
-[ANCService alertRemoved:]
-[ANCAlertSource isSilent]

{
  id v2; // x0
  id v3; // x19
  unsigned __int8 v4; // w20

  v2 = objc_msgSend(self->_dndStateService, "queryCurrentStateWithError:", 0LL);
  v3 = objc_retainAutoreleasedReturnValue(v2);
  v4 = (unsigned __int8)objc_msgSend(v3, "isActive");
  objc_release(v3);
  return v4;
}

// 结合上述诊断日志中: 
// Observer for ANCAlertSource: Delivering ADD bulletin: AC78006D-8F1E-4A01-8609-7B0C7B8545CC 来看伪代码(下面伪代码有精简)