You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

重启后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()
    }
}

可行解决思路

  1. 完善后台权限配置

    • 确认REQUEST_COMPANION_RUN_IN_BACKGROUND权限已正确声明,同时引导用户在系统设置中开启应用的「后台活动」权限。部分厂商会强制限制后台进程,即使声明权限也需手动确认。
    • 新增REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND权限申请,重启后通过前台服务维持进程存活,避免被系统杀死。
  2. 优化重启监听触发逻辑

    • 同时监听ACTION_LOCKED_BOOT_COMPLETED广播,该广播会在系统启动早期发送,避免BOOT_COMPLETED延迟或被拦截的问题。
    • 在重启回调中,先通过CompanionDeviceManager.getAssociations()获取所有已关联设备,再逐个调用startObservingDevicePresence,确保无遗漏。
  3. 确保服务正确绑定

    • 在BOOT_COMPLETED回调中,通过Context.bindService()主动绑定CompanionService,而非仅调用startService(),保证服务与系统的CDM组件建立稳定连接。
    • 检查Manifest中服务的权限与intent-filter配置,确认android.permission.BIND_COMPANION_DEVICE_SERVICE和动作声明无误。
  4. 适配厂商定制系统

    • 引导用户开启应用的「自启动」权限,非三星厂商(如Google、OnePlus)的系统常通过该权限限制应用重启后的后台运行。
    • 将应用加入电池优化豁免列表,避免系统为省电终止CDM相关监听进程。
  5. 验证关联状态与监听注册

    • 重启后调用CompanionDeviceManager.getAssociations()检查关联信息是否存在,若丢失需重新触发关联流程;若存在但监听失效,需重新注册监听。

内容的提问来源于stack exchange,提问作者user3423523498234234

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 19:49:51