Loading Android.mk +1 −0 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_STATIC_ANDROID_LIBRARIES := \ LOCAL_STATIC_ANDROID_LIBRARIES := \ androidx.core_core \ androidx.core_core \ androidx.legacy_legacy-support-v4 \ androidx.lifecycle_lifecycle-livedata \ androidx.lifecycle_lifecycle-livedata \ androidx.room_room-runtime \ androidx.room_room-runtime \ bt-androidx-room-runtime-nodeps \ bt-androidx-room-runtime-nodeps \ Loading src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +18 −15 Original line number Original line Diff line number Diff line Loading @@ -412,9 +412,10 @@ public class AvrcpControllerService extends ProfileService { if (stateMachine != null) { if (stateMachine != null) { PlayerApplicationSettings supportedSettings = PlayerApplicationSettings supportedSettings = PlayerApplicationSettings.makeSupportedSettings(playerAttribRsp); PlayerApplicationSettings.makeSupportedSettings(playerAttribRsp); stateMachine.sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS, supportedSettings); } } /* Do nothing */ } } private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, Loading @@ -426,10 +427,12 @@ public class AvrcpControllerService extends ProfileService { AvrcpControllerStateMachine stateMachine = getStateMachine(device); AvrcpControllerStateMachine stateMachine = getStateMachine(device); if (stateMachine != null) { if (stateMachine != null) { PlayerApplicationSettings desiredSettings = PlayerApplicationSettings currentSettings = PlayerApplicationSettings.makeSettings(playerAttribRsp); PlayerApplicationSettings.makeSettings(playerAttribRsp); stateMachine.sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS, currentSettings); } } /* Do nothing */ } } // Browsing related JNI callbacks. // Browsing related JNI callbacks. Loading src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +63 −11 Original line number Original line Diff line number Diff line Loading @@ -24,10 +24,10 @@ import android.content.Intent; import android.media.AudioManager; import android.media.AudioManager; import android.media.MediaMetadata; import android.media.MediaMetadata; import android.media.browse.MediaBrowser.MediaItem; import android.media.browse.MediaBrowser.MediaItem; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Bundle; import android.os.Bundle; import android.os.Message; 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.Log; import android.util.SparseArray; import android.util.SparseArray; Loading @@ -42,6 +42,7 @@ import com.android.internal.util.StateMachine; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; import java.util.List; /** /** * Provides Bluetooth AVRCP Controller State Machine responsible for all remote control connections * Provides Bluetooth AVRCP Controller State Machine responsible for all remote control connections * and interactions with a remote controlable device. * and interactions with a remote controlable device. Loading Loading @@ -76,11 +77,15 @@ class AvrcpControllerStateMachine extends StateMachine { static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 214; static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 214; static final int MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED = 215; static final int MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED = 215; static final int MESSAGE_PROCESS_NOW_PLAYING_CONTENTS_CHANGED = 216; 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 //300->399 Events for Browsing static final int MESSAGE_GET_FOLDER_ITEMS = 300; static final int MESSAGE_GET_FOLDER_ITEMS = 300; static final int MESSAGE_PLAY_ITEM = 301; static final int MESSAGE_PLAY_ITEM = 301; static final int MSG_AVRCP_PASSTHRU = 302; 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; static final int MESSAGE_INTERNAL_ABS_VOL_TIMEOUT = 404; Loading Loading @@ -215,12 +220,12 @@ class AvrcpControllerStateMachine extends StateMachine { synchronized void onBrowsingDisconnected() { synchronized void onBrowsingDisconnected() { if (!mBrowsingConnected) return; if (!mBrowsingConnected) return; mAddressedPlayer.setPlayStatus(PlaybackState.STATE_ERROR); mAddressedPlayer.setPlayStatus(PlaybackStateCompat.STATE_ERROR); mAddressedPlayer.updateCurrentTrack(null); mAddressedPlayer.updateCurrentTrack(null); mBrowseTree.mNowPlayingNode.setCached(false); mBrowseTree.mNowPlayingNode.setCached(false); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); PlaybackState.Builder pbb = new PlaybackState.Builder(); PlaybackStateCompat.Builder pbb = new PlaybackStateCompat.Builder(); pbb.setState(PlaybackState.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN, pbb.setState(PlaybackStateCompat.STATE_ERROR, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0); 1.0f).setActions(0); pbb.setErrorMessage(mService.getString(R.string.bluetooth_disconnected)); pbb.setErrorMessage(mService.getString(R.string.bluetooth_disconnected)); BluetoothMediaBrowserService.notifyChanged(pbb.build()); BluetoothMediaBrowserService.notifyChanged(pbb.build()); Loading Loading @@ -322,6 +327,14 @@ class AvrcpControllerStateMachine extends StateMachine { passThru(msg.arg1); passThru(msg.arg1); return true; 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: case MESSAGE_PROCESS_TRACK_CHANGED: mAddressedPlayer.updateCurrentTrack((MediaMetadata) msg.obj); mAddressedPlayer.updateCurrentTrack((MediaMetadata) msg.obj); BluetoothMediaBrowserService.trackChanged((MediaMetadata) msg.obj); BluetoothMediaBrowserService.trackChanged((MediaMetadata) msg.obj); Loading @@ -331,7 +344,7 @@ class AvrcpControllerStateMachine extends StateMachine { mAddressedPlayer.setPlayStatus(msg.arg1); mAddressedPlayer.setPlayStatus(msg.arg1); BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); if (mAddressedPlayer.getPlaybackState().getState() if (mAddressedPlayer.getPlaybackState().getState() == PlaybackState.STATE_PLAYING == PlaybackStateCompat.STATE_PLAYING && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE && !shouldRequestFocus()) { && !shouldRequestFocus()) { sendMessage(MSG_AVRCP_PASSTHRU, sendMessage(MSG_AVRCP_PASSTHRU, Loading Loading @@ -362,6 +375,18 @@ class AvrcpControllerStateMachine extends StateMachine { } } return true; 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: case DISCONNECT: transitionTo(mDisconnecting); transitionTo(mDisconnecting); return true; return true; Loading Loading @@ -419,6 +444,20 @@ class AvrcpControllerStateMachine extends StateMachine { return (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND) return (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND) || (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_FF); || (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 // Handle the get folder listing action Loading Loading @@ -697,7 +736,7 @@ class AvrcpControllerStateMachine extends StateMachine { mService.sendAbsVolRspNative(mDeviceAddress, absVol, label); mService.sendAbsVolRspNative(mDeviceAddress, absVol, label); } } MediaSession.Callback mSessionCallbacks = new MediaSession.Callback() { MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() { @Override @Override public void onPlay() { public void onPlay() { logD("onPlay"); logD("onPlay"); Loading Loading @@ -770,6 +809,19 @@ class AvrcpControllerStateMachine extends StateMachine { BrowseTree.BrowseNode node = mBrowseTree.findBrowseNodeByID(mediaId); BrowseTree.BrowseNode node = mBrowseTree.findBrowseNodeByID(mediaId); sendMessage(MESSAGE_PLAY_ITEM, node); 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) { protected void broadcastConnectionStateChanged(int currentState) { Loading src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java +71 −31 Original line number Original line Diff line number Diff line Loading @@ -17,8 +17,9 @@ package com.android.bluetooth.avrcpcontroller; package com.android.bluetooth.avrcpcontroller; import android.media.MediaMetadata; import android.media.MediaMetadata; import android.media.session.PlaybackState; import android.os.SystemClock; import android.os.SystemClock; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; import android.util.Log; import android.util.Log; import java.util.Arrays; import java.util.Arrays; Loading @@ -41,27 +42,31 @@ class AvrcpPlayer { public static final int FEATURE_PREVIOUS = 48; public static final int FEATURE_PREVIOUS = 48; public static final int FEATURE_BROWSING = 59; public static final int FEATURE_BROWSING = 59; private int mPlayStatus = PlaybackState.STATE_NONE; private int mPlayStatus = PlaybackStateCompat.STATE_NONE; private long mPlayTime = PlaybackState.PLAYBACK_POSITION_UNKNOWN; private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN; private long mPlayTimeUpdate = 0; private long mPlayTimeUpdate = 0; private float mPlaySpeed = 1; private float mPlaySpeed = 1; private int mId; private int mId; private String mName = ""; private String mName = ""; private int mPlayerType; private int mPlayerType; private byte[] mPlayerFeatures; private byte[] mPlayerFeatures = new byte[16]; private long mAvailableActions; private long mAvailableActions; private MediaMetadata mCurrentTrack; private MediaMetadata mCurrentTrack; private PlaybackState mPlaybackState; private PlaybackStateCompat mPlaybackStateCompat; private PlayerApplicationSettings mSupportedPlayerApplicationSettings = new PlayerApplicationSettings(); private PlayerApplicationSettings mCurrentPlayerApplicationSettings; AvrcpPlayer() { AvrcpPlayer() { mId = INVALID_ID; mId = INVALID_ID; //Set Default Actions in case Player data isn't available. //Set Default Actions in case Player data isn't available. mAvailableActions = PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY mAvailableActions = PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_STOP; | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder() | PlaybackStateCompat.ACTION_STOP; PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder() .setActions(mAvailableActions); .setActions(mAvailableActions); mPlaybackState = playbackStateBuilder.build(); mPlaybackStateCompat = playbackStateBuilder.build(); } } AvrcpPlayer(int id, String name, byte[] playerFeatures, int playStatus, int playerType) { AvrcpPlayer(int id, String name, byte[] playerFeatures, int playStatus, int playerType) { Loading @@ -70,10 +75,10 @@ class AvrcpPlayer { mPlayStatus = playStatus; mPlayStatus = playStatus; mPlayerType = playerType; mPlayerType = playerType; mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length); mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length); updateAvailableActions(); PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder() PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder() .setActions(mAvailableActions); .setActions(mAvailableActions); mPlaybackState = playbackStateBuilder.build(); mPlaybackStateCompat = playbackStateBuilder.build(); updateAvailableActions(); } } public int getId() { public int getId() { Loading @@ -87,7 +92,8 @@ class AvrcpPlayer { public void setPlayTime(int playTime) { public void setPlayTime(int playTime) { mPlayTime = playTime; mPlayTime = playTime; mPlayTimeUpdate = SystemClock.elapsedRealtime(); mPlayTimeUpdate = SystemClock.elapsedRealtime(); mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime, mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState( mPlayStatus, mPlayTime, mPlaySpeed).build(); mPlaySpeed).build(); } } Loading @@ -97,30 +103,48 @@ class AvrcpPlayer { public void setPlayStatus(int playStatus) { public void setPlayStatus(int playStatus) { mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime() mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime() - mPlaybackState.getLastPositionUpdateTime()); - mPlaybackStateCompat.getLastPositionUpdateTime()); mPlayStatus = playStatus; mPlayStatus = playStatus; switch (mPlayStatus) { switch (mPlayStatus) { case PlaybackState.STATE_STOPPED: case PlaybackStateCompat.STATE_STOPPED: mPlaySpeed = 0; mPlaySpeed = 0; break; break; case PlaybackState.STATE_PLAYING: case PlaybackStateCompat.STATE_PLAYING: mPlaySpeed = 1; mPlaySpeed = 1; break; break; case PlaybackState.STATE_PAUSED: case PlaybackStateCompat.STATE_PAUSED: mPlaySpeed = 0; mPlaySpeed = 0; break; break; case PlaybackState.STATE_FAST_FORWARDING: case PlaybackStateCompat.STATE_FAST_FORWARDING: mPlaySpeed = 3; mPlaySpeed = 3; break; break; case PlaybackState.STATE_REWINDING: case PlaybackStateCompat.STATE_REWINDING: mPlaySpeed = -3; mPlaySpeed = -3; break; break; } } mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime, mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState( mPlayStatus, mPlayTime, mPlaySpeed).build(); mPlaySpeed).build(); } } public void setSupportedPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings) { mSupportedPlayerApplicationSettings = playerApplicationSettings; updateAvailableActions(); } public void setCurrentPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings) { Log.d(TAG, "Settings changed"); mCurrentPlayerApplicationSettings = playerApplicationSettings; MediaSessionCompat session = BluetoothMediaBrowserService.getSession(); session.setRepeatMode(mCurrentPlayerApplicationSettings.getSetting( PlayerApplicationSettings.REPEAT_STATUS)); session.setShuffleMode(mCurrentPlayerApplicationSettings.getSetting( PlayerApplicationSettings.SHUFFLE_STATUS)); } public int getPlayStatus() { public int getPlayStatus() { return mPlayStatus; return mPlayStatus; } } Loading @@ -131,17 +155,22 @@ class AvrcpPlayer { return (mPlayerFeatures[byteNumber] & bitMask) == bitMask; return (mPlayerFeatures[byteNumber] & bitMask) == bitMask; } } public PlaybackState getPlaybackState() { public boolean supportsSetting(int settingType, int settingValue) { return mSupportedPlayerApplicationSettings.supportsSetting(settingType, settingValue); } public PlaybackStateCompat getPlaybackState() { if (DBG) { if (DBG) { Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime); Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime); } } return mPlaybackState; return mPlaybackStateCompat; } } public synchronized void updateCurrentTrack(MediaMetadata update) { public synchronized void updateCurrentTrack(MediaMetadata update) { if (update != null) { if (update != null) { long trackNumber = update.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER); long trackNumber = update.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER); mPlaybackState = new PlaybackState.Builder(mPlaybackState).setActiveQueueItemId( mPlaybackStateCompat = new PlaybackStateCompat.Builder( mPlaybackStateCompat).setActiveQueueItemId( trackNumber - 1).build(); trackNumber - 1).build(); } } mCurrentTrack = update; mCurrentTrack = update; Loading @@ -153,26 +182,37 @@ class AvrcpPlayer { private void updateAvailableActions() { private void updateAvailableActions() { if (supportsFeature(FEATURE_PLAY)) { if (supportsFeature(FEATURE_PLAY)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_PLAY; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PLAY; } } if (supportsFeature(FEATURE_STOP)) { if (supportsFeature(FEATURE_STOP)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_STOP; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_STOP; } } if (supportsFeature(FEATURE_PAUSE)) { if (supportsFeature(FEATURE_PAUSE)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_PAUSE; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PAUSE; } } if (supportsFeature(FEATURE_REWIND)) { if (supportsFeature(FEATURE_REWIND)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_REWIND; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_REWIND; } } if (supportsFeature(FEATURE_FAST_FORWARD)) { if (supportsFeature(FEATURE_FAST_FORWARD)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_FAST_FORWARD; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_FAST_FORWARD; } } if (supportsFeature(FEATURE_FORWARD)) { if (supportsFeature(FEATURE_FORWARD)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_NEXT; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_NEXT; } } if (supportsFeature(FEATURE_PREVIOUS)) { if (supportsFeature(FEATURE_PREVIOUS)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_PREVIOUS; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; } } if (mSupportedPlayerApplicationSettings.supportsSetting( PlayerApplicationSettings.REPEAT_STATUS)) { mAvailableActions |= PlaybackStateCompat.ACTION_SET_REPEAT_MODE; } if (mSupportedPlayerApplicationSettings.supportsSetting( PlayerApplicationSettings.SHUFFLE_STATUS)) { mAvailableActions |= PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE; } mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat) .setActions(mAvailableActions).build(); if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions); if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions); } } } } src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java +46 −26 Original line number Original line Diff line number Diff line Loading @@ -18,13 +18,17 @@ package com.android.bluetooth.avrcpcontroller; import android.media.MediaMetadata; import android.media.MediaMetadata; import android.media.browse.MediaBrowser.MediaItem; import android.media.browse.MediaBrowser.MediaItem; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Bundle; import android.os.Bundle; import android.service.media.MediaBrowserService; import android.support.v4.media.MediaBrowserCompat; import android.support.v4.media.MediaDescriptionCompat; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaControllerCompat; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; import android.util.Log; import android.util.Log; import androidx.media.MediaBrowserServiceCompat; import com.android.bluetooth.R; import com.android.bluetooth.R; import java.util.ArrayList; import java.util.ArrayList; Loading @@ -37,43 +41,44 @@ import java.util.List; * The applications are expected to use MediaBrowser (see API) and all the music * The applications are expected to use MediaBrowser (see API) and all the music * browsing/playback/metadata can be controlled via MediaBrowser and MediaController. * browsing/playback/metadata can be controlled via MediaBrowser and MediaController. * * * The current behavior of MediaSession exposed by this service is as follows: * The current behavior of MediaSessionCompat exposed by this service is as follows: * 1. MediaSession is active (i.e. SystemUI and other overview UIs can see updates) when device is * 1. MediaSessionCompat is active (i.e. SystemUI and other overview UIs can see updates) when * connected and first starts playing. Before it starts playing we do not active the session. * device is connected and first starts playing. Before it starts playing we do not activate the * session. * 1.1 The session is active throughout the duration of connection. * 1.1 The session is active throughout the duration of connection. * 2. The session is de-activated when the device disconnects. It will be connected again when (1) * 2. The session is de-activated when the device disconnects. It will be connected again when (1) * happens. * happens. */ */ public class BluetoothMediaBrowserService extends MediaBrowserService { public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { private static final String TAG = "BluetoothMediaBrowserService"; private static final String TAG = "BluetoothMediaBrowserService"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static BluetoothMediaBrowserService sBluetoothMediaBrowserService; private static BluetoothMediaBrowserService sBluetoothMediaBrowserService; private MediaSession mSession; private MediaSessionCompat mSession; // Browsing related structures. // Browsing related structures. private List<MediaSession.QueueItem> mMediaQueue = new ArrayList<>(); private List<MediaSessionCompat.QueueItem> mMediaQueue = new ArrayList<>(); /** /** * Initialize this BluetoothMediaBrowserService, creating our MediaSession, MediaPlayer and * Initialize this BluetoothMediaBrowserService, creating our MediaSessionCompat, MediaPlayer * MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService. * and MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService. */ */ @Override @Override public void onCreate() { public void onCreate() { if (DBG) Log.d(TAG, "onCreate"); if (DBG) Log.d(TAG, "onCreate"); super.onCreate(); super.onCreate(); // Create and configure the MediaSession // Create and configure the MediaSessionCompat mSession = new MediaSession(this, TAG); mSession = new MediaSessionCompat(this, TAG); setSessionToken(mSession.getSessionToken()); setSessionToken(mSession.getSessionToken()); mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name)); mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name)); mSession.setQueue(mMediaQueue); mSession.setQueue(mMediaQueue); PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder(); PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder(); playbackStateBuilder.setState(PlaybackState.STATE_ERROR, playbackStateBuilder.setState(PlaybackStateCompat.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0); PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0); playbackStateBuilder.setErrorMessage(getString(R.string.bluetooth_disconnected)); playbackStateBuilder.setErrorMessage(getString(R.string.bluetooth_disconnected)); mSession.setPlaybackState(playbackStateBuilder.build()); mSession.setPlaybackState(playbackStateBuilder.build()); sBluetoothMediaBrowserService = this; sBluetoothMediaBrowserService = this; Loading @@ -91,9 +96,10 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { @Override @Override public synchronized void onLoadChildren(final String parentMediaId, public synchronized void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) { final Result<List<MediaBrowserCompat.MediaItem>> result) { if (DBG) Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId); if (DBG) Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId); List<MediaItem> contents = getContents(parentMediaId); List<MediaBrowserCompat.MediaItem> contents = MediaBrowserCompat.MediaItem.fromMediaItemList(getContents(parentMediaId)); if (contents == null) { if (contents == null) { result.detach(); result.detach(); } else { } else { Loading @@ -112,7 +118,8 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { mMediaQueue.clear(); mMediaQueue.clear(); if (songList != null) { if (songList != null) { for (MediaItem song : songList) { for (MediaItem song : songList) { mMediaQueue.add(new MediaSession.QueueItem(song.getDescription(), mMediaQueue.add(new MediaSessionCompat.QueueItem( MediaDescriptionCompat.fromMediaDescription(song.getDescription()), mMediaQueue.size())); mMediaQueue.size())); } } } } Loading @@ -129,7 +136,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { } } } } static synchronized void addressedPlayerChanged(MediaSession.Callback callback) { static synchronized void addressedPlayerChanged(MediaSessionCompat.Callback callback) { if (sBluetoothMediaBrowserService != null) { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.setCallback(callback); sBluetoothMediaBrowserService.mSession.setCallback(callback); } else { } else { Loading @@ -139,13 +146,14 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { static synchronized void trackChanged(MediaMetadata mediaMetadata) { static synchronized void trackChanged(MediaMetadata mediaMetadata) { if (sBluetoothMediaBrowserService != null) { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.setMetadata(mediaMetadata); sBluetoothMediaBrowserService.mSession.setMetadata( MediaMetadataCompat.fromMediaMetadata(mediaMetadata)); } else { } else { Log.w(TAG, "trackChanged Unavailable"); Log.w(TAG, "trackChanged Unavailable"); } } } } static synchronized void notifyChanged(PlaybackState playbackState) { static synchronized void notifyChanged(PlaybackStateCompat playbackState) { Log.d(TAG, "notifyChanged PlaybackState" + playbackState); Log.d(TAG, "notifyChanged PlaybackState" + playbackState); if (sBluetoothMediaBrowserService != null) { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState); sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState); Loading Loading @@ -179,7 +187,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { /** /** * Get object for controlling playback * Get object for controlling playback */ */ public static synchronized MediaController.TransportControls getTransportControls() { public static synchronized MediaControllerCompat.TransportControls getTransportControls() { if (sBluetoothMediaBrowserService != null) { if (sBluetoothMediaBrowserService != null) { return sBluetoothMediaBrowserService.mSession.getController().getTransportControls(); return sBluetoothMediaBrowserService.mSession.getController().getTransportControls(); } else { } else { Loading @@ -198,4 +206,16 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { Log.w(TAG, "setActive Unavailable"); Log.w(TAG, "setActive Unavailable"); } } } } /** * Get Media session for updating state */ public static synchronized MediaSessionCompat getSession() { if (sBluetoothMediaBrowserService != null) { return sBluetoothMediaBrowserService.mSession; } else { Log.w(TAG, "getSession Unavailable"); return null; } } } } Loading
Android.mk +1 −0 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_STATIC_ANDROID_LIBRARIES := \ LOCAL_STATIC_ANDROID_LIBRARIES := \ androidx.core_core \ androidx.core_core \ androidx.legacy_legacy-support-v4 \ androidx.lifecycle_lifecycle-livedata \ androidx.lifecycle_lifecycle-livedata \ androidx.room_room-runtime \ androidx.room_room-runtime \ bt-androidx-room-runtime-nodeps \ bt-androidx-room-runtime-nodeps \ Loading
src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +18 −15 Original line number Original line Diff line number Diff line Loading @@ -412,9 +412,10 @@ public class AvrcpControllerService extends ProfileService { if (stateMachine != null) { if (stateMachine != null) { PlayerApplicationSettings supportedSettings = PlayerApplicationSettings supportedSettings = PlayerApplicationSettings.makeSupportedSettings(playerAttribRsp); PlayerApplicationSettings.makeSupportedSettings(playerAttribRsp); stateMachine.sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS, supportedSettings); } } /* Do nothing */ } } private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, Loading @@ -426,10 +427,12 @@ public class AvrcpControllerService extends ProfileService { AvrcpControllerStateMachine stateMachine = getStateMachine(device); AvrcpControllerStateMachine stateMachine = getStateMachine(device); if (stateMachine != null) { if (stateMachine != null) { PlayerApplicationSettings desiredSettings = PlayerApplicationSettings currentSettings = PlayerApplicationSettings.makeSettings(playerAttribRsp); PlayerApplicationSettings.makeSettings(playerAttribRsp); stateMachine.sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS, currentSettings); } } /* Do nothing */ } } // Browsing related JNI callbacks. // Browsing related JNI callbacks. Loading
src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +63 −11 Original line number Original line Diff line number Diff line Loading @@ -24,10 +24,10 @@ import android.content.Intent; import android.media.AudioManager; import android.media.AudioManager; import android.media.MediaMetadata; import android.media.MediaMetadata; import android.media.browse.MediaBrowser.MediaItem; import android.media.browse.MediaBrowser.MediaItem; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Bundle; import android.os.Bundle; import android.os.Message; 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.Log; import android.util.SparseArray; import android.util.SparseArray; Loading @@ -42,6 +42,7 @@ import com.android.internal.util.StateMachine; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; import java.util.List; /** /** * Provides Bluetooth AVRCP Controller State Machine responsible for all remote control connections * Provides Bluetooth AVRCP Controller State Machine responsible for all remote control connections * and interactions with a remote controlable device. * and interactions with a remote controlable device. Loading Loading @@ -76,11 +77,15 @@ class AvrcpControllerStateMachine extends StateMachine { static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 214; static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 214; static final int MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED = 215; static final int MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED = 215; static final int MESSAGE_PROCESS_NOW_PLAYING_CONTENTS_CHANGED = 216; 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 //300->399 Events for Browsing static final int MESSAGE_GET_FOLDER_ITEMS = 300; static final int MESSAGE_GET_FOLDER_ITEMS = 300; static final int MESSAGE_PLAY_ITEM = 301; static final int MESSAGE_PLAY_ITEM = 301; static final int MSG_AVRCP_PASSTHRU = 302; 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; static final int MESSAGE_INTERNAL_ABS_VOL_TIMEOUT = 404; Loading Loading @@ -215,12 +220,12 @@ class AvrcpControllerStateMachine extends StateMachine { synchronized void onBrowsingDisconnected() { synchronized void onBrowsingDisconnected() { if (!mBrowsingConnected) return; if (!mBrowsingConnected) return; mAddressedPlayer.setPlayStatus(PlaybackState.STATE_ERROR); mAddressedPlayer.setPlayStatus(PlaybackStateCompat.STATE_ERROR); mAddressedPlayer.updateCurrentTrack(null); mAddressedPlayer.updateCurrentTrack(null); mBrowseTree.mNowPlayingNode.setCached(false); mBrowseTree.mNowPlayingNode.setCached(false); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); PlaybackState.Builder pbb = new PlaybackState.Builder(); PlaybackStateCompat.Builder pbb = new PlaybackStateCompat.Builder(); pbb.setState(PlaybackState.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN, pbb.setState(PlaybackStateCompat.STATE_ERROR, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0); 1.0f).setActions(0); pbb.setErrorMessage(mService.getString(R.string.bluetooth_disconnected)); pbb.setErrorMessage(mService.getString(R.string.bluetooth_disconnected)); BluetoothMediaBrowserService.notifyChanged(pbb.build()); BluetoothMediaBrowserService.notifyChanged(pbb.build()); Loading Loading @@ -322,6 +327,14 @@ class AvrcpControllerStateMachine extends StateMachine { passThru(msg.arg1); passThru(msg.arg1); return true; 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: case MESSAGE_PROCESS_TRACK_CHANGED: mAddressedPlayer.updateCurrentTrack((MediaMetadata) msg.obj); mAddressedPlayer.updateCurrentTrack((MediaMetadata) msg.obj); BluetoothMediaBrowserService.trackChanged((MediaMetadata) msg.obj); BluetoothMediaBrowserService.trackChanged((MediaMetadata) msg.obj); Loading @@ -331,7 +344,7 @@ class AvrcpControllerStateMachine extends StateMachine { mAddressedPlayer.setPlayStatus(msg.arg1); mAddressedPlayer.setPlayStatus(msg.arg1); BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); if (mAddressedPlayer.getPlaybackState().getState() if (mAddressedPlayer.getPlaybackState().getState() == PlaybackState.STATE_PLAYING == PlaybackStateCompat.STATE_PLAYING && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE && !shouldRequestFocus()) { && !shouldRequestFocus()) { sendMessage(MSG_AVRCP_PASSTHRU, sendMessage(MSG_AVRCP_PASSTHRU, Loading Loading @@ -362,6 +375,18 @@ class AvrcpControllerStateMachine extends StateMachine { } } return true; 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: case DISCONNECT: transitionTo(mDisconnecting); transitionTo(mDisconnecting); return true; return true; Loading Loading @@ -419,6 +444,20 @@ class AvrcpControllerStateMachine extends StateMachine { return (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND) return (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND) || (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_FF); || (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 // Handle the get folder listing action Loading Loading @@ -697,7 +736,7 @@ class AvrcpControllerStateMachine extends StateMachine { mService.sendAbsVolRspNative(mDeviceAddress, absVol, label); mService.sendAbsVolRspNative(mDeviceAddress, absVol, label); } } MediaSession.Callback mSessionCallbacks = new MediaSession.Callback() { MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() { @Override @Override public void onPlay() { public void onPlay() { logD("onPlay"); logD("onPlay"); Loading Loading @@ -770,6 +809,19 @@ class AvrcpControllerStateMachine extends StateMachine { BrowseTree.BrowseNode node = mBrowseTree.findBrowseNodeByID(mediaId); BrowseTree.BrowseNode node = mBrowseTree.findBrowseNodeByID(mediaId); sendMessage(MESSAGE_PLAY_ITEM, node); 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) { protected void broadcastConnectionStateChanged(int currentState) { Loading
src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java +71 −31 Original line number Original line Diff line number Diff line Loading @@ -17,8 +17,9 @@ package com.android.bluetooth.avrcpcontroller; package com.android.bluetooth.avrcpcontroller; import android.media.MediaMetadata; import android.media.MediaMetadata; import android.media.session.PlaybackState; import android.os.SystemClock; import android.os.SystemClock; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; import android.util.Log; import android.util.Log; import java.util.Arrays; import java.util.Arrays; Loading @@ -41,27 +42,31 @@ class AvrcpPlayer { public static final int FEATURE_PREVIOUS = 48; public static final int FEATURE_PREVIOUS = 48; public static final int FEATURE_BROWSING = 59; public static final int FEATURE_BROWSING = 59; private int mPlayStatus = PlaybackState.STATE_NONE; private int mPlayStatus = PlaybackStateCompat.STATE_NONE; private long mPlayTime = PlaybackState.PLAYBACK_POSITION_UNKNOWN; private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN; private long mPlayTimeUpdate = 0; private long mPlayTimeUpdate = 0; private float mPlaySpeed = 1; private float mPlaySpeed = 1; private int mId; private int mId; private String mName = ""; private String mName = ""; private int mPlayerType; private int mPlayerType; private byte[] mPlayerFeatures; private byte[] mPlayerFeatures = new byte[16]; private long mAvailableActions; private long mAvailableActions; private MediaMetadata mCurrentTrack; private MediaMetadata mCurrentTrack; private PlaybackState mPlaybackState; private PlaybackStateCompat mPlaybackStateCompat; private PlayerApplicationSettings mSupportedPlayerApplicationSettings = new PlayerApplicationSettings(); private PlayerApplicationSettings mCurrentPlayerApplicationSettings; AvrcpPlayer() { AvrcpPlayer() { mId = INVALID_ID; mId = INVALID_ID; //Set Default Actions in case Player data isn't available. //Set Default Actions in case Player data isn't available. mAvailableActions = PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY mAvailableActions = PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_STOP; | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder() | PlaybackStateCompat.ACTION_STOP; PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder() .setActions(mAvailableActions); .setActions(mAvailableActions); mPlaybackState = playbackStateBuilder.build(); mPlaybackStateCompat = playbackStateBuilder.build(); } } AvrcpPlayer(int id, String name, byte[] playerFeatures, int playStatus, int playerType) { AvrcpPlayer(int id, String name, byte[] playerFeatures, int playStatus, int playerType) { Loading @@ -70,10 +75,10 @@ class AvrcpPlayer { mPlayStatus = playStatus; mPlayStatus = playStatus; mPlayerType = playerType; mPlayerType = playerType; mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length); mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length); updateAvailableActions(); PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder() PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder() .setActions(mAvailableActions); .setActions(mAvailableActions); mPlaybackState = playbackStateBuilder.build(); mPlaybackStateCompat = playbackStateBuilder.build(); updateAvailableActions(); } } public int getId() { public int getId() { Loading @@ -87,7 +92,8 @@ class AvrcpPlayer { public void setPlayTime(int playTime) { public void setPlayTime(int playTime) { mPlayTime = playTime; mPlayTime = playTime; mPlayTimeUpdate = SystemClock.elapsedRealtime(); mPlayTimeUpdate = SystemClock.elapsedRealtime(); mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime, mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState( mPlayStatus, mPlayTime, mPlaySpeed).build(); mPlaySpeed).build(); } } Loading @@ -97,30 +103,48 @@ class AvrcpPlayer { public void setPlayStatus(int playStatus) { public void setPlayStatus(int playStatus) { mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime() mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime() - mPlaybackState.getLastPositionUpdateTime()); - mPlaybackStateCompat.getLastPositionUpdateTime()); mPlayStatus = playStatus; mPlayStatus = playStatus; switch (mPlayStatus) { switch (mPlayStatus) { case PlaybackState.STATE_STOPPED: case PlaybackStateCompat.STATE_STOPPED: mPlaySpeed = 0; mPlaySpeed = 0; break; break; case PlaybackState.STATE_PLAYING: case PlaybackStateCompat.STATE_PLAYING: mPlaySpeed = 1; mPlaySpeed = 1; break; break; case PlaybackState.STATE_PAUSED: case PlaybackStateCompat.STATE_PAUSED: mPlaySpeed = 0; mPlaySpeed = 0; break; break; case PlaybackState.STATE_FAST_FORWARDING: case PlaybackStateCompat.STATE_FAST_FORWARDING: mPlaySpeed = 3; mPlaySpeed = 3; break; break; case PlaybackState.STATE_REWINDING: case PlaybackStateCompat.STATE_REWINDING: mPlaySpeed = -3; mPlaySpeed = -3; break; break; } } mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime, mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState( mPlayStatus, mPlayTime, mPlaySpeed).build(); mPlaySpeed).build(); } } public void setSupportedPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings) { mSupportedPlayerApplicationSettings = playerApplicationSettings; updateAvailableActions(); } public void setCurrentPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings) { Log.d(TAG, "Settings changed"); mCurrentPlayerApplicationSettings = playerApplicationSettings; MediaSessionCompat session = BluetoothMediaBrowserService.getSession(); session.setRepeatMode(mCurrentPlayerApplicationSettings.getSetting( PlayerApplicationSettings.REPEAT_STATUS)); session.setShuffleMode(mCurrentPlayerApplicationSettings.getSetting( PlayerApplicationSettings.SHUFFLE_STATUS)); } public int getPlayStatus() { public int getPlayStatus() { return mPlayStatus; return mPlayStatus; } } Loading @@ -131,17 +155,22 @@ class AvrcpPlayer { return (mPlayerFeatures[byteNumber] & bitMask) == bitMask; return (mPlayerFeatures[byteNumber] & bitMask) == bitMask; } } public PlaybackState getPlaybackState() { public boolean supportsSetting(int settingType, int settingValue) { return mSupportedPlayerApplicationSettings.supportsSetting(settingType, settingValue); } public PlaybackStateCompat getPlaybackState() { if (DBG) { if (DBG) { Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime); Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime); } } return mPlaybackState; return mPlaybackStateCompat; } } public synchronized void updateCurrentTrack(MediaMetadata update) { public synchronized void updateCurrentTrack(MediaMetadata update) { if (update != null) { if (update != null) { long trackNumber = update.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER); long trackNumber = update.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER); mPlaybackState = new PlaybackState.Builder(mPlaybackState).setActiveQueueItemId( mPlaybackStateCompat = new PlaybackStateCompat.Builder( mPlaybackStateCompat).setActiveQueueItemId( trackNumber - 1).build(); trackNumber - 1).build(); } } mCurrentTrack = update; mCurrentTrack = update; Loading @@ -153,26 +182,37 @@ class AvrcpPlayer { private void updateAvailableActions() { private void updateAvailableActions() { if (supportsFeature(FEATURE_PLAY)) { if (supportsFeature(FEATURE_PLAY)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_PLAY; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PLAY; } } if (supportsFeature(FEATURE_STOP)) { if (supportsFeature(FEATURE_STOP)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_STOP; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_STOP; } } if (supportsFeature(FEATURE_PAUSE)) { if (supportsFeature(FEATURE_PAUSE)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_PAUSE; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PAUSE; } } if (supportsFeature(FEATURE_REWIND)) { if (supportsFeature(FEATURE_REWIND)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_REWIND; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_REWIND; } } if (supportsFeature(FEATURE_FAST_FORWARD)) { if (supportsFeature(FEATURE_FAST_FORWARD)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_FAST_FORWARD; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_FAST_FORWARD; } } if (supportsFeature(FEATURE_FORWARD)) { if (supportsFeature(FEATURE_FORWARD)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_NEXT; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_NEXT; } } if (supportsFeature(FEATURE_PREVIOUS)) { if (supportsFeature(FEATURE_PREVIOUS)) { mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_PREVIOUS; mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; } } if (mSupportedPlayerApplicationSettings.supportsSetting( PlayerApplicationSettings.REPEAT_STATUS)) { mAvailableActions |= PlaybackStateCompat.ACTION_SET_REPEAT_MODE; } if (mSupportedPlayerApplicationSettings.supportsSetting( PlayerApplicationSettings.SHUFFLE_STATUS)) { mAvailableActions |= PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE; } mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat) .setActions(mAvailableActions).build(); if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions); if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions); } } } }
src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java +46 −26 Original line number Original line Diff line number Diff line Loading @@ -18,13 +18,17 @@ package com.android.bluetooth.avrcpcontroller; import android.media.MediaMetadata; import android.media.MediaMetadata; import android.media.browse.MediaBrowser.MediaItem; import android.media.browse.MediaBrowser.MediaItem; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Bundle; import android.os.Bundle; import android.service.media.MediaBrowserService; import android.support.v4.media.MediaBrowserCompat; import android.support.v4.media.MediaDescriptionCompat; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaControllerCompat; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; import android.util.Log; import android.util.Log; import androidx.media.MediaBrowserServiceCompat; import com.android.bluetooth.R; import com.android.bluetooth.R; import java.util.ArrayList; import java.util.ArrayList; Loading @@ -37,43 +41,44 @@ import java.util.List; * The applications are expected to use MediaBrowser (see API) and all the music * The applications are expected to use MediaBrowser (see API) and all the music * browsing/playback/metadata can be controlled via MediaBrowser and MediaController. * browsing/playback/metadata can be controlled via MediaBrowser and MediaController. * * * The current behavior of MediaSession exposed by this service is as follows: * The current behavior of MediaSessionCompat exposed by this service is as follows: * 1. MediaSession is active (i.e. SystemUI and other overview UIs can see updates) when device is * 1. MediaSessionCompat is active (i.e. SystemUI and other overview UIs can see updates) when * connected and first starts playing. Before it starts playing we do not active the session. * device is connected and first starts playing. Before it starts playing we do not activate the * session. * 1.1 The session is active throughout the duration of connection. * 1.1 The session is active throughout the duration of connection. * 2. The session is de-activated when the device disconnects. It will be connected again when (1) * 2. The session is de-activated when the device disconnects. It will be connected again when (1) * happens. * happens. */ */ public class BluetoothMediaBrowserService extends MediaBrowserService { public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { private static final String TAG = "BluetoothMediaBrowserService"; private static final String TAG = "BluetoothMediaBrowserService"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static BluetoothMediaBrowserService sBluetoothMediaBrowserService; private static BluetoothMediaBrowserService sBluetoothMediaBrowserService; private MediaSession mSession; private MediaSessionCompat mSession; // Browsing related structures. // Browsing related structures. private List<MediaSession.QueueItem> mMediaQueue = new ArrayList<>(); private List<MediaSessionCompat.QueueItem> mMediaQueue = new ArrayList<>(); /** /** * Initialize this BluetoothMediaBrowserService, creating our MediaSession, MediaPlayer and * Initialize this BluetoothMediaBrowserService, creating our MediaSessionCompat, MediaPlayer * MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService. * and MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService. */ */ @Override @Override public void onCreate() { public void onCreate() { if (DBG) Log.d(TAG, "onCreate"); if (DBG) Log.d(TAG, "onCreate"); super.onCreate(); super.onCreate(); // Create and configure the MediaSession // Create and configure the MediaSessionCompat mSession = new MediaSession(this, TAG); mSession = new MediaSessionCompat(this, TAG); setSessionToken(mSession.getSessionToken()); setSessionToken(mSession.getSessionToken()); mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name)); mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name)); mSession.setQueue(mMediaQueue); mSession.setQueue(mMediaQueue); PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder(); PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder(); playbackStateBuilder.setState(PlaybackState.STATE_ERROR, playbackStateBuilder.setState(PlaybackStateCompat.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0); PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0); playbackStateBuilder.setErrorMessage(getString(R.string.bluetooth_disconnected)); playbackStateBuilder.setErrorMessage(getString(R.string.bluetooth_disconnected)); mSession.setPlaybackState(playbackStateBuilder.build()); mSession.setPlaybackState(playbackStateBuilder.build()); sBluetoothMediaBrowserService = this; sBluetoothMediaBrowserService = this; Loading @@ -91,9 +96,10 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { @Override @Override public synchronized void onLoadChildren(final String parentMediaId, public synchronized void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) { final Result<List<MediaBrowserCompat.MediaItem>> result) { if (DBG) Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId); if (DBG) Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId); List<MediaItem> contents = getContents(parentMediaId); List<MediaBrowserCompat.MediaItem> contents = MediaBrowserCompat.MediaItem.fromMediaItemList(getContents(parentMediaId)); if (contents == null) { if (contents == null) { result.detach(); result.detach(); } else { } else { Loading @@ -112,7 +118,8 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { mMediaQueue.clear(); mMediaQueue.clear(); if (songList != null) { if (songList != null) { for (MediaItem song : songList) { for (MediaItem song : songList) { mMediaQueue.add(new MediaSession.QueueItem(song.getDescription(), mMediaQueue.add(new MediaSessionCompat.QueueItem( MediaDescriptionCompat.fromMediaDescription(song.getDescription()), mMediaQueue.size())); mMediaQueue.size())); } } } } Loading @@ -129,7 +136,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { } } } } static synchronized void addressedPlayerChanged(MediaSession.Callback callback) { static synchronized void addressedPlayerChanged(MediaSessionCompat.Callback callback) { if (sBluetoothMediaBrowserService != null) { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.setCallback(callback); sBluetoothMediaBrowserService.mSession.setCallback(callback); } else { } else { Loading @@ -139,13 +146,14 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { static synchronized void trackChanged(MediaMetadata mediaMetadata) { static synchronized void trackChanged(MediaMetadata mediaMetadata) { if (sBluetoothMediaBrowserService != null) { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.setMetadata(mediaMetadata); sBluetoothMediaBrowserService.mSession.setMetadata( MediaMetadataCompat.fromMediaMetadata(mediaMetadata)); } else { } else { Log.w(TAG, "trackChanged Unavailable"); Log.w(TAG, "trackChanged Unavailable"); } } } } static synchronized void notifyChanged(PlaybackState playbackState) { static synchronized void notifyChanged(PlaybackStateCompat playbackState) { Log.d(TAG, "notifyChanged PlaybackState" + playbackState); Log.d(TAG, "notifyChanged PlaybackState" + playbackState); if (sBluetoothMediaBrowserService != null) { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState); sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState); Loading Loading @@ -179,7 +187,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { /** /** * Get object for controlling playback * Get object for controlling playback */ */ public static synchronized MediaController.TransportControls getTransportControls() { public static synchronized MediaControllerCompat.TransportControls getTransportControls() { if (sBluetoothMediaBrowserService != null) { if (sBluetoothMediaBrowserService != null) { return sBluetoothMediaBrowserService.mSession.getController().getTransportControls(); return sBluetoothMediaBrowserService.mSession.getController().getTransportControls(); } else { } else { Loading @@ -198,4 +206,16 @@ public class BluetoothMediaBrowserService extends MediaBrowserService { Log.w(TAG, "setActive Unavailable"); Log.w(TAG, "setActive Unavailable"); } } } } /** * Get Media session for updating state */ public static synchronized MediaSessionCompat getSession() { if (sBluetoothMediaBrowserService != null) { return sBluetoothMediaBrowserService.mSession; } else { Log.w(TAG, "getSession Unavailable"); return null; } } } }