You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在蓝牙耳机发起语音识别会话时阻止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

火山引擎 最新活动