背景
- 分析通知不可达时候,“三板斧”:检测手表和手机是否连接、通知开关是否打开、手表是否配对(系统设置是否允许通知);无法继续分析。
- 上述 1 操作之后,提供给固件端同学查看,多数情况以“手机没有推给手表”为理由,无法继续分析。
- QA复现出一个通知不可达的场景,我们无法有效分析内部问题,还是继续上述 1 和 2操作,时不时破坏手机环境导致问题被跳过,没有真正找到相关原因。
分析流程
提供一种灰盒的方式来分析消息通知问题,流程如下:
-
用户遇到问题,首先触发诊断手机日志 (eg.
sysdiagnose_2023.05.09_16-01-38+0800_iPhone-OS_iPhone_20C65.tar.gz
),上传诊断日志(详细操作步骤见下面) -
使用控制台打开
system_logs.logarchive
日志,过滤 BTLEServer 以及 bluetoothd 两个主要进程信息。-
首先检查BTLEServer数据源是否收到 ANCAlertSource变化
- 如果没有收到,先检查NC以及Springboard有没有收到来自apsd推送的消息
- 如果NC和Springboard收到消息,那么数据源订阅存在问题,可以关键字过滤subscribe以及
ANCS Authorization Manager
等,确认ANCS特征订阅是否存在问题 - 上述均没有,基本判断apsd未收到来自apple的推送
-
收到ANCAlertSource数据源,再次检查当前手机状态
- 主要检查
DND,suppressionState
等状态,或者结合visible
字段以及isSilent
字段查看
- 主要检查
-
上述正常后,检查BTLEServer有无
Added ** notification
记录 -
此时通知数据已准备完毕,检查bluetoothd进程是否发送
sending notification to <device>
-
-
对于iOS系统黑盒的情况下,可以结合下面反汇编的 os log记录来筛选当前手机对于消息通知的操作情况。
可分析出来的结果
- 手表和手机ANCS授权订阅数据源过程以及error日志
- 消息通知如何流程发送到手表的 (基本流程: apsd -> BTLEServer -> bluetoothd -> 手表) ,可以排除类似“手机没有推给手表”的问题
- 当前手机状态是否允许通知 (eg. DNDMode, 或者其他消极状态, 是否静默推送)
- 【延伸】连接问题、配对问题、蓝牙抓包数据均可分析,借助反汇编的os log日志记录
详细操作
如何上传手机诊断日志
不同于手机在正常使用过程中产生的Crash Log(崩溃日志)会被日志系统自动保存记录。Sysdiagnose(系统诊断日志)不是由操作系统自动记录的,必须由用户手动触发才可生成。
推荐方式(辅助触控)
借助手机端的“辅助触控”功能生成系统诊断日志。具体的操作步骤需在手机端完成,具体如下:
- 设置-辅助功能-触控-轻点两下-分析。当“触控”功能被激活后桌面上会出现辅助触控的“小圆点”。此时可点击两次系统便会自动收集诊断日志并储存。
- 触发后等待约几分钟,看到如下情况代表诊断完毕.
- 在系统设置-> 隐私与安全 -> 分析与改进 -> 分析数据 中找到 sysdiagnose 开头文件,分享上传即可.
备选方式(组合按键)
手机开机状态下,同时按住音量上+音量下+电源键持续一段时间(iOS sysdiagnose 最多可能需要 10 分钟才能完成。),当感觉到手机发出震动的时候松开全部按键即可生成系统诊断日志。当然一次尝试有可能会失败,需反复重复以上操作直至成功。iOS_Logs/sysdiagnose_Logging_Instructions.pdf
[拓展] 诊断日志TCC文件
TCC.db为SQLite数据库文件,用于记录应用程序权限。比如微信在当前系统下为其赋予的权限,如读取电话本、蓝牙、FaceID、相机等权限。
如何查看和分析诊断日志(以消息通知为例)
下面是正常推送情况,异常情况case较多,后续可以参考反汇编os log日志记录来分析。
申明:
- 这里主要以 BTLEServer 以及 bluetoothd 两个主要进程为例,从数据源订阅到状态监测,再到最终推送的流程。
- 其他进程如 SpringBoard 或者 runningboardd 进程可辅助分析,再或者可以直接使用待分析的APP包名过滤信息来辅助分析 (eg. com.bytedance.ee.lark)
- 如果遇到其他异常情况,可以参考下面反汇编的os log日志记录来相应分析。
注意:
部分系统日志输出 <private>
原因如下:
eg. os_log(log, "Message: %s", dynamicString);
动态字符串format不添加public的情况下,根据日志系统的规则会打印出 <private>
基于iOS14+日志分析
下面是一次正常推送消息的流程,只展示了BTLEServer
数据源监听服务,以及bluetoothd
蓝牙设备通信进程
存在推送不到达的case包括:
- 没有接收到apsd数据源
- 检测手机状态不正常而没有成功添加通知数据
- 蓝牙设备通信进程没有发送 等情况
/* 监听到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/**
相关系统日志记录
- 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"
- 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 其中的两个类 ANCAlertSource、ANCService
// 开启关闭方法
-[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 来看伪代码(下面伪代码有精简)