重启后Android Companion Device Manager无法识别关联BLE设备
Companion Device Manager重启后设备监听失效问题
问题描述
开发的Android应用基于Companion Device Manager(CDM)实现关联设备连接,调用startObservingDevicePresence方法后,首次安装并完成设备关联时功能正常,但手机重启(手动触发或系统更新导致)后,CDM无法自动识别关联设备。
预期行为:关联设备靠近时,CompanionDeviceService的onDeviceAppeared方法自动触发;目前仅当手动在蓝牙设置中连接BLE设备时,该方法才会触发,不符合需求。
设备兼容性情况
- 可正常工作设备:
- Samsung S20(Android 13)
- Samsung S23(Android 13)
- Samsung S23 Ultra(Android 14)
- 失效设备:
- Google Pixel 6a(Android 13)
- Nothing Phone 2(Android 13)
- OnePlus 8 pro(Android 13)
已尝试的解决方案
- 实现监听
BOOT_COMPLETED动作的BroadcastReceiver,手动启动CompanionService - 在
BOOT_COMPLETED回调中,通过deviceManager调用stopObservingDevicePresence和startObservingDevicePresence,重新启动所有关联设备的监听 - 添加重启后自动启动的前台服务,短暂运行后关闭,试图确保应用在重启后处于激活状态
- 手动连接BLE设备可触发
onDeviceAppeared,但需要实现自动触发逻辑
相关代码片段
AndroidManifest.xml
... <uses-feature android:name="android.software.companion_device_setup" android:required="true" /> <uses-permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" /> <uses-permission android:name="android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND" /> <uses-permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE" /> ... <service android:name="CompanionService" android:exported="true" android:foregroundServiceType="connectedDevice" android:permission="android.permission.BIND_COMPANION_DEVICE_SERVICE"> <intent-filter> <action android:name="android.companiondevicemanager.action.BIND_SERVICE" /> <action android:name="android.companion.CompanionDeviceService" /> </intent-filter> </service> ...
设备关联代码
private const val SELECT_DEVICE_REQUEST_CODE = 0 class MainActivity : AppCompatActivity() { private val deviceManager: CompanionDeviceManager by lazy { getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager } val mBluetoothAdapter: BluetoothAdapter by lazy { val java = BluetoothManager::class.java getSystemService(java)!!.adapter } val executor: Executor = Executor { it.run() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder() .setNamePattern(Pattern.compile("My device")) .addServiceUuid(ParcelUuid(UUID(0x123abcL, -1L)), null) .build() val pairingRequest: AssociationRequest = AssociationRequest.Builder() .addDeviceFilter(deviceFilter) .setSingleDevice(true) .build() deviceManager.associate(pairingRequest, executor, object : CompanionDeviceManager.Callback() { override fun onAssociationPending(intentSender: IntentSender) { intentSender?.let { startIntentSenderForResult(it, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0) } } override fun onAssociationCreated(associationInfo: AssociationInfo) { val associationId: Int = associationInfo.id val macAddress: MacAddress = associationInfo.deviceMacAddress } override fun onFailure(errorMessage: CharSequence?) { // 处理失败逻辑 } } ) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { SELECT_DEVICE_REQUEST_CODE -> when(resultCode) { Activity.RESULT_OK -> { val deviceToPair: BluetoothDevice? = data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE) deviceToPair?.let { device -> deviceManager.startObservingDevicePresence(device.address) } } } else -> super.onActivityResult(requestCode, resultCode, data) } } }
CompanionService.kt
@RequiresApi(Build.VERSION_CODES.S) class CompanionService : CompanionDeviceService() { override fun onCreate() { super.onCreate() Timber.d("Companion - onCreate") } override fun onUnbind(intent: Intent?): Boolean { Timber.d("Companion - onUnbind") return super.onUnbind(intent) } @RequiresApi(Build.VERSION_CODES.TIRAMISU) override fun onDeviceAppeared(associationInfo: AssociationInfo) { Timber.d("Companion - onDeviceAppeared") } @Deprecated("Deprecated in Java") override fun onDeviceAppeared(address: String) { Timber.d("Companion - onDeviceAppeared") } @RequiresApi(Build.VERSION_CODES.TIRAMISU) override fun onDeviceDisappeared(associationInfo: AssociationInfo) { Timber.d("Companion - onDeviceDisappeared") } @Deprecated("Deprecated in Java") override fun onDeviceDisappeared(address: String) { Timber.d("Companion - onDeviceDisappeared") } override fun onDestroy() { Timber.d("Companion - onDestroy") super.onDestroy() } }
可行解决思路
完善后台权限配置
- 确认
REQUEST_COMPANION_RUN_IN_BACKGROUND权限已正确声明,同时引导用户在系统设置中开启应用的「后台活动」权限。部分厂商会强制限制后台进程,即使声明权限也需手动确认。 - 新增
REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND权限申请,重启后通过前台服务维持进程存活,避免被系统杀死。
- 确认
优化重启监听触发逻辑
- 同时监听
ACTION_LOCKED_BOOT_COMPLETED广播,该广播会在系统启动早期发送,避免BOOT_COMPLETED延迟或被拦截的问题。 - 在重启回调中,先通过
CompanionDeviceManager.getAssociations()获取所有已关联设备,再逐个调用startObservingDevicePresence,确保无遗漏。
- 同时监听
确保服务正确绑定
- 在
BOOT_COMPLETED回调中,通过Context.bindService()主动绑定CompanionService,而非仅调用startService(),保证服务与系统的CDM组件建立稳定连接。 - 检查Manifest中服务的权限与intent-filter配置,确认
android.permission.BIND_COMPANION_DEVICE_SERVICE和动作声明无误。
- 在
适配厂商定制系统
- 引导用户开启应用的「自启动」权限,非三星厂商(如Google、OnePlus)的系统常通过该权限限制应用重启后的后台运行。
- 将应用加入电池优化豁免列表,避免系统为省电终止CDM相关监听进程。
验证关联状态与监听注册
- 重启后调用
CompanionDeviceManager.getAssociations()检查关联信息是否存在,若丢失需重新触发关联流程;若存在但监听失效,需重新注册监听。
- 重启后调用
内容的提问来源于stack exchange,提问作者user3423523498234234
相关产品推荐
相关产品推荐

