该文章基于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);
}

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

被折叠的 条评论
为什么被折叠?



