当前位置: 首页 > article >正文

【Android】蓝牙电话HFP连接源码分析

一、概述

在Android系统中,HF(Hands-Free Profile)客户端与AG(Audio Gateway)端之间的HFP(Hands-Free Profile)连接是蓝牙音频通信的重要组成部分。这一过程涉及多个层次和组件的协同工作,从Java层的BluetoothHeadsetClient开始,一直到C++层的蓝牙核心协议栈。

1.1. 初始连接请求

连接过程始于HF客户端(如车载蓝牙设备)上的应用程序调用BluetoothHeadsetClient的connect函数。这一调用标志着连接请求的发起,是整个连接流程的起点。

1.2. 状态机处理

随后,该连接请求被传递给一个状态机进行处理。状态机是Android蓝牙栈中用于管理蓝牙设备连接状态的重要组件。在接收到连接请求后,状态机会根据当前的状态和事件来决定下一步的动作。

在HFP连接过程中,状态机会经历多个状态的切换,包括但不限于:

  • IDLE:初始状态,等待连接请求。

  • CONNECTING:正在尝试建立连接。

  • CONNECTED:连接已成功建立。

  • DISCONNECTED:连接已断开。

1.3. C++接口调用

在状态机确定需要继续推进连接过程后,会调用到底层的C++接口。这些接口是Android蓝牙协议栈与蓝牙硬件之间的桥梁,负责执行实际的连接操作。

1.4. 蓝牙设备队列管理

在C++层,蓝牙设备的管理是通过一个设备队列来实现的。这个队列维护了所有待连接的蓝牙设备信息。当HF客户端发起连接请求时,该设备会被添加到队列中,并等待蓝牙核心栈的处理。

1.5. 实际连接操作

蓝牙核心栈在接收到连接请求后,会开始执行实际的连接操作。这包括:

  • SDP服务搜索:服务发现协议(SDP)用于在蓝牙设备间搜索可用的服务。在HFP连接中,SDP用于查找AG端提供的HFP服务。

  • 建立RFCOMM通道:RFCOMM是一种基于蓝牙的串行通信协议,用于在蓝牙设备间建立可靠的连接。在找到AG端的HFP服务后,HF客户端会与AG端建立一个RFCOMM通道作为通信的媒介。

  • L2CAP连接:逻辑链路控制和适配协议层(L2CAP)是蓝牙协议栈中的数据传输层。在RFCOMM通道建立之前,HF客户端和AG端之间需要先建立一个L2CAP连接。

1.6. 状态机切换与事件处理

在整个连接过程中,状态机会根据连接的状态和发生的事件进行切换和处理。例如,当SDP服务搜索完成时,状态机会从“搜索中”状态切换到“已找到服务”状态,并触发相应的事件处理逻辑。

二、源码分析

connect

packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean connect(BluetoothDevice device) {
    if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
        Log.w(TAG, "connect: CONNECTION_POLICY_FORBIDDEN, device=" + device + ", "
                + Utils.getUidPidString());
        return false;
    }
    ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
    if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { // 检查这些UUID中是否包含任何与HFP相关的UUID
        Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID, "
                + Utils.getUidPidString());
        return false;
    }
    synchronized (mStateMachines) {
        Log.i(TAG, "connect: device=" + device + ", " + Utils.getUidPidString());
        HeadsetStateMachine stateMachine = mStateMachines.get(device);
        if (stateMachine == null) {
            stateMachine = HeadsetObjectsFactory.getInstance()
                    .makeStateMachine(device, mStateMachinesThread.getLooper(), this,
                            mAdapterService, mNativeInterface, mSystemInterface);
            mStateMachines.put(device, stateMachine);
        }
        int connectionState = stateMachine.getConnectionState();
        if (connectionState == BluetoothProfile.STATE_CONNECTED
                || connectionState == BluetoothProfile.STATE_CONNECTING) {
            Log.w(TAG, "connect: device " + device
                    + " is already connected/connecting, connectionState=" + connectionState);
            return false;
        }
        List<BluetoothDevice> connectingConnectedDevices =
                getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
        boolean disconnectExisting = false;
        if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) {
            // When there is maximum one device, we automatically disconnect the current one
            if (mMaxHeadsetConnections == 1) {
                disconnectExisting = true;
            } else {
                Log.w(TAG, "Max connection has reached, rejecting connection to " + device);
                return false;
            }
        }
        if (disconnectExisting) {
            for (BluetoothDevice connectingConnectedDevice : connectingConnectedDevices) {
                disconnect(connectingConnectedDevice);
            }
            setActiveDevice(null);
        }
        stateMachine.sendMessage(HeadsetStateMachine.CONNECT, device); // 向状态机发送一个连接消息
    }
    return true;
}

processMessage(CONNECT)

packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
@Override
public synchronized boolean processMessage(Message message) {
    logD("Connected process message: " + message.what);
    if (mCurrentDevice == null) {
        Log.e(TAG, "ERROR: mCurrentDevice is null in Connected");
        return NOT_HANDLED;
    }

    switch (message.what) {
        case CONNECT:
            BluetoothDevice device = (BluetoothDevice) message.obj;
            if (mCurrentDevice.equals(device)) {
                // already connected to this device, do nothing
                break;
            }
            mNativeInterface.connect(device);
            break;
            
            ...
            

处理与蓝牙耳机(HFP客户端)相关的各种状态变化。

mNativeInterface.connect

packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfpclient/NativeInterface.java
/**
 * Connect to the specified paired device
 *
 * @param device target device
 * @return True on success, False on failure
 */
@VisibleForTesting
public boolean connect(BluetoothDevice device) {
    return connectNative(getByteAddress(device));
}

Java层与底层原生代码(C/C++编写的)之间的桥梁。

connectNative

/packages/modules/Bluetooth/android/app/jni/com_android_bluetooth_hfpclient.cpp
static jboolean connectNative(JNIEnv* env, jobject /* object */,
                              jbyteArray address) {
  std::shared_lock<std::shared_mutex> lock(interface_mutex);
  if (!sBluetoothHfpClientInterface) return JNI_FALSE;

  jbyte* addr = env->GetByteArrayElements(address, NULL);
  if (!addr) {
    jniThrowIOException(env, EINVAL);
    return JNI_FALSE;
  }

  bt_status_t status =
      sBluetoothHfpClientInterface->connect((const RawAddress*)addr);
  if (status != BT_STATUS_SUCCESS) {
    log::error("Failed AG connection, status: {}", bt_status_text(status));
  }
  env->ReleaseByteArrayElements(address, addr, 0);
  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}

connectNative函数是一个native method,通过JNI(Java Native Interface)从Java层接收调用,并尝试建立到指定蓝牙地址的HFP(Hands-Free Profile)客户端连接。

协议栈代码分析见【Bluedroid】HFP连接流程源码分析(一)-CSDN博客

连接回调处理。

connection_state_cb

/packages/modules/Bluetooth/android/app/jni/com_android_bluetooth_hfpclient.cpp
static void connection_state_cb(const RawAddress* bd_addr,
                                bthf_client_connection_state_t state,
                                unsigned int peer_feat,
                                unsigned int chld_feat) {
  std::shared_lock<std::shared_mutex> lock(callbacks_mutex);
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid() || mCallbacksObj == NULL) return;

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

  log::debug("state {} peer_feat {} chld_feat {}", state, peer_feat, chld_feat);
  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
                               (jint)state, (jint)peer_feat, (jint)chld_feat,
                               addr.get());
}

connection_state_cb是一个回调函数,用于通知Java层的Bluetooth HFP客户端关于连接状态的变化。 

 

onConnectionStateChanged

/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfpclient/NativeInterface.java
// Callbacks from the native back into the java framework. All callbacks are routed via the
// Service which will disambiguate which state machine the message should be routed through.
@VisibleForTesting
void onConnectionStateChanged(int state, int peerFeat, int chldFeat, byte[] address) {
    StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
    event.valueInt = state;
    event.valueInt2 = peerFeat;
    event.valueInt3 = chldFeat;
    event.device = getDevice(address);
    // BluetoothAdapter.getDefaultAdapter().getRemoteDevice(Utils.getAddressStringFromByte
    // (address));
    if (DBG) {
        Log.d(TAG, "Device addr " + event.device + " State " + state);
    }
    HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
    if (service != null) {
        service.messageFromNative(event);
    } else {
        Log.w(TAG, "Ignoring message because service not available: " + event);
    }
}

Android Bluetooth HFP(Hands-Free Profile)客户端Java层与从本地(native)代码回调到Java框架相关的部分。onConnectionStateChanged方法是一个回调方法,它接收来自本地层的连接状态变化通知。

messageFromNative

packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
// Handle messages from native (JNI) to java
public void messageFromNative(StackEvent stackEvent) {
    Objects.requireNonNull(stackEvent.device,
            "Device should never be null, event: " + stackEvent);

    HeadsetClientStateMachine sm = getStateMachine(stackEvent.device,
            isConnectionEvent(stackEvent));
    if (sm == null) {
        throw new IllegalStateException(
                "State machine not found for stack event: " + stackEvent);
    }
    sm.sendMessage(StackEvent.STACK_EVENT, stackEvent);
}

Android Bluetooth HFP客户端Java层中处理来自本地(JNI)回调消息的一部分。messageFromNative方法负责接收一个封装了蓝牙堆栈事件的StackEvent对象,并根据该事件中的信息将事件分发给相应的状态机(HeadsetClientStateMachine)进行处理。

processMessage(EVENT_TYPE_CONNECTION_STATE_CHANGED)

packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
@Override
public synchronized boolean processMessage(Message message) {
    logD("Connecting process message: " + message.what);

    switch (message.what) {
        case CONNECT:
        case CONNECT_AUDIO:
        case DISCONNECT:
            deferMessage(message);
            break;
        case StackEvent.STACK_EVENT:
            StackEvent event = (StackEvent) message.obj;
            logD("Connecting: event type: " + event.type);
            switch (event.type) {
                    
                ...
                
                case StackEvent.EVENT_TYPE_CMD_RESULT:
                    logD("Connecting: CMD_RESULT valueInt:" + event.valueInt
                            + " mQueuedActions.size=" + mQueuedActions.size());
                    if (!mQueuedActions.isEmpty()) {
                        logD("queuedAction:" + mQueuedActions.peek().first);
                    }
                    Pair<Integer, Object> queuedAction = mQueuedActions.poll();
                    if (queuedAction == null || queuedAction.first == NO_ACTION) {
                        break;
                    }
                    switch (queuedAction.first) {
                        case SEND_ANDROID_AT_COMMAND:
                            if (event.valueInt == StackEvent.CMD_RESULT_TYPE_OK) {
                                Log.w(TAG, "Received OK instead of +ANDROID");
                            } else {
                                Log.w(TAG, "Received ERROR instead of +ANDROID");
                            }
                            setAudioPolicyRemoteSupported(false);
                            transitionTo(mConnected);
                            break;
                        default:
                            Log.w(TAG, "Ignored CMD Result");
                            break;
                    }
                    break;
                    
           ...
                    
          default:
            Log.w(TAG, "Message not handled " + message);
            return NOT_HANDLED;
    }
    return HANDLED;
}

HeadsetClientStateMachine类中的processMessage方法负责处理从消息队列中接收到的消息,并根据消息类型执行相应的操作。

enter(Connected)

@Override
public void enter() {
    logD("Enter Connected: " + getCurrentMessage().what);
    mAudioWbs = false;
    mAudioSWB = false;
    mCommandedSpeakerVolume = -1;

    if (mPrevState == mConnecting) {
        broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
                BluetoothProfile.STATE_CONNECTING);
        if (mHeadsetService != null) {
            mHeadsetService.updateInbandRinging(mCurrentDevice, true);
        }
        MetricsLogger.logProfileConnectionEvent(
                BluetoothMetricsProto.ProfileId.HEADSET_CLIENT);
    } else if (mPrevState != mAudioOn) {
        String prevStateName = mPrevState == null ? "null" : mPrevState.getName();
        Log.e(TAG, "Connected: Illegal state transition from " + prevStateName
                + " to Connected, mCurrentDevice=" + mCurrentDevice);
    }
    mService.updateBatteryLevel();
}

HeadsetClientStateMachine状态机中的enter方法,该方法在状态机进入“Connected”(已连接)状态时被调用。enter方法通常用于执行进入新状态时需要立即执行的操作,如初始化状态变量、发送广播、更新服务等。

broadcastConnectionState

packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
// This method does not check for error condition (newState == prevState)
private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
    logD("Connection state " + device + ": " + prevState + "->" + newState);
    /*
     * Notifying the connection state change of the profile before sending
     * the intent for connection state change, as it was causing a race
     * condition, with the UI not being updated with the correct connection
     * state.
     */
    Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); // 表示蓝牙HFP客户端的连接状态发生了变化
    intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
    intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
    intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);

    // add feature extras when connected
    if (newState == BluetoothProfile.STATE_CONNECTED) {
        if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY)
                == HeadsetClientHalConstants.PEER_FEAT_3WAY) {
            intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true);
        }
        if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC)
                == HeadsetClientHalConstants.PEER_FEAT_VREC) {
            intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true);
        }
        if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT)
                == HeadsetClientHalConstants.PEER_FEAT_REJECT) {
            intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true);
        }
        if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC)
                == HeadsetClientHalConstants.PEER_FEAT_ECC) {
            intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true);
        }

        // add individual CHLD support extras
        if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC)
                == HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) {
            intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL,
                    true);
        }
        if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL)
                == HeadsetClientHalConstants.CHLD_FEAT_REL) {
            intent.putExtra(
                    BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true);
        }
        if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC)
                == HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) {
            intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true);
        }
        if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE)
                == HeadsetClientHalConstants.CHLD_FEAT_MERGE) {
            intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true);
        }
        if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH)
                == HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) {
            intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true);
        }
    }

    mService.sendBroadcastMultiplePermissions(intent,
            new String[] {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
            Utils.getTempBroadcastOptions()); // 广播了创建的Intent,同时确保了只有具有相应权限的应用程序才能接收到这个广播

    // 通知了HFP客户端连接服务关于连接状态的变化
    HfpClientConnectionService.onConnectionStateChanged(device, newState, prevState);
}

负责在蓝牙客户端的状态发生变化时广播这一变化,负责创建并广播包含连接状态和相关功能信息的Intent,并通知相关的服务关于状态的变化。确保蓝牙HFP客户端能够与其他应用程序和服务有效地交互,并提供正确的连接状态信息。

HfpClientConnectionService.onConnectionStateChanged

packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfpclient/HfpClientConnectionService.java
/**
 * Send a device connection state changed event to this service
 */
public static void onConnectionStateChanged(BluetoothDevice device, int newState,
        int oldState) {
    HfpClientConnectionService service = getInstance();
    if (service == null) {
        Log.e(TAG, "onConnectionStateChanged: HFP Client Connection Service not started");
        return;
    }
    service.onConnectionStateChangedInternal(device, newState, oldState);
}

当蓝牙HFP设备的连接状态发生变化时,通过HfpClientConnectionService服务来处理这一变化。

onConnectionStateChangedInternal

packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfpclient/HfpClientConnectionService.java
private void onConnectionStateChangedInternal(BluetoothDevice device, int newState,
        int oldState) {
    if (newState == BluetoothProfile.STATE_CONNECTED) { // 已连接
        if (DBG) {
            Log.d(TAG, "Established connection with " + device);
        }

        HfpClientDeviceBlock block = createBlockForDevice(device);
        if (block == null) {
            Log.w(TAG, "Block already exists for device= " + device + ", ignoring.");
        }
    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
        if (DBG) {
            Log.d(TAG, "Disconnecting from " + device);
        }

        // Disconnect any inflight calls from the connection service.
        synchronized (HfpClientConnectionService.this) {
            HfpClientDeviceBlock block = mDeviceBlocks.remove(device);
            if (block == null) {
                Log.w(TAG, "Disconnect for device but no block, device=" + device);
                return;
            }
            block.cleanup();
        }
    }
    AdapterService adapterService = AdapterService.getAdapterService();
    if (adapterService != null && adapterService.getRemoteDevices() != null) {
        adapterService
                .getRemoteDevices()
                .handleHeadsetClientConnectionStateChanged(device, oldState, newState);
    }
    // 通知GATT(Generic Attribute Profile)关于HFP客户端配置文件连接状态的变化
    adapterService.notifyProfileConnectionStateChangeToGatt(
            BluetoothProfile.HEADSET_CLIENT, oldState, newState);
    if (PbapClientService.getPbapClientService() != null) {
        PbapClientService.getPbapClientService()
                .handleHeadsetClientConnectionStateChanged(device, oldState, newState);
    }
    if (adapterService != null) {
        adapterService.updateProfileConnectionAdapterProperties(
                device, BluetoothProfile.HEADSET_CLIENT, newState, oldState);
    }
}

负责处理蓝牙HFP客户端设备的连接状态变化,包括建立连接和断开连接时的逻辑处理,以及通知其他相关服务关于连接状态的变化。

至此,HFP连接一完成。

三、关键字

HeadsetClientService|HeadsetClientStateMachine


http://www.kler.cn/a/510327.html

相关文章:

  • 【Django开发】django美多商城项目完整开发4.0第12篇:商品部分,表结构【附代码文档】
  • 【2024年华为OD机试】(C卷,100分)- 悄悄话 (Java JS PythonC/C++)
  • 安路FPGA开发工具TD:问题解决办法 及 Tips 总结
  • LabVIEW桥接传感器数据采集与校准程序
  • 令牌主动失效机制实现——Redis登录优化
  • 快速入门:如何注册并使用GPT
  • Debian没有reboot命令记录
  • 【数据分析】02- A/B 测试:玩转假设检验、t 检验与卡方检验
  • 【深入解析】 RNN 算法:原理、应用与实现
  • MPSOC 裸机测试USB3.0接口
  • boss直聘 验证码 手图 分析
  • git系列之revert回滚
  • 使用 Blazor 和 Elsa Workflows 作为引擎的工作流系统开发
  • 几个Linux系统安装体验(续): 中标麒麟服务器系统
  • node.js卸载与安装超详细教程
  • 好用的输大文件的软件推荐!
  • 【博客之星】2024年度个人成长、强化学习算法领域总结
  • 解决 Mac 系统上的 node-sass 问题
  • Redis的安装和使用--Windows系统
  • 开发规范
  • java springboot项目使用easypackage一键打包windows服务
  • SpringBoot多级配置文件
  • 五.指派问题(变异问题)
  • 【TCP】rfc文档
  • 数据结构学习笔记——排序
  • 详细探讨:为什么 Java 不支持泛型数组?