记录一下蓝牙A2DP的流程
packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothPairingDetail.java
@Override
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
disableScanning();
super.onDevicePreferenceClick(btPreference);
}
packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
btPreference.onClicked();
}
packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothDevicePreference.java
void onClicked() {
Context context = getContext();
// 获取连接状态
int bondState = mCachedDevice.getBondState();
final MetricsFeatureProvider metricsFeatureProvider =
FeatureFactory.getFactory(context).getMetricsFeatureProvider();
// 已经连接
if (mCachedDevice.isConnected()) {
// 断开连接
metricsFeatureProvider.action(context,
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_DISCONNECT);
askDisconnect();
// 以前连接过,不需要再配对,直接进行连接
} else if (bondState == BluetoothDevice.BOND_BONDED) {
metricsFeatureProvider.action(context,
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT);
mCachedDevice.connect(true);
// 没连接过,进行配对,需要连接的双方都同意之后才能连接
} else if (bondState == BluetoothDevice.BOND_NONE) {
metricsFeatureProvider.action(context,
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR);
if (!mCachedDevice.hasHumanReadableName()) {
metricsFeatureProvider.action(context,
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
}
pair();
}
}
匹配过的设备,进行连接
frameworks\base\packages\SettingsLib\src\com\android\settingslib\bluetooth\CachedBluetoothDevice.java
public void connect(boolean connectAllProfiles) {
// 是否配对过
if (!ensurePaired()) {
return;
}
mConnectAttempted = SystemClock.elapsedRealtime();
connectWithoutResettingTimer(connectAllProfiles);
}
private void connectWithoutResettingTimer(boolean connectAllProfiles) {
// Try to initialize the profiles if they were not.
if (mProfiles.isEmpty()) {
// if mProfiles is empty, then do not invoke updateProfiles. This causes a race
// condition with carkits during pairing, wherein RemoteDevice.UUIDs have been updated
// from bluetooth stack but ACTION.uuid is not sent yet.
// Eventually ACTION.uuid will be received which shall trigger the connection of the
// various profiles
// If UUIDs are not available yet, connect will be happen
// upon arrival of the ACTION_UUID intent.
Log.d(TAG, "No profiles. Maybe we will connect later");
return;
}
// Reset the only-show-one-error-dialog tracking variable
mIsConnectingErrorPossible = true;
int preferredProfiles = 0;
for (LocalBluetoothProfile profile : mProfiles) {
// connectAllProfile传进来的是true
if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
if (profile.isPreferred(mDevice)) {
++preferredProfiles;
connectInt(profile); // 选择对应的profile进行连接
}
}
}
if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
if (preferredProfiles == 0) {
connectAutoConnectableProfiles();
}
}
private void connectAutoConnectableProfiles() {
if (!ensurePaired()) {
return;
}
// Reset the only-show-one-error-dialog tracking variable
mIsConnectingErrorPossible = true;
for (LocalBluetoothProfile profile : mProfiles) {
if (profile.isAutoConnectable()) {
profile.setPreferred(mDevice, true);
connectInt(profile);
}
}
}
frameworks\base\packages\SettingsLib\src\com\android\settingslib\bluetooth\CachedBluetoothDevice.java
synchronized void connectInt(LocalBluetoothProfile profile) {
// 判断是否配对过
if (!ensurePaired()) {
return;
}
// 连接
if (profile.connect(mDevice)) {
if (Utils.D) {
Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
}
return;
}
Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
}
A2dp的profile
frameworks\base\packages\SettingsLib\src\com\android\settingslib\bluetooth\A2dpProfile.java
public class A2dpProfile implements LocalBluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (mService == null) return false;
List<BluetoothDevice> sinks = getConnectedDevices();
if (sinks != null) {
for (BluetoothDevice sink : sinks) {
if (sink.equals(device)) {
Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
continue;
}
}
}
// 连接
return mService.connect(device);
}
frameworks\base\core\java\android\bluetooth\BluetoothA2dp.java
@GuardedBy("mServiceLock") private IBluetoothA2dp mService;
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
try {
mServiceLock.readLock().lock();
if (mService != null && isEnabled() &&
isValidDevice(device)) {
return mService.connect(device); // 调用aidl中的方法
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
} finally {
mServiceLock.readLock().unlock();
}
}
Binder进程间通信,服务端实现方法如下。
packages\apps\Bluetooth\src\com\android\bluetooth\a2dp\A2dpService.java
private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub
implements IProfileServiceBinder {
public boolean connect(BluetoothDevice device) {
A2dpService service = getService();
if (service == null) return false;
//do not allow new connections with active multicast
if (service.isMulticastOngoing(device)) {
Log.i(TAG,"A2dp Multicast is Ongoing, ignore Connection Request");
return false;
}
return service.connect(device);
}
public boolean connect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "Enter connect");
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
return false;
}
ParcelUuid[] featureUuids = device.getUuids();
if ((BluetoothUuid.containsAnyUuid(featureUuids, A2DP_SOURCE_UUID)) &&
!(BluetoothUuid.containsAllUuids(featureUuids ,A2DP_SOURCE_SINK_UUIDS))) {
Log.e(TAG,"Remote does not have A2dp Sink UUID");
return false;
}
int connectionState = BluetoothProfile.STATE_DISCONNECTED;
synchronized(mBtA2dpLock) {
if (mStateMachine != null) {
connectionState = mStateMachine.getConnectionState(device);
}
}
if (connectionState == BluetoothProfile.STATE_CONNECTED ||
connectionState == BluetoothProfile.STATE_CONNECTING) {
return false;
}
// 发送消息给状态机
synchronized(mBtA2dpLock) {
if (mStateMachine != null) {
mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device);
}
}
if (DBG) Log.d(TAG, "Exit connect");
return true;
}
状态机初始状态Disconnect
packages\apps\Bluetooth\src\com\android\bluetooth\a2dp\A2dpStateMachine.java
final class A2dpStateMachine extends StateMachine {
switch(message.what) {
case CONNECT:
BluetoothDevice device = (BluetoothDevice) message.obj;
// 发送广播,状态改变
broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED);
// 进行连接
if (!connectA2dpNative(getByteAddress(device)) ) {
// 连接完成,失败就再次发广播,状态有connecting->disconnect
broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
break;
}
synchronized (A2dpStateMachine.this) {
mTargetDevice = device;
transitionTo(mPending); // 切换状态机状态
}
// TODO(BT) remove CONNECT_TIMEOUT when the stack
// sends back events consistently
Message m = obtainMessage(CONNECT_TIMEOUT);
m.obj = device;
sendMessageDelayed(m, CONNECT_TIMEOUT_SEC);
break;
packages\apps\Bluetooth\jni\com_android_bluetooth_a2dp.cpp
static jboolean connectA2dpNative(JNIEnv* env, jobject object,
jbyteArray address) {
ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
if (!sBluetoothA2dpInterface) return JNI_FALSE;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return JNI_FALSE;
}
bt_status_t status = sBluetoothA2dpInterface->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;
}
初始化
static void initNative(JNIEnv* env, jobject object,
jobjectArray codecConfigArray,
jint maxA2dpConnection,
jint multiCastState) {
sBluetoothA2dpInterface =
(btav_source_interface_t*)btInf->get_profile_interface(
BT_PROFILE_ADVANCED_AUDIO_ID);
if (sBluetoothA2dpInterface == NULL) {
ALOGE("Failed to get Bluetooth A2DP Interface");
return;
}
Z:\eda51\system\bt\btif\src\bluetooth.cc
static const void* get_profile_interface(const char* profile_id) {
LOG_INFO(LOG_TAG, "%s: id = %s", __func__, profile_id);
/* sanity check */
if (interface_ready() == false) return NULL;
/* check for supported profile interfaces */
if (is_profile(profile_id, BT_PROFILE_HANDSFREE_ID))
return btif_hf_get_interface();
if (is_profile(profile_id, BT_PROFILE_HANDSFREE_CLIENT_ID))
return btif_hf_client_get_interface();
if (is_profile(profile_id, BT_PROFILE_SOCKETS_ID))
return btif_sock_get_interface();
if (is_profile(profile_id, BT_PROFILE_PAN_ID))
return btif_pan_get_interface();
// audio有两个,一个是作为source用
if (is_profile(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID))
return btif_av_get_src_interface();
// audio, 作为sink用
if (is_profile(profile_id, BT_PROFILE_ADVANCED_AUDIO_SINK_ID))
return btif_av_get_sink_interface();
system\bt\btif\src\btif_av.cc
const btav_source_interface_t* btif_av_get_src_interface(void) {
BTIF_TRACE_EVENT("%s", __func__);
return &bt_av_src_interface;
}
static const btav_source_interface_t bt_av_src_interface = {
sizeof(btav_source_interface_t),
init_src,
src_connect_sink, // 音频源连接sink,这个应该是对音频进行编码,然后输出
disconnect,
codec_config_src,
cleanup_src,
allow_connection,
select_audio_device,
};
static const btav_sink_interface_t bt_av_sink_interface = {
sizeof(btav_sink_interface_t),
init_sink,
sink_connect_src, // sink连接音频源, sink接受音频数据,解码进行播放
disconnect,
cleanup_sink,
update_audio_focus_state,
update_audio_track_gain,
};
Liu Tao
2019-3-28