能否通过Accessibility Service程序化点击蓝牙配对的Pair/Pair&Connect按钮?
解决Accessibility Service无法检测蓝牙配对对话框的问题
问题分析
你的代码无法检测到系统蓝牙配对对话框,主要原因包括:
- Accessibility Service权限配置未覆盖系统蓝牙相关应用
- 节点遍历仅检查一级子节点,未递归查找深层嵌套的按钮
- 事件监听逻辑未针对性处理系统配对通知和对话框
1. 修正Accessibility Service配置
首先确保服务能访问系统蓝牙相关应用,创建res/xml/accessibility_service_config.xml配置文件:
<?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeWindowStateChanged|typeNotificationStateChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault|flagIncludeNotImportantViews" android:canRetrieveWindowContent="true" android:description="@string/accessibility_service_desc" android:packageNames="com.android.settings,com.android.bluetooth,com.android.systemui" />
在AndroidManifest.xml中正确声明服务:
<service android:name=".MyAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service>
2. 修正Accessibility Service代码
以下是优化后的代码,解决节点遍历、事件过滤和按钮匹配问题:
class MyAccessibilityService : AccessibilityService() { companion object { const val TAG = "MyAccessibilityService" // 覆盖常见配对按钮文本(含中英文) private val PAIR_TRIGGERS = listOf("PAIR", "PAIR & CONNECT", "配对", "配对并连接") } override fun onAccessibilityEvent(event: AccessibilityEvent?) { event ?: return val packageName = event.packageName?.toString() ?: "" when (event.eventType) { // 处理配对对话框弹出事件 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED -> { if (packageName in listOf("com.android.settings", "com.android.bluetooth")) { findAndClickPairButton(rootInActiveWindow) } } // 处理配对通知点击 AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED -> { val notificationContent = event.text.joinToString(" ") if (PAIR_TRIGGERS.any { notificationContent.contains(it, ignoreCase = true) }) { event.source?.performAction(AccessibilityNodeInfo.ACTION_CLICK) } } } } // 递归遍历所有节点查找配对按钮 private fun findAndClickPairButton(node: AccessibilityNodeInfo?) { node ?: return // 检查当前节点是否为目标按钮 if (node.className == "android.widget.Button" && node.isClickable && node.isEnabled) { val buttonText = node.text?.toString() ?: "" val contentDesc = node.contentDescription?.toString() ?: "" if (PAIR_TRIGGERS.any { buttonText.equals(it, ignoreCase = true) || contentDesc.equals(it, ignoreCase = true) }) { node.performAction(AccessibilityNodeInfo.ACTION_CLICK) Log.e(TAG, "已自动点击配对按钮: $buttonText") return } } // 递归遍历子节点,用完后回收避免内存泄漏 for (i in 0 until node.childCount) { val childNode = node.getChild(i) ?: continue findAndClickPairButton(childNode) childNode.recycle() } } override fun onInterrupt() { Log.i(TAG, "Accessibility服务被中断") } }
关键优化点
- 递归节点遍历:覆盖深层嵌套的系统对话框按钮
- 多包名过滤:仅处理蓝牙相关系统应用的事件,减少无效检测
- 多文本匹配:支持中英文配对按钮文本,适配不同系统语言
- 通知处理:自动点击配对通知,触发对话框弹出后再点击配对按钮
注意事项
- 必须引导用户在系统设置中手动开启你的Accessibility Service权限(敏感权限无法自动授予)
- 不同厂商的系统配对对话框布局可能存在差异,需针对性测试适配
- 遍历节点后必须调用
recycle()回收资源,避免内存泄漏
内容的提问来源于stack exchange,提问作者SSF
相关产品推荐
相关产品推荐

