如何在蓝牙耳机发起语音识别会话时阻止Google Assistant?
解决Google Assistant抢占HFP SCO连接(AT+BVRA触发)的Java方案
首先非常理解你的困扰——当耳机通过AT+BVRA命令触发SCO连接时,Android系统默认会把这个语音识别请求路由给Google Assistant,这确实会打断自定义应用的音频流。结合你已经尝试过的方法,我整理了几个更针对性的Java解决方案,你可以逐一测试:
1. 拦截HFP Vendor Specific广播,阻止系统处理AT+BVRA事件
Android系统会在收到耳机的AT+BVRA命令时发送ACTION_VENDOR_SPECIFIC_HEADSET_EVENT广播,我们可以注册优先级更高的广播接收器,拦截这个事件并阻止系统后续处理。
实现步骤:
- 在Manifest中添加必要权限(注意Android版本差异):
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- Android 12+ 需要 --> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
- 注册高优先级广播接收器:
public class HfpCommandInterceptor extends BroadcastReceiver { private static final String ACTION_VENDOR_SPECIFIC = "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT"; private static final String EXTRA_VENDOR_SPECIFIC_CMD = "android.bluetooth.headset.extra.VENDOR_SPECIFIC_CMD"; @Override public void onReceive(Context context, Intent intent) { if (ACTION_VENDOR_SPECIFIC.equals(intent.getAction())) { String cmd = intent.getStringExtra(EXTRA_VENDOR_SPECIFIC_CMD); // 检测是否是AT+BVRA命令 if (cmd != null && cmd.startsWith("AT+BVRA")) { // 拦截广播,阻止系统(Google Assistant)接收 abortBroadcast(); // 在这里触发你的应用的SCO连接逻辑 initiateAppScoConnection(context); } } } private void initiateAppScoConnection(Context context) { AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); // 重新初始化你的SCO连接逻辑 audioManager.startBluetoothSco(); audioManager.setBluetoothScoOn(true); // 绑定AudioRecord到SCO通道(你的原有逻辑) } }
- 在Activity或Service中注册接收器,设置高优先级:
IntentFilter filter = new IntentFilter(ACTION_VENDOR_SPECIFIC); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); registerReceiver(new HfpCommandInterceptor(), filter);
注意:部分定制ROM可能限制高优先级广播的拦截,需要在不同设备上测试。
2. 将你的应用设置为默认语音识别服务
如果你的应用本身是语音识别类应用,可以申请成为系统默认的语音识别服务,这样系统会把AT+BVRA的请求路由给你,而不是Google Assistant。
实现步骤:
- 在Manifest中声明语音识别服务:
<service android:name=".MyVoiceRecognitionService" android:permission="android.permission.BIND_VOICE_RECOGNITION_SERVICE"> <intent-filter> <action android:name="android.speech.RecognitionService" /> </intent-filter> </service>
- 实现
RecognitionService子类:
public class MyVoiceRecognitionService extends RecognitionService { @Override protected void onStartListening(Intent recognizerIntent, Callback listener) { // 这里处理你的语音识别逻辑,复用原有的AudioRecord+PCM上传逻辑 } @Override protected void onCancel(Callback listener) { // 取消识别的逻辑 } @Override protected void onStopListening(Callback listener) { // 停止识别的逻辑 } }
- 引导用户设置你的应用为默认语音识别服务:
Intent intent = new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS); startActivity(intent);
用户在设置中选择你的应用后,系统会把语音识别请求(包括HFP触发的)定向到你的服务。
3. 反射修改BluetoothHeadset的语音识别回调(兼容性有限)
如果上述方法无效,可以尝试反射拦截BluetoothHeadset内部的语音识别事件回调。这个方法依赖Android系统的内部API,不同版本可能有变化,仅作为备选方案:
private void hookBluetoothHeadsetVoiceRecognition() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter == null) return; try { // 获取BluetoothHeadset实例 Method getProfileMethod = BluetoothAdapter.class.getMethod("getProfileProxy", Context.class, BluetoothProfile.ServiceListener.class, int.class); getProfileMethod.invoke(adapter, getApplicationContext(), new BluetoothProfile.ServiceListener() { @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { if (profile == BluetoothProfile.HEADSET) { BluetoothHeadset headset = (BluetoothHeadset) proxy; try { // 反射设置自定义的VoiceRecognitionListener Method setListenerMethod = BluetoothHeadset.class.getDeclaredMethod("setVoiceRecognitionListener", BluetoothHeadset.VoiceRecognitionListener.class); setListenerMethod.setAccessible(true); setListenerMethod.invoke(headset, new BluetoothHeadset.VoiceRecognitionListener() { @Override public void onVoiceRecognitionStarted(BluetoothDevice device) { // 拦截开始事件,执行你的逻辑 initiateAppScoConnection(getApplicationContext()); } @Override public void onVoiceRecognitionStopped(BluetoothDevice device) { // 处理停止事件 } }); } catch (Exception e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected(int profile) { // 服务断开后的处理 } }, BluetoothProfile.HEADSET); } catch (Exception e) { e.printStackTrace(); } }
注意:Android 10及以上版本对反射系统API的限制更严格,可能需要应用拥有系统签名才能生效。
额外提示
- 部分厂商(如小米、华为)的定制系统可能自带语音助手,它们的抢占逻辑可能和Google Assistant不同,需要针对性适配。
- 确保你的应用持有
RECORD_AUDIO权限,并且在Android 13+中申请POST_NOTIFICATIONS权限(如果需要后台运行)。 - 可以尝试在SCO连接建立后,立即请求音频焦点并设置独占模式:
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); AudioAttributes attrs = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build(); AudioFocusRequest request = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) .setAudioAttributes(attrs) .build(); int result = audioManager.requestAudioFocus(request); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // 成功获取焦点,继续你的音频逻辑 }
内容的提问来源于stack exchange,提问作者user608578




