该文章基于Android Q
1 hfp简单介绍
HFP (Hands-free Profile),让蓝牙设备(如蓝牙耳机)可以控制电话,如接听、挂断、拒接、语音拨号等,拒接、语音拨号要看蓝牙耳机及电话是否支持。
HFP定义了音频网关(AG)和免提组件(HF)两个角色:
音频网关(AG) – 该设备为音频(特别是手机)的输入/输出网关。
免提组件(HF) – 该设备作为音频网关的远程音频输入/输出机制,并可提供若干遥控功能。
2 手机音频连接
点击手机音频进行连接时,调用onPreferenceClick。
@Override
public boolean onPreferenceClick(Preference preference) {
......
SwitchPreference profilePref = (SwitchPreference) preference;
if (profilePref.isChecked()) {
enableProfile(profile);
} else {
disableProfile(profile);
}
refreshProfilePreference(profilePref, profile);
return true;
}
private void enableProfile(LocalBluetoothProfile profile) {
final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
if (profile instanceof PbapServerProfile) {
bluetoothDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
// We don't need to do the additional steps below for this profile.
return;
}
if (profile instanceof MapProfile) {
bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
}
profile.setPreferred(bluetoothDevice, true);
mCachedDevice.connectProfile(profile);
}
frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
public void connectProfile(LocalBluetoothProfile profile) {
mConnectAttempted = SystemClock.elapsedRealtime();
connectInt(profile);
// Refresh the UI based on profile.connect() call
refresh();
}
synchronized void connectInt(LocalBluetoothProfile profile) {
if (!ensurePaired()) {
return;
}
if (profile.connect(mDevice)) {
if (BluetoothUtils.D) {
Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
}
return;
}
Log.i(TAG, "Failed to connect " + profile.toString() + " to " + getName());
}
frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
public boolean connect(BluetoothDevice device) {
if (mService == null) {
return false;
}
return mService.connect(device);
}
frameworks/base/core/java/android/bluetooth/BluetoothHeadset.java
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHeadset service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.connect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java
private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub{
......
public boolean connect(BluetoothDevice device) {
HeadsetService service = getService();
if (service == null) {
return false;
}
return service.connect(device);
}
}
public boolean connect(BluetoothDevice device) {
......
stateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);
}
packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
class Disconnected extends HeadsetStateBase {
public boolean processMessage(Message message) {
switch (message.what) {
case CONNECT:
BluetoothDevice device = (BluetoothDevice) message.obj;
stateLogD("Connecting to " + device);
......
if (!mNativeInterface.connectHfp(device)) {
stateLogE("CONNECT failed for connectHfp(" + device + ")");
// No state transition is involved, fire broadcast immediately
broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_DISCONNECTED);
break;
}
retryConnectCount++;
transitionTo(mConnecting);
break;
}
最后状态机Disconnected->Connecting
packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java
public boolean connectHfp(BluetoothDevice device) {
return connectHfpNative(Utils.getByteAddress(device));
}
packages/apps/Bluetooth/jni/com_android_bluetooth_hfp.cpp
static jboolean connectHfpNative(JNIEnv* env, jobject object,
jbyteArray address) {
......
ALOGI("%s: device %s", __func__, ((RawAddress*)addr)->ToString().c_str());
bt_status_t status = sBluetoothHfpInterface->Connect((RawAddress*)addr);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed HF 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::Connect(RawAddress* bd_addr) {
CHECK_BTHF_INIT();
return btif_queue_connect(UUID_SERVCLASS_AG_HANDSFREE, *bd_addr, connect_int,
btif_max_hf_clients);
}
3 连接状态
当协议栈连接状态改变会回调com_android_bluetooth_hfp.cpp中的方法ConnectionStateCallback()。
packages/apps/Bluetooth/jni/com_android_bluetooth_hfp.cpp
void ConnectionStateCallback(
bluetooth::headset::bthf_connection_state_t state,
RawAddress* bd_addr) override {
ALOGI("%s %d for %s", __func__, state, bd_addr->ToString().c_str());
......
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
(jint)state, addr.get());
}
packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java
void onConnectionStateChanged(int state, byte[] address) {
HeadsetStackEvent event =
new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_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);
}
}
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);
}
}
packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
class Connecting extends HeadsetStateBase {
public boolean processMessage(Message message) {
switch (message.what) {
......
case STACK_EVENT:
switch (event.type) {
case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
processConnectionEvent(message, event.valueInt);
break;
......
}
}
public void processConnectionEvent(Message message, int state) {
stateLogD("processConnectionEvent, state=" + state);
switch (state) {
......
case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
stateLogD("SLC connected");
retryConnectCount = 0;
transitionTo(mConnected);
break;
......
}
}
状态机Connecting->Connected,执行enter
class Connected extends ConnectedBase {
......
public void enter() {
......
broadcastStateTransitions();
}
发送broadcast BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED
void broadcastConnectionState(BluetoothDevice device, int fromState, int toState) {
stateLogD("broadcastConnectionState " + device + ": " + fromState + "->" + toState);
mHeadsetService.onConnectionStateChangedFromStateMachine(device, fromState, toState);
Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mHeadsetService.sendBroadcastAsUser(intent, UserHandle.ALL,
HeadsetService.BLUETOOTH_PERM);
}