記錄一下藍牙A2DP的流程java
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-28android