Android 蓝牙 hfp音频连接

本文详细介绍了Android设备通过蓝牙HFP协议进行音频连接的过程,包括连接音频时的源代码路径,如BluetoothRouteManager、BluetoothDeviceManager和HeadsetStateMachine等。在音频连接状态改变时,系统会回调com_android_bluetooth_hfp.cpp的AudioStateCallback函数,并通过messageFromNative传递给状态机,最终在AudioConnecting状态下完成音频连接,转变为mAudioOn状态,并发送ACTION_AUDIO_STATE_CHANGED广播。

Android 蓝牙 hfp音频连接


该文章基于Android Q

1 连接音频

在手机音频正常连接时,接通电话,点选蓝牙通话。mDeviceManager.connectAudio返回true。
如果是之前默认蓝牙mDeviceManager.connectAudio返回false。
packages/services/Telecomm/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java

    private String connectBtAudio(String address, int retryCount) {
        ......
        if (!mDeviceManager.connectAudio(actualAddress)) {
            boolean shouldRetry = retryCount < MAX_CONNECTION_RETRIES;
            Log.w(LOG_TAG, "Could not connect to %s. Will %s", actualAddress,
                    shouldRetry ? "retry" : "not retry");
            if (shouldRetry) {
                SomeArgs args = SomeArgs.obtain();
                args.arg1 = Log.createSubsession();
                args.arg2 = actualAddress;
                args.argi1 = retryCount + 1;
                sendMessageDelayed(RETRY_HFP_CONNECTION, args,
                        mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
                                mContext.getContentResolver()));
            }
            return null;
        }

        return actualAddress;
    }

packages/services/Telecomm/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java

    public boolean connectAudio(String address) {
        if (mHearingAidDevicesByAddress.containsKey(address)) {
            ......
        } else if (mHfpDevicesByAddress.containsKey(address)) {
            BluetoothDevice device = mHfpDevicesByAddress.get(address);
            if (mBluetoothHeadsetService == null) {
                Log.w(this, "Attempting to turn on audio when the headset service is null");
                return false;
            }
            boolean success = mBluetoothHeadsetService.setActiveDevice(device);
            if (!success) {
                Log.w(this, "Couldn't set active device to %s", address);
                return false;
            }
            if (!mBluetoothHeadsetService.isAudioOn()) {
                return mBluetoothHeadsetService.connectAudio();
            }
            return true;
        } else {
            Log.w(this, "Attempting to turn on audio for a disconnected device");
            return false;
        }
    }

packages/services/Telecomm/src/com/android/server/telecom/BluetoothHeadsetProxy.java

    public boolean connectAudio() {
        return mBluetoothHeadset.connectAudio();
    }

frameworks/base/core/java/android/bluetooth/BluetoothHeadset.java

    public boolean connectAudio() {
        final IBluetoothHeadset service = mService;
        if (service != null && isEnabled()) {
            try {
                return service.connectAudio();
            } catch (RemoteException e) {
                Log.e(TAG, e.toString());
            }
        } else {
            Log.w(TAG, "Proxy not attached to service");
            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
        }
        return false;
    }

packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java

    private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub{
        @Override
        public boolean connectAudio() {
            HeadsetService service = getService();
            if (service == null) {
                return false;
            }
            return service.connectAudio();
        }
    }
    
    boolean connectAudio() {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
        synchronized (mStateMachines) {
            BluetoothDevice device = mActiveDevice;
            if (device == null) {
                Log.w(TAG, "connectAudio: no active device, " + Utils.getUidPidString());
                return false;
            }
            return connectAudio(device);
        }
    }

如果之前默认蓝牙通话,isScoAcceptable返回false。
如果是点选蓝牙会向状态机发送CONNECT_AUDIO。

    boolean connectAudio(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
        Log.i(TAG, "connectAudio: device=" + device + ", " + Utils.getUidPidString());
        synchronized (mStateMachines) {
            if (!isScoAcceptable(device)) {
                Log.w(TAG, "connectAudio, rejected SCO request to " + device);
                return false;
            }
            final HeadsetStateMachine stateMachine = mStateMachines.get(device);
            ......
            stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
        }
        return true;
    }

当前状态机为Connected,接前篇hfp连接。
调用jni connectAudio,最后状态机转到mAudioConnecting
packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java

    class Connected extends ConnectedBase {
        public boolean processMessage(Message message) {
            switch (message.what) {
                case CONNECT_AUDIO:
                    stateLogD("CONNECT_AUDIO, device=" + mDevice);
                    ......
                    if (!mNativeInterface.connectAudio(mDevice)) {
                        stateLogE("Failed to connect SCO audio for " + mDevice);
                        // No state change involved, fire broadcast immediately
                        broadcastAudioState(mDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
                                BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
                        break;
                    }
                    transitionTo(mAudioConnecting);
                    break;
                }
            }
        }
    }

packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java

    public boolean connectAudio(BluetoothDevice device) {
        return connectAudioNative(Utils.getByteAddress(device));
    }

进入bluedroid。
packages/apps/Bluetooth/jni/com_android_bluetooth_hfp.cpp

static jboolean connectAudioNative(JNIEnv* env, jobject object,
                                   jbyteArray address) {
  ......
  bt_status_t status = sBluetoothHfpInterface->ConnectAudio((RawAddress*)addr);
  if (status != BT_STATUS_SUCCESS) {
    ALOGE("Failed HF audio connection, status: %d", status);
  }
  env->ReleaseByteArrayElements(address, addr, 0);
  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}

vendor/qcom/opensource/commonsys/system/bt/btif/src/btif_hf.cc

bt_status_t HeadsetInterface::ConnectAudio(RawAddress* bd_addr) {
  CHECK_BTHF_INIT();
  ......
  if (idx != BTIF_HF_INVALID_IDX) {
    BTA_AgAudioOpen(btif_hf_cb[idx].handle);
    ......
    /* Inform the application that the audio connection has been initiated
     * successfully */
    btif_transfer_context(btif_in_hf_generic_evt, BTIF_HFP_CB_AUDIO_CONNECTING,
                          (char*)bd_addr, sizeof(RawAddress), NULL);
    return BT_STATUS_SUCCESS;
  }

  return BT_STATUS_FAIL;
}

2 音频连接状态

packages/apps/Bluetooth/jni/com_android_bluetooth_hfp.cpp
当音频连接状态改变会回调com_android_bluetooth_hfp.cpp中AudioStateCallback函数。

  void AudioStateCallback(bluetooth::headset::bthf_audio_state_t state,
                          RawAddress* bd_addr) override {
    ALOGI("%s, %d for %s", __func__, state, bd_addr->ToString().c_str());

    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || !mCallbacksObj) return;

    ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
    if (!addr.get()) return;

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged,
                                 (jint)state, addr.get());
  }

packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java

    private void onAudioStateChanged(int state, byte[] address) {
        HeadsetStackEvent event =
                new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED, state,
                        getDevice(address));
        sendMessageToService(event);
    }

    private void sendMessageToService(HeadsetStackEvent event) {
        HeadsetService service = HeadsetService.getHeadsetService();
        if (service != null) {
            service.messageFromNative(event);
        } else {
            // Service must call cleanup() when quiting and native stack shouldn't send any event
            // after cleanup() -> cleanupNative() is called.
            Log.wtfStack(TAG, "FATAL: Stack sent event while service is not available: " + event);
        }
    }

回调信息会通过messageFromNative发送STACK_EVENT消息转给状态机。
packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java

    void messageFromNative(HeadsetStackEvent stackEvent) {
        Objects.requireNonNull(stackEvent.device,
                "Device should never be null, event: " + stackEvent);
        synchronized (mStateMachines) {
            HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device);
            ......
            stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent);
        }
    }

此时状态机为AudioConnecting。AudioConnecting中的processMessage没有STACK_EVENT项,
运行super.processMessage。

    class AudioConnecting extends ConnectedBase {
        public boolean processMessage(Message message) {
            switch (message.what) {
                case CONNECT:
                case DISCONNECT:
                case CONNECT_AUDIO:
                case DISCONNECT_AUDIO:
                    ......
                    break;
                case CONNECT_TIMEOUT: {
                	......
                    break;
                }
                default:
                    return super.processMessage(message);
            }
            return HANDLED;
        }
        ......
    }
   private abstract class ConnectedBase extends HeadsetStateBase {
        public boolean processMessage(Message message) {
            switch (message.what) {
                case STACK_EVENT:
                    HeadsetStackEvent event = (HeadsetStackEvent) message.obj;
                    stateLogD("STACK_EVENT: " + event);
                    switch (event.type) {
                        case HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
                            processAudioEvent(event.valueInt);
                            break;
                    }
            }
        }

        public abstract void processAudioEvent(int state);
    }

processAudioEvent由子类实现。
连接成功,状态机变为mAudioOn

    class AudioConnecting extends ConnectedBase {
        ......
        public void processAudioEvent(int state) {
            switch (state) {
                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
                    if (!mSystemInterface.getHeadsetPhoneState().getIsCsCall()) {
                        stateLogI("Sco connected for call other than CS, check network type");
                        sendVoipConnectivityNetworktype(true);
                    } else {
                        stateLogI("Sco connected for CS call, do not check network type");
                    }
                    stateLogI("processAudioEvent: audio connected");
                    transitionTo(mAudioOn);
                    break;
            }
        }
    }
    class AudioOn extends ConnectedBase {
        public void enter() {
            super.enter();
            ......
            setAudioParameters();

            broadcastStateTransitions();
        }

通过broadcastAudioState发送广播ACTION_AUDIO_STATE_CHANGED

    private abstract class HeadsetStateBase extends State {
        void broadcastStateTransitions() {
            ......
            if (getAudioStateInt() != mPrevState.getAudioStateInt() || (
                    mPrevState instanceof AudioDisconnecting && this instanceof AudioOn)) {
                stateLogD("audio state changed: " + mDevice + ": " + mPrevState + " -> " + this);
                broadcastAudioState(mDevice, mPrevState.getAudioStateInt(), getAudioStateInt());
            }
            if (getConnectionStateInt() != mPrevState.getConnectionStateInt()) {
                stateLogD(
                        "connection state changed: " + mDevice + ": " + mPrevState + " -> " + this);
                broadcastConnectionState(mDevice, mPrevState.getConnectionStateInt(),
                        getConnectionStateInt());
            }
        }

        void broadcastAudioState(BluetoothDevice device, int fromState, int toState) {
            stateLogD("broadcastAudioState: " + device + ": " + fromState + "->" + toState);
            ......
            mHeadsetService.onAudioStateChangedFromStateMachine(device, fromState, toState);
            Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
            intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
            intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
            mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL,
                    HeadsetService.BLUETOOTH_PERM);
        }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值