对于蓝牙音乐的播放状态,我们首先主要处理的是 onPlayStatusChanged() 回调,这是协议栈通知 FW 层的一个回调接口。还有一个就是 getPlayBackState() 方法,这是媒体应用在初始化时未收到回调信息主动获取当前状态的方法。我们这里就来分析一下这两个状态的获取流程。
一、状态回调
1、AvrcpControllerService
源码位置:/packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
onPlayStatusChanged
这个回调比较重要,直接接收协议栈返回的播放状态变化接口,很多 Bug 的播放状态分析都是以这个接口的状态为参考,所以记住这里打印的 LOG。
// Called by JNI on changes of play status
private synchronized void onPlayStatusChanged(byte[] address, byte playStatus) {if (DBG) {Log.d(TAG, "onPlayStatusChanged " + playStatus);}BluetoothDevice device = getAnonymousDevice(address);AvrcpControllerStateMachine stateMachine = getStateMachine(device);if (stateMachine != null) {stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,toPlaybackStateFromJni(playStatus));}
}
这里通过 toPlaybackStateFromJni() 接口将底层返回的播放状态转化为对应数据。然后发送 MESSAGE_PROCESS_PLAY_STATUS_CHANGED 消息到协议栈进行处理。
toPlaybackStateFromJni
private int toPlaybackStateFromJni(int fromJni) {int playbackState = PlaybackStateCompat.STATE_NONE;switch (fromJni) {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;}return playbackState;
}
3、AvrcpControllerStateMachine
源码位置:/packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
private AvrcpPlayer mAddressedPlayer;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();BluetoothMediaBrowserService.notifyChanged(playbackState);// 处理A2dp焦点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()) {mSessionCallbacks.onPrepare();} else {sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);}}return true;
}
这里处理 MESSAGE_PROCESS_PLAY_STATUS_CHANGED 消息,首先调用了 AvrcpPlayer 的 setPlayStatus() 方法保存播放状态,然后通过 BluetoothMediaBrowserService 继续通过回调接口返回当前状态,这个流程就与前面的播放进度回调一致了。这里我们主要关注播放状态的保存。
4、AvrcpPlayer
源码位置:/packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
private PlaybackStateCompat mPlaybackStateCompat;public void setPlayStatus(int playStatus) {if (mPlayTime != PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN) {mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime() - mPlaybackStateCompat.getLastPositionUpdateTime());}mPlayStatus = playStatus;switch (mPlayStatus) {case PlaybackStateCompat.STATE_STOPPED:mPlaySpeed = 0;break;case PlaybackStateCompat.STATE_PLAYING:mPlaySpeed = 1;break;case PlaybackStateCompat.STATE_PAUSED:mPlaySpeed = 0;break;case PlaybackStateCompat.STATE_FAST_FORWARDING:mPlaySpeed = 3;break;case PlaybackStateCompat.STATE_REWINDING:mPlaySpeed = -3;break;}mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState(mPlayStatus, mPlayTime, mPlaySpeed).build();
}
这里的播放状态转换成对应的数值,最终保存到 mPlaybackStateCompat 中。
二、获取播放状态
这里我们在来看一下 getPlaybackState() 获取播放状态的方法。
1、AvrcpPlayer
getPlaybackState
private int mPlayStatus = PlaybackStateCompat.STATE_NONE;
private PlaybackStateCompat mPlaybackStateCompat;public PlaybackStateCompat getPlaybackState() {if (DBG) {Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime);}return mPlaybackStateCompat;
}
AvrcpPlayer 主要包含有关远程播放器的信息,所以对于车机端来说这里是获取手机播放器播放状态的方法。在 getPlaybackState() 中值返回一个 PlaybackStateCompat 变量,而 PlaybackStateCompat 正是上面回调接口最终保存的播放状态。
所以对于播放状态还是要以 onPlayStatusChanged() 接口的回调信息为主,这才是协议栈实时反馈的最新状态。而 getPlaybackState() 只有在初始化的时候使用,这时由于 onPlayStatusChanged() 还未注册完成或者初始化时间段内播放状态没有变化,才通过 getPlaybackState() 方法主动获取。