记录一下蓝牙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; } 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, }; 连接成功之后,底层发送事件。 packages\apps\Bluetooth\src\com\android\bluetooth\a2dp\A2dpStateMachine.java private void onConnectionStateChanged(int state, byte[] address) { log("Enter onConnectionStateChanged() "); StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); event.valueInt = state; event.device = getDevice(address); sendMessage(STACK_EVENT, event); log("Exit onConnectionStateChanged() "); } private class Disconnected extends State { case STACK_EVENT: StackEvent event = (StackEvent) message.obj; switch (event.type) { case EVENT_TYPE_CONNECTION_STATE_CHANGED: processConnectionEvent(event.valueInt, event.device); break; default: loge("Unexpected stack event: " + event.type); break; private void processConnectionEvent(int state, BluetoothDevice device) { log("processConnectionEvent state = " + state + ", device = " + device); switch (state) { ... case CONNECTION_STATE_CONNECTED: logw("A2DP Connected from Disconnected state"); if (okToConnect(device)) { logi("Incoming A2DP accepted"); synchronized (A2dpStateMachine.this) { if (!mConnectedDevicesList.contains(device)) { mConnectedDevicesList.add(device); log( "device " + device.getAddress() + " is adding in Disconnected state"); } mCurrentDevice = device; transitionTo(mConnected); } broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); } else { //reject the connection and stay in Disconnected state itself logi("Incoming A2DP rejected"); disconnectA2dpNative(getByteAddress(device)); } break; private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { log("Enter broadcastConnectionState() "); int delay = 0; if (mDummyDevice == null) { Log.i(TAG, "Setting the dummy device for audio service: " + device); String dummyAddress = "FA:CE:FA:CE:FA:CE"; mDummyDevice = mAdapter.getRemoteDevice(dummyAddress); } if ((newState == BluetoothProfile.STATE_CONNECTED) || (newState == BluetoothProfile.STATE_DISCONNECTING)) { if (mConnectedDevicesList.size() == 1) { Log.d("A2dpStateMachine", "broadcasting connection state"); delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(mDummyDevice, newState, BluetoothProfile.A2DP); } else { Log.d("A2dpStateMachine", "DualA2dp: not broadcasting connected/disconnecting state"); } } else if ((newState == BluetoothProfile.STATE_DISCONNECTED) || (newState == BluetoothProfile.STATE_CONNECTING)) { if (mConnectedDevicesList.size() == 0) { Log.d("A2dpStateMachine", "broadcasting connection state"); delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(mDummyDevice, newState, BluetoothProfile.A2DP); } else { Log.d("A2dpStateMachine", "DualA2dp: not broadcasting connecting/disconnected state"); } } if (mCodecNotifPending && newState == BluetoothProfile.STATE_CONNECTED) { Log.i(TAG, "There was pending codec config change, dispatching now. " + "device: " + device); Log.i(TAG, "Sending broadcast with device " + mDummyDevice); Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED); intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, mCodecStatus); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDummyDevice); mAudioManager.handleBluetoothA2dpDeviceConfigChange(mDummyDevice); mCodecNotifPending = false; mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); } Log.i(TAG,"mLastDelay: " + mLastDelay + " Current_Delay: " + delay); if (mIntentBroadcastHandler.hasMessages(MSG_CONNECTION_STATE_CHANGED)) { Log.i(TAG," Braodcast handler has the pending messages: " ); if (mLastDelay > delay) { Log.i(TAG,"Last delay is greater than the current delay: " ); delay = mLastDelay; } } mLastDelay = delay; Log.i(TAG,"connection state change: " + device + " newState: " + newState + " prevState:" + prevState); mWakeLock.acquire(); // 发送广播 mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage( MSG_CONNECTION_STATE_CHANGED, prevState, newState, device), delay); log("Exit broadcastConnectionState() "); } broadcastConnectionState中会向AudioManager中设置A2DP的连接状态,返回值用来延时发送广播。AudioManager设置A2DP的连接状态非常重要,这样音频系统根据当前状态,判断音频从哪里发出(蓝牙a2dp、扬声器、耳机)。Liu Tao
2019-3-28