diff options
Diffstat (limited to 'src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java')
-rw-r--r-- | src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java | 138 |
1 files changed, 108 insertions, 30 deletions
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java index 66571c4e7..c319364c1 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java @@ -24,10 +24,10 @@ import android.content.Intent; import android.media.AudioManager; import android.media.MediaMetadata; import android.media.browse.MediaBrowser.MediaItem; -import android.media.session.MediaSession; -import android.media.session.PlaybackState; import android.os.Bundle; import android.os.Message; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.media.session.PlaybackStateCompat; import android.util.Log; import android.util.SparseArray; @@ -42,6 +42,7 @@ import com.android.internal.util.StateMachine; import java.util.ArrayList; import java.util.List; + /** * Provides Bluetooth AVRCP Controller State Machine responsible for all remote control connections * and interactions with a remote controlable device. @@ -76,11 +77,15 @@ class AvrcpControllerStateMachine extends StateMachine { static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 214; static final int MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED = 215; static final int MESSAGE_PROCESS_NOW_PLAYING_CONTENTS_CHANGED = 216; + static final int MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS = 217; + static final int MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS = 218; //300->399 Events for Browsing static final int MESSAGE_GET_FOLDER_ITEMS = 300; static final int MESSAGE_PLAY_ITEM = 301; static final int MSG_AVRCP_PASSTHRU = 302; + static final int MSG_AVRCP_SET_SHUFFLE = 303; + static final int MSG_AVRCP_SET_REPEAT = 304; static final int MESSAGE_INTERNAL_ABS_VOL_TIMEOUT = 404; @@ -218,26 +223,19 @@ class AvrcpControllerStateMachine extends StateMachine { mService.sBrowseTree.mRootNode.addChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService .sBrowseTree.mRootNode); - BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); mBrowsingConnected = true; } synchronized void onBrowsingDisconnected() { if (!mBrowsingConnected) return; - mAddressedPlayer.setPlayStatus(PlaybackState.STATE_ERROR); + mAddressedPlayer.setPlayStatus(PlaybackStateCompat.STATE_ERROR); mAddressedPlayer.updateCurrentTrack(null); mBrowseTree.mNowPlayingNode.setCached(false); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); - PlaybackState.Builder pbb = new PlaybackState.Builder(); - pbb.setState(PlaybackState.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN, - 1.0f).setActions(0); - pbb.setErrorMessage(mService.getString(R.string.bluetooth_disconnected)); - BluetoothMediaBrowserService.notifyChanged(pbb.build()); mService.sBrowseTree.mRootNode.removeChild( mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService .sBrowseTree.mRootNode); - BluetoothMediaBrowserService.trackChanged(null); mBrowsingConnected = false; } @@ -298,8 +296,9 @@ class AvrcpControllerStateMachine extends StateMachine { @Override public void enter() { if (mMostRecentState == BluetoothProfile.STATE_CONNECTING) { - broadcastConnectionStateChanged(BluetoothProfile.STATE_CONNECTED); BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); + BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); + broadcastConnectionStateChanged(BluetoothProfile.STATE_CONNECTED); } else { logD("ReEnteringConnected"); } @@ -315,14 +314,14 @@ class AvrcpControllerStateMachine extends StateMachine { removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT); sendMessageDelayed(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT, ABS_VOL_TIMEOUT_MILLIS); - setAbsVolume(msg.arg1, msg.arg2); + handleAbsVolumeRequest(msg.arg1, msg.arg2); return true; case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION: mVolumeNotificationLabel = msg.arg1; mService.sendRegisterAbsVolRspNative(mDeviceAddress, NOTIFICATION_RSP_TYPE_INTERIM, - getAbsVolumeResponse(), mVolumeNotificationLabel); + getAbsVolume(), mVolumeNotificationLabel); return true; case MESSAGE_GET_FOLDER_ITEMS: @@ -338,6 +337,14 @@ class AvrcpControllerStateMachine extends StateMachine { passThru(msg.arg1); return true; + case MSG_AVRCP_SET_REPEAT: + setRepeat(msg.arg1); + return true; + + case MSG_AVRCP_SET_SHUFFLE: + setShuffle(msg.arg1); + return true; + case MESSAGE_PROCESS_TRACK_CHANGED: mAddressedPlayer.updateCurrentTrack((MediaMetadata) msg.obj); BluetoothMediaBrowserService.trackChanged((MediaMetadata) msg.obj); @@ -347,11 +354,14 @@ class AvrcpControllerStateMachine extends StateMachine { mAddressedPlayer.setPlayStatus(msg.arg1); BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); if (mAddressedPlayer.getPlaybackState().getState() - == PlaybackState.STATE_PLAYING - && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE - && !shouldRequestFocus()) { + == PlaybackStateCompat.STATE_PLAYING + && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE) { + if (shouldRequestFocus()) { + mSessionCallbacks.onPrepare(); + } else { sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE); + } } return true; @@ -378,6 +388,18 @@ class AvrcpControllerStateMachine extends StateMachine { } return true; + case MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS: + mAddressedPlayer.setSupportedPlayerApplicationSettings( + (PlayerApplicationSettings) msg.obj); + BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); + return true; + + case MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS: + mAddressedPlayer.setCurrentPlayerApplicationSettings( + (PlayerApplicationSettings) msg.obj); + BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); + return true; + case DISCONNECT: transitionTo(mDisconnecting); return true; @@ -435,6 +457,20 @@ class AvrcpControllerStateMachine extends StateMachine { return (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND) || (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_FF); } + + private void setRepeat(int repeatMode) { + mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1, + new byte[]{PlayerApplicationSettings.REPEAT_STATUS}, new byte[]{ + PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal( + PlayerApplicationSettings.REPEAT_STATUS, repeatMode)}); + } + + private void setShuffle(int shuffleMode) { + mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1, + new byte[]{PlayerApplicationSettings.SHUFFLE_STATUS}, new byte[]{ + PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal( + PlayerApplicationSettings.SHUFFLE_STATUS, shuffleMode)}); + } } // Handle the get folder listing action @@ -554,7 +590,7 @@ class AvrcpControllerStateMachine extends StateMachine { case MESSAGE_GET_FOLDER_ITEMS: if (!mBrowseNode.equals(msg.obj)) { if (shouldAbort(mBrowseNode.getScope(), - ((BrowseTree.BrowseNode) msg.obj).getScope())) { + ((BrowseTree.BrowseNode) msg.obj).getScope())) { mAbort = true; } deferMessage(msg); @@ -576,8 +612,8 @@ class AvrcpControllerStateMachine extends StateMachine { * necessary. * * @return true: a new folder in the same scope - * a new player while fetching contents of a folder - * false: other cases, specifically Now Playing while fetching a folder + * a new player while fetching contents of a folder + * false: other cases, specifically Now Playing while fetching a folder */ private boolean shouldAbort(int currentScope, int fetchScope) { if ((currentScope == fetchScope) @@ -674,31 +710,60 @@ class AvrcpControllerStateMachine extends StateMachine { @Override public void enter() { onBrowsingDisconnected(); + BluetoothMediaBrowserService.trackChanged(null); + BluetoothMediaBrowserService.addressedPlayerChanged(null); broadcastConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTING); transitionTo(mDisconnected); } } + /** + * Handle a request to align our local volume with the volume of a remote device. If + * we're assuming the source volume is fixed then a response of ABS_VOL_MAX will always be + * sent and no volume adjustment action will be taken on the sink side. + * + * @param absVol A volume level based on a domain of [0, ABS_VOL_MAX] + * @param label Volume notification label + */ + private void handleAbsVolumeRequest(int absVol, int label) { + logD("handleAbsVolumeRequest: absVol = " + absVol + ", label = " + label); + if (mIsVolumeFixed) { + logD("Source volume is assumed to be fixed, responding with max volume"); + absVol = ABS_VOL_BASE; + } else { + mVolumeChangedNotificationsToIgnore++; + removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT); + sendMessageDelayed(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT, + ABS_VOL_TIMEOUT_MILLIS); + setAbsVolume(absVol); + } + mService.sendAbsVolRspNative(mDeviceAddress, absVol, label); + } + + /** + * Align our volume with a requested absolute volume level + * + * @param absVol A volume level based on a domain of [0, ABS_VOL_MAX] + */ + private void setAbsVolume(int absVol) { + int maxLocalVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int curLocalVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); + int reqLocalVolume = (maxLocalVolume * absVol) / ABS_VOL_BASE; + logD("setAbsVolme: absVol = " + absVol + ", reqLocal = " + reqLocalVolume + + ", curLocal = " + curLocalVolume + ", maxLocal = " + maxLocalVolume); - private void setAbsVolume(int absVol, int label) { - int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); - int currIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); - int newIndex = (maxVolume * absVol) / ABS_VOL_BASE; - logD(" setAbsVolume =" + absVol + " maxVol = " + maxVolume - + " cur = " + currIndex + " new = " + newIndex); /* * In some cases change in percentage is not sufficient enough to warrant * change in index values which are in range of 0-15. For such cases * no action is required */ - if (newIndex != currIndex) { - mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newIndex, + if (reqLocalVolume != curLocalVolume) { + mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, reqLocalVolume, AudioManager.FLAG_SHOW_UI); } - mService.sendAbsVolRspNative(mDeviceAddress, getAbsVolumeResponse(), label); } - private int getAbsVolumeResponse() { + private int getAbsVolume() { if (mIsVolumeFixed) { return ABS_VOL_BASE; } @@ -708,7 +773,7 @@ class AvrcpControllerStateMachine extends StateMachine { return newIndex; } - MediaSession.Callback mSessionCallbacks = new MediaSession.Callback() { + MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() { @Override public void onPlay() { logD("onPlay"); @@ -781,6 +846,19 @@ class AvrcpControllerStateMachine extends StateMachine { BrowseTree.BrowseNode node = mBrowseTree.findBrowseNodeByID(mediaId); sendMessage(MESSAGE_PLAY_ITEM, node); } + + @Override + public void onSetRepeatMode(int repeatMode) { + logD("onSetRepeatMode"); + sendMessage(MSG_AVRCP_SET_REPEAT, repeatMode); + } + + @Override + public void onSetShuffleMode(int shuffleMode) { + logD("onSetShuffleMode"); + sendMessage(MSG_AVRCP_SET_SHUFFLE, shuffleMode); + + } }; protected void broadcastConnectionStateChanged(int currentState) { |