Loading android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +33 −27 Original line number Diff line number Diff line Loading @@ -309,8 +309,8 @@ class AvrcpControllerStateMachine extends StateMachine { mBrowseTree.mNowPlayingNode.setCached(false); mBrowseTree.mRootNode.setCached(false); if (isActive()) { BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode); BluetoothMediaBrowserService.onNowPlayingQueueChanged(mBrowseTree.mNowPlayingNode); BluetoothMediaBrowserService.onBrowseNodeChanged(mBrowseTree.mRootNode); } removeUnusedArtwork(previousTrackUuid); removeUnusedArtworkFromBrowseTree(); Loading Loading @@ -378,19 +378,22 @@ class AvrcpControllerStateMachine extends StateMachine { } } private void notifyChanged(BrowseTree.BrowseNode node) { private void notifyNodeChanged(BrowseTree.BrowseNode node) { // We should only notify now playing content updates if we're the active device. VFS // updates are fine at any time int scope = node.getScope(); if (scope != AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING || (scope == AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING && isActive())) { BluetoothMediaBrowserService.notifyChanged(node); if (scope == AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING) { if (isActive()) { BluetoothMediaBrowserService.onNowPlayingQueueChanged(node); } } else { BluetoothMediaBrowserService.onBrowseNodeChanged(node); } } private void notifyChanged(PlaybackStateCompat state) { private void notifyPlaybackStateChanged(PlaybackStateCompat state) { if (isActive()) { BluetoothMediaBrowserService.notifyChanged(state); BluetoothMediaBrowserService.onPlaybackStateChanged(state); } } Loading Loading @@ -458,7 +461,7 @@ class AvrcpControllerStateMachine extends StateMachine { if (mMostRecentState == BluetoothProfile.STATE_CONNECTING) { broadcastConnectionStateChanged(BluetoothProfile.STATE_CONNECTED); mService.sBrowseTree.mRootNode.addChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService.sBrowseTree.mRootNode); BluetoothMediaBrowserService.onBrowseNodeChanged(mService.sBrowseTree.mRootNode); connectCoverArt(); // only works if we have a valid PSM } else { debug("Connected: Re-entering Connected "); Loading @@ -473,12 +476,13 @@ class AvrcpControllerStateMachine extends StateMachine { case ACTIVE_DEVICE_CHANGE: int state = msg.arg1; if (state == AvrcpControllerService.DEVICE_STATE_ACTIVE) { BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); BluetoothMediaBrowserService.trackChanged( BluetoothMediaBrowserService.onAddressedPlayerChanged(mSessionCallbacks); BluetoothMediaBrowserService.onTrackChanged( mAddressedPlayer.getCurrentTrack()); BluetoothMediaBrowserService.notifyChanged( BluetoothMediaBrowserService.onPlaybackStateChanged( mAddressedPlayer.getPlaybackState()); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); BluetoothMediaBrowserService.onNowPlayingQueueChanged( mBrowseTree.mNowPlayingNode); // If we switch to a device that is playing and we don't have focus, pause int focusState = getFocusState(); Loading Loading @@ -583,8 +587,8 @@ class AvrcpControllerStateMachine extends StateMachine { downloadImageIfNeeded(track); mAddressedPlayer.updateCurrentTrack(track); if (isActive()) { BluetoothMediaBrowserService.trackChanged(track); BluetoothMediaBrowserService.notifyChanged( BluetoothMediaBrowserService.onTrackChanged(track); BluetoothMediaBrowserService.onPlaybackStateChanged( mAddressedPlayer.getPlaybackState()); } if (previousTrack != null) { Loading @@ -604,7 +608,8 @@ class AvrcpControllerStateMachine extends StateMachine { return true; } BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); BluetoothMediaBrowserService.onPlaybackStateChanged( mAddressedPlayer.getPlaybackState()); int focusState = getFocusState(); if (focusState == AudioManager.ERROR) { Loading @@ -629,7 +634,7 @@ class AvrcpControllerStateMachine extends StateMachine { case MESSAGE_PROCESS_PLAY_POS_CHANGED: if (msg.arg2 != -1) { mAddressedPlayer.setPlayTime(msg.arg2); notifyChanged(mAddressedPlayer.getPlaybackState()); notifyPlaybackStateChanged(mAddressedPlayer.getPlaybackState()); } return true; Loading @@ -650,7 +655,8 @@ class AvrcpControllerStateMachine extends StateMachine { debug( "Connected: Addressed player change has invalidated the now playing" + " list"); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); BluetoothMediaBrowserService.onNowPlayingQueueChanged( mBrowseTree.mNowPlayingNode); } removeUnusedArtworkFromBrowseTree(); Loading Loading @@ -691,13 +697,13 @@ class AvrcpControllerStateMachine extends StateMachine { case MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS: mAddressedPlayer.setSupportedPlayerApplicationSettings( (PlayerApplicationSettings) msg.obj); notifyChanged(mAddressedPlayer.getPlaybackState()); notifyPlaybackStateChanged(mAddressedPlayer.getPlaybackState()); return true; case MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS: mAddressedPlayer.setCurrentPlayerApplicationSettings( (PlayerApplicationSettings) msg.obj); notifyChanged(mAddressedPlayer.getPlaybackState()); notifyPlaybackStateChanged(mAddressedPlayer.getPlaybackState()); return true; case MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED: Loading @@ -720,7 +726,7 @@ class AvrcpControllerStateMachine extends StateMachine { // track now has cover artwork boolean addedArtwork = mAddressedPlayer.notifyImageDownload(uuid, uri); if (addedArtwork && isActive()) { BluetoothMediaBrowserService.trackChanged( BluetoothMediaBrowserService.onTrackChanged( mAddressedPlayer.getCurrentTrack()); } Loading @@ -728,7 +734,7 @@ class AvrcpControllerStateMachine extends StateMachine { // all the items that need it. Notify of changed nodes accordingly Set<BrowseTree.BrowseNode> nodes = mBrowseTree.notifyImageDownload(uuid, uri); for (BrowseTree.BrowseNode node : nodes) { notifyChanged(node); notifyNodeChanged(node); } // Delete images that were downloaded and entirely unused Loading Loading @@ -825,7 +831,7 @@ class AvrcpControllerStateMachine extends StateMachine { debug("Connected: processAvailablePlayerChanged"); mBrowseTree.mRootNode.setCached(false); mBrowseTree.mRootNode.setExpectedChildren(BrowseTree.DEFAULT_FOLDER_SIZE); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode); BluetoothMediaBrowserService.onBrowseNodeChanged(mBrowseTree.mRootNode); removeUnusedArtworkFromBrowseTree(); requestContents(mBrowseTree.mRootNode); } Loading Loading @@ -891,7 +897,7 @@ class AvrcpControllerStateMachine extends StateMachine { // for the list to populate. int newSize = mBrowseNode.addChildren(folderList); debug("GetFolderList: Added " + newSize + " items to the browse tree"); notifyChanged(mBrowseNode); notifyNodeChanged(mBrowseNode); if (mBrowseNode.getChildrenCount() >= endIndicator || folderList.size() == 0 Loading Loading @@ -990,7 +996,7 @@ class AvrcpControllerStateMachine extends StateMachine { mBrowseTree.setCurrentBrowsedFolder(BrowseTree.ROOT); rootNode.setExpectedChildren(playerList.size()); rootNode.setCached(true); notifyChanged(rootNode); notifyNodeChanged(rootNode); } transitionTo(mConnected); break; Loading Loading @@ -1145,7 +1151,7 @@ class AvrcpControllerStateMachine extends StateMachine { // Whatever we have, notify on it so the UI doesn't hang if (mBrowseNode != null) { mBrowseNode.setCached(true); notifyChanged(mBrowseNode); notifyNodeChanged(mBrowseNode); } mBrowseNode = null; Loading @@ -1161,7 +1167,7 @@ class AvrcpControllerStateMachine extends StateMachine { onBrowsingDisconnected(); if (mService.sBrowseTree != null) { mService.sBrowseTree.mRootNode.removeChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService.sBrowseTree.mRootNode); BluetoothMediaBrowserService.onBrowseNodeChanged(mService.sBrowseTree.mRootNode); } broadcastConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTING); transitionTo(mDisconnected); Loading android/app/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java +150 −89 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import androidx.media.MediaBrowserServiceCompat; import com.android.bluetooth.BluetoothPrefs; import com.android.bluetooth.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; Loading @@ -54,6 +55,9 @@ import java.util.List; public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { private static final String TAG = BluetoothMediaBrowserService.class.getSimpleName(); private static final Object INSTANCE_LOCK = new Object(); @GuardedBy("INSTANCE_LOCK") private static BluetoothMediaBrowserService sBluetoothMediaBrowserService; private MediaSessionCompat mSession; Loading Loading @@ -84,8 +88,14 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { String action = intent.getAction(); if (action.equals(Intent.ACTION_LOCALE_CHANGED)) { Log.d(TAG, "Locale has updated"); if (sBluetoothMediaBrowserService == null) return; MediaSessionCompat session = sBluetoothMediaBrowserService.getSession(); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "onReceive(): Got locale update, but service isn't active"); return; } MediaSessionCompat session = service.getSession(); // Update playback state error message under new locale, if applicable MediaControllerCompat controller = session.getController(); Loading @@ -103,6 +113,27 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { private LocaleChangedReceiver mReceiver; /** * Set the BluetoothMediaBrowserService instance * * <p>This object is a singleton, as their can only be one service instance active for a process * at a time. */ private static void setInstance(BluetoothMediaBrowserService service) { synchronized (INSTANCE_LOCK) { sBluetoothMediaBrowserService = service; Log.i(TAG, "Service set to " + service); } } /** Get the BluetoothMediaBrowserService instance */ @VisibleForTesting public static BluetoothMediaBrowserService getInstance() { synchronized (INSTANCE_LOCK) { return sBluetoothMediaBrowserService; } } /** * Initialize this BluetoothMediaBrowserService, creating our MediaSessionCompat, MediaPlayer * and MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService. Loading @@ -121,20 +152,23 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name)); mSession.setQueue(mMediaQueue); setErrorPlaybackState(); sBluetoothMediaBrowserService = this; mReceiver = new LocaleChangedReceiver(); IntentFilter filter = new IntentFilter(); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); filter.addAction(Intent.ACTION_LOCALE_CHANGED); registerReceiver(mReceiver, filter); setInstance(this); } @Override public void onDestroy() { Log.d(TAG, "Service Destroyed"); super.onDestroy(); unregisterReceiver(mReceiver); mReceiver = null; setInstance(null); } /** Loading Loading @@ -259,8 +293,27 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { return new BrowserRoot(BrowseTree.ROOT, style); } private void updateNowPlayingQueue(BrowseTree.BrowseNode node) { List<MediaItem> songList = node.getContents(); static synchronized void onNowPlayingQueueChanged(BrowseTree.BrowseNode node) { BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "onNowPlayingQueueChanged(node=" + node + "): Service not available"); return; } if (node == null) { Log.w(TAG, "Received now playing update for null node"); return; } if (node.getScope() != AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING) { Log.w(TAG, "Received now playing update for node not in now playing scope."); return; } service.setNowPlayingQueue(node.getContents()); } private void setNowPlayingQueue(List<MediaItem> songList) { mMediaQueue.clear(); if (songList != null && songList.size() > 0) { for (MediaItem song : songList) { Loading @@ -280,104 +333,107 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { mSession.setQueue(null); } static synchronized void notifyChanged(BrowseTree.BrowseNode node) { if (sBluetoothMediaBrowserService != null) { if (node.getScope() == AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING) { sBluetoothMediaBrowserService.updateNowPlayingQueue(node); } else { static synchronized void onBrowseNodeChanged(BrowseTree.BrowseNode node) { BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "onBrowseNodeChanged(node=" + node + "): Service not available"); return; } if (node == null) { Log.w(TAG, "Received browse node update for null node"); return; } Log.d(TAG, "Browse Node contents changed, node=" + node); sBluetoothMediaBrowserService.notifyChildrenChanged(node.getID()); int scope = node.getScope(); if (scope != AvrcpControllerService.BROWSE_SCOPE_VFS && scope != AvrcpControllerService.BROWSE_SCOPE_PLAYER_LIST) { Log.w(TAG, "Received browse tree update for node outside of player or VFS scope"); return; } service.notifyChildrenChanged(node.getID()); } static synchronized void onAddressedPlayerChanged(MediaSessionCompat.Callback callback) { BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "addressedPlayerChanged(callback=" + callback + "): Service not available"); return; } static synchronized void addressedPlayerChanged(MediaSessionCompat.Callback callback) { if (sBluetoothMediaBrowserService != null) { if (callback == null) { sBluetoothMediaBrowserService.setErrorPlaybackState(); sBluetoothMediaBrowserService.clearNowPlayingQueue(); service.setErrorPlaybackState(); service.clearNowPlayingQueue(); } sBluetoothMediaBrowserService.mSession.setCallback(callback); } else { Log.w(TAG, "addressedPlayerChanged Unavailable"); service.mSession.setCallback(callback); } static synchronized void onTrackChanged(AvrcpItem track) { BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "trackChanged(track=" + track + "): Service not available"); return; } static synchronized void trackChanged(AvrcpItem track) { Log.d(TAG, "Track Changed, track=" + track); if (sBluetoothMediaBrowserService != null) { if (track != null) { sBluetoothMediaBrowserService.mSession.setMetadata(track.toMediaMetadata()); service.mSession.setMetadata(track.toMediaMetadata()); } else { sBluetoothMediaBrowserService.mSession.setMetadata(null); service.mSession.setMetadata(null); } } else { Log.w(TAG, "trackChanged Unavailable"); } static synchronized void onPlaybackStateChanged(PlaybackStateCompat state) { BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "onPlaybackStateChanged(state=" + state + "): Service not available"); return; } static synchronized void notifyChanged(PlaybackStateCompat playbackState) { Log.d( TAG, "Playback State Changed, state=" + AvrcpControllerUtils.playbackStateCompatToString(playbackState)); if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState); } else { Log.w(TAG, "notifyChanged Unavailable"); } } /** Send AVRCP Play command */ public static synchronized void play() { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.getController().getTransportControls().play(); } else { Log.w(TAG, "play Unavailable"); } } /** Send AVRCP Pause command */ public static synchronized void pause() { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.getController().getTransportControls().pause(); } else { Log.w(TAG, "pause Unavailable"); } + AvrcpControllerUtils.playbackStateCompatToString(state)); service.mSession.setPlaybackState(state); } /** Get playback state */ public static synchronized PlaybackStateCompat getPlaybackState() { if (sBluetoothMediaBrowserService != null) { MediaSessionCompat session = sBluetoothMediaBrowserService.getSession(); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "getPlaybackState(): Service not available"); return null; } MediaSessionCompat session = service.getSession(); if (session == null) return null; MediaControllerCompat controller = session.getController(); PlaybackStateCompat playbackState = controller == null ? null : controller.getPlaybackState(); return playbackState; } return null; } /** Get object for controlling playback */ public static synchronized MediaControllerCompat.TransportControls getTransportControls() { if (sBluetoothMediaBrowserService != null) { return sBluetoothMediaBrowserService.mSession.getController().getTransportControls(); } else { Log.w(TAG, "transportControls Unavailable"); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "getTransportControls(): Service not available"); return null; } return service.mSession.getController().getTransportControls(); } /** Set Media session active whenever we have Focus of any kind */ public static synchronized void setActive(boolean active) { if (sBluetoothMediaBrowserService != null) { Log.d(TAG, "Setting the session active state to:" + active); sBluetoothMediaBrowserService.mSession.setActive(active); } else { Log.w(TAG, "setActive Unavailable"); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "setActive(active=" + active + "): Service not available"); return; } Log.d(TAG, "Setting the session active state to:" + active); service.mSession.setActive(active); } /** Loading @@ -387,41 +443,46 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { */ @VisibleForTesting public static synchronized boolean isActive() { if (sBluetoothMediaBrowserService != null) { return sBluetoothMediaBrowserService.mSession.isActive(); } BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "isActive(): Service not available"); return false; } return service.mSession.isActive(); } /** Get Media session for updating state */ public static synchronized MediaSessionCompat getSession() { if (sBluetoothMediaBrowserService != null) { return sBluetoothMediaBrowserService.mSession; } else { Log.w(TAG, "getSession Unavailable"); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "getSession(): Service not available"); return null; } return service.mSession; } /** Reset the state of BluetoothMediaBrowserService to that before a device connected */ public static synchronized void reset() { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.clearNowPlayingQueue(); sBluetoothMediaBrowserService.mSession.setMetadata(null); sBluetoothMediaBrowserService.setErrorPlaybackState(); sBluetoothMediaBrowserService.mSession.setCallback(null); Log.d(TAG, "Service state has been reset"); } else { Log.w(TAG, "reset unavailable"); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "reset(): Service not available"); return; } service.clearNowPlayingQueue(); service.mSession.setMetadata(null); service.setErrorPlaybackState(); service.mSession.setCallback(null); Log.d(TAG, "Service state has been reset"); } /** Get the state of the BluetoothMediaBrowserService as a debug string */ public static synchronized String dump() { StringBuilder sb = new StringBuilder(); sb.append(TAG + ":"); if (sBluetoothMediaBrowserService != null) { MediaSessionCompat session = sBluetoothMediaBrowserService.getSession(); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service != null) { MediaSessionCompat session = service.getSession(); MediaControllerCompat controller = session.getController(); MediaMetadataCompat metadata = controller == null ? null : controller.getMetadata(); PlaybackStateCompat playbackState = Loading Loading @@ -456,7 +517,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { "\n playbackState=" + AvrcpControllerUtils.playbackStateCompatToString(playbackState)); sb.append("\n queue=" + queue); sb.append("\n internal_queue=" + sBluetoothMediaBrowserService.mMediaQueue); sb.append("\n internal_queue=" + service.mMediaQueue); sb.append("\n session active state=").append(isActive()); } else { Log.w(TAG, "dump Unavailable"); Loading Loading
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +33 −27 Original line number Diff line number Diff line Loading @@ -309,8 +309,8 @@ class AvrcpControllerStateMachine extends StateMachine { mBrowseTree.mNowPlayingNode.setCached(false); mBrowseTree.mRootNode.setCached(false); if (isActive()) { BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode); BluetoothMediaBrowserService.onNowPlayingQueueChanged(mBrowseTree.mNowPlayingNode); BluetoothMediaBrowserService.onBrowseNodeChanged(mBrowseTree.mRootNode); } removeUnusedArtwork(previousTrackUuid); removeUnusedArtworkFromBrowseTree(); Loading Loading @@ -378,19 +378,22 @@ class AvrcpControllerStateMachine extends StateMachine { } } private void notifyChanged(BrowseTree.BrowseNode node) { private void notifyNodeChanged(BrowseTree.BrowseNode node) { // We should only notify now playing content updates if we're the active device. VFS // updates are fine at any time int scope = node.getScope(); if (scope != AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING || (scope == AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING && isActive())) { BluetoothMediaBrowserService.notifyChanged(node); if (scope == AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING) { if (isActive()) { BluetoothMediaBrowserService.onNowPlayingQueueChanged(node); } } else { BluetoothMediaBrowserService.onBrowseNodeChanged(node); } } private void notifyChanged(PlaybackStateCompat state) { private void notifyPlaybackStateChanged(PlaybackStateCompat state) { if (isActive()) { BluetoothMediaBrowserService.notifyChanged(state); BluetoothMediaBrowserService.onPlaybackStateChanged(state); } } Loading Loading @@ -458,7 +461,7 @@ class AvrcpControllerStateMachine extends StateMachine { if (mMostRecentState == BluetoothProfile.STATE_CONNECTING) { broadcastConnectionStateChanged(BluetoothProfile.STATE_CONNECTED); mService.sBrowseTree.mRootNode.addChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService.sBrowseTree.mRootNode); BluetoothMediaBrowserService.onBrowseNodeChanged(mService.sBrowseTree.mRootNode); connectCoverArt(); // only works if we have a valid PSM } else { debug("Connected: Re-entering Connected "); Loading @@ -473,12 +476,13 @@ class AvrcpControllerStateMachine extends StateMachine { case ACTIVE_DEVICE_CHANGE: int state = msg.arg1; if (state == AvrcpControllerService.DEVICE_STATE_ACTIVE) { BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); BluetoothMediaBrowserService.trackChanged( BluetoothMediaBrowserService.onAddressedPlayerChanged(mSessionCallbacks); BluetoothMediaBrowserService.onTrackChanged( mAddressedPlayer.getCurrentTrack()); BluetoothMediaBrowserService.notifyChanged( BluetoothMediaBrowserService.onPlaybackStateChanged( mAddressedPlayer.getPlaybackState()); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); BluetoothMediaBrowserService.onNowPlayingQueueChanged( mBrowseTree.mNowPlayingNode); // If we switch to a device that is playing and we don't have focus, pause int focusState = getFocusState(); Loading Loading @@ -583,8 +587,8 @@ class AvrcpControllerStateMachine extends StateMachine { downloadImageIfNeeded(track); mAddressedPlayer.updateCurrentTrack(track); if (isActive()) { BluetoothMediaBrowserService.trackChanged(track); BluetoothMediaBrowserService.notifyChanged( BluetoothMediaBrowserService.onTrackChanged(track); BluetoothMediaBrowserService.onPlaybackStateChanged( mAddressedPlayer.getPlaybackState()); } if (previousTrack != null) { Loading @@ -604,7 +608,8 @@ class AvrcpControllerStateMachine extends StateMachine { return true; } BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); BluetoothMediaBrowserService.onPlaybackStateChanged( mAddressedPlayer.getPlaybackState()); int focusState = getFocusState(); if (focusState == AudioManager.ERROR) { Loading @@ -629,7 +634,7 @@ class AvrcpControllerStateMachine extends StateMachine { case MESSAGE_PROCESS_PLAY_POS_CHANGED: if (msg.arg2 != -1) { mAddressedPlayer.setPlayTime(msg.arg2); notifyChanged(mAddressedPlayer.getPlaybackState()); notifyPlaybackStateChanged(mAddressedPlayer.getPlaybackState()); } return true; Loading @@ -650,7 +655,8 @@ class AvrcpControllerStateMachine extends StateMachine { debug( "Connected: Addressed player change has invalidated the now playing" + " list"); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); BluetoothMediaBrowserService.onNowPlayingQueueChanged( mBrowseTree.mNowPlayingNode); } removeUnusedArtworkFromBrowseTree(); Loading Loading @@ -691,13 +697,13 @@ class AvrcpControllerStateMachine extends StateMachine { case MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS: mAddressedPlayer.setSupportedPlayerApplicationSettings( (PlayerApplicationSettings) msg.obj); notifyChanged(mAddressedPlayer.getPlaybackState()); notifyPlaybackStateChanged(mAddressedPlayer.getPlaybackState()); return true; case MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS: mAddressedPlayer.setCurrentPlayerApplicationSettings( (PlayerApplicationSettings) msg.obj); notifyChanged(mAddressedPlayer.getPlaybackState()); notifyPlaybackStateChanged(mAddressedPlayer.getPlaybackState()); return true; case MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED: Loading @@ -720,7 +726,7 @@ class AvrcpControllerStateMachine extends StateMachine { // track now has cover artwork boolean addedArtwork = mAddressedPlayer.notifyImageDownload(uuid, uri); if (addedArtwork && isActive()) { BluetoothMediaBrowserService.trackChanged( BluetoothMediaBrowserService.onTrackChanged( mAddressedPlayer.getCurrentTrack()); } Loading @@ -728,7 +734,7 @@ class AvrcpControllerStateMachine extends StateMachine { // all the items that need it. Notify of changed nodes accordingly Set<BrowseTree.BrowseNode> nodes = mBrowseTree.notifyImageDownload(uuid, uri); for (BrowseTree.BrowseNode node : nodes) { notifyChanged(node); notifyNodeChanged(node); } // Delete images that were downloaded and entirely unused Loading Loading @@ -825,7 +831,7 @@ class AvrcpControllerStateMachine extends StateMachine { debug("Connected: processAvailablePlayerChanged"); mBrowseTree.mRootNode.setCached(false); mBrowseTree.mRootNode.setExpectedChildren(BrowseTree.DEFAULT_FOLDER_SIZE); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode); BluetoothMediaBrowserService.onBrowseNodeChanged(mBrowseTree.mRootNode); removeUnusedArtworkFromBrowseTree(); requestContents(mBrowseTree.mRootNode); } Loading Loading @@ -891,7 +897,7 @@ class AvrcpControllerStateMachine extends StateMachine { // for the list to populate. int newSize = mBrowseNode.addChildren(folderList); debug("GetFolderList: Added " + newSize + " items to the browse tree"); notifyChanged(mBrowseNode); notifyNodeChanged(mBrowseNode); if (mBrowseNode.getChildrenCount() >= endIndicator || folderList.size() == 0 Loading Loading @@ -990,7 +996,7 @@ class AvrcpControllerStateMachine extends StateMachine { mBrowseTree.setCurrentBrowsedFolder(BrowseTree.ROOT); rootNode.setExpectedChildren(playerList.size()); rootNode.setCached(true); notifyChanged(rootNode); notifyNodeChanged(rootNode); } transitionTo(mConnected); break; Loading Loading @@ -1145,7 +1151,7 @@ class AvrcpControllerStateMachine extends StateMachine { // Whatever we have, notify on it so the UI doesn't hang if (mBrowseNode != null) { mBrowseNode.setCached(true); notifyChanged(mBrowseNode); notifyNodeChanged(mBrowseNode); } mBrowseNode = null; Loading @@ -1161,7 +1167,7 @@ class AvrcpControllerStateMachine extends StateMachine { onBrowsingDisconnected(); if (mService.sBrowseTree != null) { mService.sBrowseTree.mRootNode.removeChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService.sBrowseTree.mRootNode); BluetoothMediaBrowserService.onBrowseNodeChanged(mService.sBrowseTree.mRootNode); } broadcastConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTING); transitionTo(mDisconnected); Loading
android/app/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java +150 −89 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import androidx.media.MediaBrowserServiceCompat; import com.android.bluetooth.BluetoothPrefs; import com.android.bluetooth.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; Loading @@ -54,6 +55,9 @@ import java.util.List; public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { private static final String TAG = BluetoothMediaBrowserService.class.getSimpleName(); private static final Object INSTANCE_LOCK = new Object(); @GuardedBy("INSTANCE_LOCK") private static BluetoothMediaBrowserService sBluetoothMediaBrowserService; private MediaSessionCompat mSession; Loading Loading @@ -84,8 +88,14 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { String action = intent.getAction(); if (action.equals(Intent.ACTION_LOCALE_CHANGED)) { Log.d(TAG, "Locale has updated"); if (sBluetoothMediaBrowserService == null) return; MediaSessionCompat session = sBluetoothMediaBrowserService.getSession(); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "onReceive(): Got locale update, but service isn't active"); return; } MediaSessionCompat session = service.getSession(); // Update playback state error message under new locale, if applicable MediaControllerCompat controller = session.getController(); Loading @@ -103,6 +113,27 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { private LocaleChangedReceiver mReceiver; /** * Set the BluetoothMediaBrowserService instance * * <p>This object is a singleton, as their can only be one service instance active for a process * at a time. */ private static void setInstance(BluetoothMediaBrowserService service) { synchronized (INSTANCE_LOCK) { sBluetoothMediaBrowserService = service; Log.i(TAG, "Service set to " + service); } } /** Get the BluetoothMediaBrowserService instance */ @VisibleForTesting public static BluetoothMediaBrowserService getInstance() { synchronized (INSTANCE_LOCK) { return sBluetoothMediaBrowserService; } } /** * Initialize this BluetoothMediaBrowserService, creating our MediaSessionCompat, MediaPlayer * and MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService. Loading @@ -121,20 +152,23 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name)); mSession.setQueue(mMediaQueue); setErrorPlaybackState(); sBluetoothMediaBrowserService = this; mReceiver = new LocaleChangedReceiver(); IntentFilter filter = new IntentFilter(); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); filter.addAction(Intent.ACTION_LOCALE_CHANGED); registerReceiver(mReceiver, filter); setInstance(this); } @Override public void onDestroy() { Log.d(TAG, "Service Destroyed"); super.onDestroy(); unregisterReceiver(mReceiver); mReceiver = null; setInstance(null); } /** Loading Loading @@ -259,8 +293,27 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { return new BrowserRoot(BrowseTree.ROOT, style); } private void updateNowPlayingQueue(BrowseTree.BrowseNode node) { List<MediaItem> songList = node.getContents(); static synchronized void onNowPlayingQueueChanged(BrowseTree.BrowseNode node) { BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "onNowPlayingQueueChanged(node=" + node + "): Service not available"); return; } if (node == null) { Log.w(TAG, "Received now playing update for null node"); return; } if (node.getScope() != AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING) { Log.w(TAG, "Received now playing update for node not in now playing scope."); return; } service.setNowPlayingQueue(node.getContents()); } private void setNowPlayingQueue(List<MediaItem> songList) { mMediaQueue.clear(); if (songList != null && songList.size() > 0) { for (MediaItem song : songList) { Loading @@ -280,104 +333,107 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { mSession.setQueue(null); } static synchronized void notifyChanged(BrowseTree.BrowseNode node) { if (sBluetoothMediaBrowserService != null) { if (node.getScope() == AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING) { sBluetoothMediaBrowserService.updateNowPlayingQueue(node); } else { static synchronized void onBrowseNodeChanged(BrowseTree.BrowseNode node) { BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "onBrowseNodeChanged(node=" + node + "): Service not available"); return; } if (node == null) { Log.w(TAG, "Received browse node update for null node"); return; } Log.d(TAG, "Browse Node contents changed, node=" + node); sBluetoothMediaBrowserService.notifyChildrenChanged(node.getID()); int scope = node.getScope(); if (scope != AvrcpControllerService.BROWSE_SCOPE_VFS && scope != AvrcpControllerService.BROWSE_SCOPE_PLAYER_LIST) { Log.w(TAG, "Received browse tree update for node outside of player or VFS scope"); return; } service.notifyChildrenChanged(node.getID()); } static synchronized void onAddressedPlayerChanged(MediaSessionCompat.Callback callback) { BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "addressedPlayerChanged(callback=" + callback + "): Service not available"); return; } static synchronized void addressedPlayerChanged(MediaSessionCompat.Callback callback) { if (sBluetoothMediaBrowserService != null) { if (callback == null) { sBluetoothMediaBrowserService.setErrorPlaybackState(); sBluetoothMediaBrowserService.clearNowPlayingQueue(); service.setErrorPlaybackState(); service.clearNowPlayingQueue(); } sBluetoothMediaBrowserService.mSession.setCallback(callback); } else { Log.w(TAG, "addressedPlayerChanged Unavailable"); service.mSession.setCallback(callback); } static synchronized void onTrackChanged(AvrcpItem track) { BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "trackChanged(track=" + track + "): Service not available"); return; } static synchronized void trackChanged(AvrcpItem track) { Log.d(TAG, "Track Changed, track=" + track); if (sBluetoothMediaBrowserService != null) { if (track != null) { sBluetoothMediaBrowserService.mSession.setMetadata(track.toMediaMetadata()); service.mSession.setMetadata(track.toMediaMetadata()); } else { sBluetoothMediaBrowserService.mSession.setMetadata(null); service.mSession.setMetadata(null); } } else { Log.w(TAG, "trackChanged Unavailable"); } static synchronized void onPlaybackStateChanged(PlaybackStateCompat state) { BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "onPlaybackStateChanged(state=" + state + "): Service not available"); return; } static synchronized void notifyChanged(PlaybackStateCompat playbackState) { Log.d( TAG, "Playback State Changed, state=" + AvrcpControllerUtils.playbackStateCompatToString(playbackState)); if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState); } else { Log.w(TAG, "notifyChanged Unavailable"); } } /** Send AVRCP Play command */ public static synchronized void play() { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.getController().getTransportControls().play(); } else { Log.w(TAG, "play Unavailable"); } } /** Send AVRCP Pause command */ public static synchronized void pause() { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.mSession.getController().getTransportControls().pause(); } else { Log.w(TAG, "pause Unavailable"); } + AvrcpControllerUtils.playbackStateCompatToString(state)); service.mSession.setPlaybackState(state); } /** Get playback state */ public static synchronized PlaybackStateCompat getPlaybackState() { if (sBluetoothMediaBrowserService != null) { MediaSessionCompat session = sBluetoothMediaBrowserService.getSession(); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "getPlaybackState(): Service not available"); return null; } MediaSessionCompat session = service.getSession(); if (session == null) return null; MediaControllerCompat controller = session.getController(); PlaybackStateCompat playbackState = controller == null ? null : controller.getPlaybackState(); return playbackState; } return null; } /** Get object for controlling playback */ public static synchronized MediaControllerCompat.TransportControls getTransportControls() { if (sBluetoothMediaBrowserService != null) { return sBluetoothMediaBrowserService.mSession.getController().getTransportControls(); } else { Log.w(TAG, "transportControls Unavailable"); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "getTransportControls(): Service not available"); return null; } return service.mSession.getController().getTransportControls(); } /** Set Media session active whenever we have Focus of any kind */ public static synchronized void setActive(boolean active) { if (sBluetoothMediaBrowserService != null) { Log.d(TAG, "Setting the session active state to:" + active); sBluetoothMediaBrowserService.mSession.setActive(active); } else { Log.w(TAG, "setActive Unavailable"); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "setActive(active=" + active + "): Service not available"); return; } Log.d(TAG, "Setting the session active state to:" + active); service.mSession.setActive(active); } /** Loading @@ -387,41 +443,46 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { */ @VisibleForTesting public static synchronized boolean isActive() { if (sBluetoothMediaBrowserService != null) { return sBluetoothMediaBrowserService.mSession.isActive(); } BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "isActive(): Service not available"); return false; } return service.mSession.isActive(); } /** Get Media session for updating state */ public static synchronized MediaSessionCompat getSession() { if (sBluetoothMediaBrowserService != null) { return sBluetoothMediaBrowserService.mSession; } else { Log.w(TAG, "getSession Unavailable"); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "getSession(): Service not available"); return null; } return service.mSession; } /** Reset the state of BluetoothMediaBrowserService to that before a device connected */ public static synchronized void reset() { if (sBluetoothMediaBrowserService != null) { sBluetoothMediaBrowserService.clearNowPlayingQueue(); sBluetoothMediaBrowserService.mSession.setMetadata(null); sBluetoothMediaBrowserService.setErrorPlaybackState(); sBluetoothMediaBrowserService.mSession.setCallback(null); Log.d(TAG, "Service state has been reset"); } else { Log.w(TAG, "reset unavailable"); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service == null) { Log.w(TAG, "reset(): Service not available"); return; } service.clearNowPlayingQueue(); service.mSession.setMetadata(null); service.setErrorPlaybackState(); service.mSession.setCallback(null); Log.d(TAG, "Service state has been reset"); } /** Get the state of the BluetoothMediaBrowserService as a debug string */ public static synchronized String dump() { StringBuilder sb = new StringBuilder(); sb.append(TAG + ":"); if (sBluetoothMediaBrowserService != null) { MediaSessionCompat session = sBluetoothMediaBrowserService.getSession(); BluetoothMediaBrowserService service = BluetoothMediaBrowserService.getInstance(); if (service != null) { MediaSessionCompat session = service.getSession(); MediaControllerCompat controller = session.getController(); MediaMetadataCompat metadata = controller == null ? null : controller.getMetadata(); PlaybackStateCompat playbackState = Loading Loading @@ -456,7 +517,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { "\n playbackState=" + AvrcpControllerUtils.playbackStateCompatToString(playbackState)); sb.append("\n queue=" + queue); sb.append("\n internal_queue=" + sBluetoothMediaBrowserService.mMediaQueue); sb.append("\n internal_queue=" + service.mMediaQueue); sb.append("\n session active state=").append(isActive()); } else { Log.w(TAG, "dump Unavailable"); Loading