關鍵類:
/packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
/packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
一、音樂播放狀態
CPP中通過JNI接口將接從手機端接收來的播放狀態回調到AvrcpControllerService的onPlayStatusChanged方法,然后通過狀態機AvrcpControllerStateMachine處理。
// Called by JNI on changes of play statusprivate synchronized void onPlayStatusChanged(byte[] address, byte playStatus) {if (DBG) {Log.d(TAG, "onPlayStatusChanged " + playStatus);}int playbackState = PlaybackStateCompat.STATE_NONE;switch (playStatus) {case JNI_PLAY_STATUS_STOPPED:playbackState = PlaybackStateCompat.STATE_STOPPED;break;case JNI_PLAY_STATUS_PLAYING:playbackState = PlaybackStateCompat.STATE_PLAYING;break;case JNI_PLAY_STATUS_PAUSED:playbackState = PlaybackStateCompat.STATE_PAUSED;break;case JNI_PLAY_STATUS_FWD_SEEK:playbackState = PlaybackStateCompat.STATE_FAST_FORWARDING;break;case JNI_PLAY_STATUS_REV_SEEK:playbackState = PlaybackStateCompat.STATE_REWINDING;break;default:playbackState = PlaybackStateCompat.STATE_NONE;}BluetoothDevice device = mAdapter.getRemoteDevice(address);AvrcpControllerStateMachine stateMachine = getStateMachine(device);if (stateMachine != null) {stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, playbackState);}}
AvrcpControllerStateMachine.java中Connected內部類的processMessage方法中,調BluetoothMediaBrowserService類的方法將播放狀態通知到UI應用,如果是播放狀態,要申請音頻焦點。
case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:mAddressedPlayer.setPlayStatus(msg.arg1);if (!isActive()) {sendMessage(MSG_AVRCP_PASSTHRU,AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);return true;}PlaybackStateCompat playbackState = mAddressedPlayer.getPlaybackState();// 將播放狀態通過mediasession通知給UIBluetoothMediaBrowserService.notifyChanged(playbackState);// 獲取當前的音頻焦點狀態int focusState = AudioManager.ERROR;A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();if (a2dpSinkService != null) {focusState = a2dpSinkService.getFocusState();}if (focusState == AudioManager.ERROR) {sendMessage(MSG_AVRCP_PASSTHRU,AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);return true;}if (playbackState.getState() == PlaybackStateCompat.STATE_PLAYING&& focusState == AudioManager.AUDIOFOCUS_NONE) {// 申請音頻焦點if (shouldRequestFocus()) {requestAudioFocus();} else {sendMessage(MSG_AVRCP_PASSTHRU,AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);}}return true;
BluetoothMediaBrowserService是MediaSession框架的一個服務,UI端通過MediaSession框架綁定該服務就可以監聽藍牙音頻的播放狀態,控制藍牙音頻的播放。
BluetoothMediaBrowserService的notifyChanged方法中通過MediaSession框架將播放狀態通知到UI端。
static synchronized void notifyChanged(PlaybackStateCompat playbackState) {Log.d(TAG, "notifyChanged PlaybackState" + playbackState);if (sBluetoothMediaBrowserService != null) {sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState);} else {Log.w(TAG, "notifyChanged Unavailable");}}
二、音頻焦點申請
上面播放狀態處理的邏輯中會調到A2dpSinkService的requestAudioFocus方法
/*** Request audio focus such that the designated device can stream audio*/public void requestAudioFocus(BluetoothDevice device, boolean request) {synchronized (mStreamHandlerLock) {if (mA2dpSinkStreamHandler == null) return;mA2dpSinkStreamHandler.requestAudioFocus(request);}}
A2dpSinkStreamHandler
private synchronized int requestAudioFocus() {if (DBG) Log.d(TAG, "requestAudioFocus()");// Bluetooth A2DP may carry Music, Audio Books, Navigation, or other sounds so mark content// type unknown.AudioAttributes streamAttributes =new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN).build();// Bluetooth ducking is handled at the native layer at the request of AudioManager.AudioFocusRequest focusRequest =new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).setAudioAttributes(streamAttributes).setOnAudioFocusChangeListener(mAudioFocusListener, this).build();int focusRequestStatus = mAudioManager.requestAudioFocus(focusRequest);// If the request is granted begin streaming immediately and schedule an upgrade.if (focusRequestStatus == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {startFluorideStreaming();mAudioFocus = AudioManager.AUDIOFOCUS_GAIN;}return focusRequestStatus;}
三、播放控制
UI通過MediaSession框架綁定到BluetoothMediaBrowserService服務,BluetoothMediaBrowserService中的MediaSessionCompat對象是關鍵,MediaSessionCompat.CallBack是專門接收MediaSession客戶端的指令并處理的。AvrcpControllerStateMachine的setActive方法中,會將MediaSessionCompat.CallBack對象傳給BluetoothMediaBrowserService的MediaSessionCompat。
BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks);
MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() {@Overridepublic void onPlay() {logD("onPlay");requestAudioFocus();sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_PLAY);}@Overridepublic void onPause() {logD("onPause");sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);}};
public boolean processMessage(Message msg) {logD(STATE_TAG + " processMessage " + msg.what);switch (msg.what) {case MSG_AVRCP_PASSTHRU:passThru(msg.arg1);return true;}
}
而后調AvrcpControllerService類的sendPassThroughCommandNative方法將指令通過JNI接口發給CPP層。