Loading android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +86 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.util.Log; import com.android.bluetooth.R; import com.android.bluetooth.Utils; import com.android.bluetooth.a2dpsink.A2dpSinkService; import com.android.bluetooth.btservice.ProfileService; import java.util.ArrayList; Loading Loading @@ -88,12 +89,18 @@ public class AvrcpControllerService extends ProfileService { public static final int KEY_STATE_PRESSED = 0; public static final int KEY_STATE_RELEASED = 1; /* Active Device State Variables */ public static final int DEVICE_STATE_INACTIVE = 0; public static final int DEVICE_STATE_ACTIVE = 1; static BrowseTree sBrowseTree; private static AvrcpControllerService sService; private final BluetoothAdapter mAdapter; protected Map<BluetoothDevice, AvrcpControllerStateMachine> mDeviceStateMap = new ConcurrentHashMap<>(1); private BluetoothDevice mActiveDevice = null; private final Object mActiveDeviceLock = new Object(); private boolean mCoverArtEnabled = false; protected AvrcpCoverArtManager mCoverArtManager; Loading Loading @@ -139,11 +146,13 @@ public class AvrcpControllerService extends ProfileService { // Start the media browser service. Intent startIntent = new Intent(this, BluetoothMediaBrowserService.class); startService(startIntent); setActiveDevice(null); return true; } @Override protected synchronized boolean stop() { setActiveDevice(null); Intent stopIntent = new Intent(this, BluetoothMediaBrowserService.class); stopService(stopIntent); for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) { Loading @@ -163,6 +172,56 @@ public class AvrcpControllerService extends ProfileService { return sService; } /** * Get the current active device */ public BluetoothDevice getActiveDevice() { synchronized (mActiveDeviceLock) { return mActiveDevice; } } /** * Set the current active device, notify devices of activity status */ private boolean setActiveDevice(BluetoothDevice device) { A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); if (a2dpSinkService == null) { return false; } BluetoothDevice currentActiveDevice = getActiveDevice(); if ((device == null && currentActiveDevice == null) || (device != null && device.equals(currentActiveDevice))) { return true; } // Try and update the active device synchronized (mActiveDeviceLock) { if (a2dpSinkService.setActiveDevice(device)) { mActiveDevice = device; // Pause the old active device if (currentActiveDevice != null) { AvrcpControllerStateMachine oldStateMachine = getStateMachine(currentActiveDevice); if (oldStateMachine != null) { oldStateMachine.setDeviceState(DEVICE_STATE_INACTIVE); } } AvrcpControllerStateMachine stateMachine = getStateMachine(device); if (stateMachine != null) { stateMachine.setDeviceState(DEVICE_STATE_ACTIVE); } else { BluetoothMediaBrowserService.reset(); } return true; } } return false; } protected AvrcpControllerStateMachine newStateMachine(BluetoothDevice device) { return new AvrcpControllerStateMachine(device, this); } Loading Loading @@ -198,6 +257,10 @@ public class AvrcpControllerService extends ProfileService { requestedNode = stateMachine.findNode(parentMediaId); if (requestedNode != null) { if (DBG) Log.d(TAG, "Found a node"); BluetoothDevice device = stateMachine.getDevice(); if (device != null) { setActiveDevice(device); } stateMachine.playItem(requestedNode); break; } Loading Loading @@ -234,6 +297,12 @@ public class AvrcpControllerService extends ProfileService { if (DBG) Log.d(TAG, "Didn't find a node"); return new ArrayList(0); } else { // If we found a node and it belongs to a device then go ahead and make it active BluetoothDevice device = requestedNode.getDevice(); if (device != null) { setActiveDevice(device); } if (!requestedNode.isCached()) { if (DBG) Log.d(TAG, "node is not cached"); refreshContents(requestedNode); Loading Loading @@ -355,8 +424,15 @@ public class AvrcpControllerService extends ProfileService { AvrcpControllerStateMachine stateMachine = getOrCreateStateMachine(device); if (remoteControlConnected || browsingConnected) { stateMachine.connect(event); // The first device to connect gets to be the active device if (getActiveDevice() == null) { setActiveDevice(device); } } else { stateMachine.disconnect(); if (device.equals(getActiveDevice())) { setActiveDevice(null); } } } Loading Loading @@ -729,6 +805,10 @@ public class AvrcpControllerService extends ProfileService { * Remove state machine from device map once it is no longer needed. */ public void removeStateMachine(AvrcpControllerStateMachine stateMachine) { BluetoothDevice device = stateMachine.getDevice(); if (device.equals(getActiveDevice())) { setActiveDevice(null); } mDeviceStateMap.remove(stateMachine.getDevice()); } Loading @@ -737,6 +817,9 @@ public class AvrcpControllerService extends ProfileService { } protected AvrcpControllerStateMachine getStateMachine(BluetoothDevice device) { if (device == null) { return null; } return mDeviceStateMap.get(device); } Loading Loading @@ -783,6 +866,7 @@ public class AvrcpControllerService extends ProfileService { public void dump(StringBuilder sb) { super.dump(sb); ProfileService.println(sb, "Devices Tracked = " + mDeviceStateMap.size()); ProfileService.println(sb, "Active Device = " + mActiveDevice); for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) { ProfileService.println(sb, Loading @@ -795,6 +879,8 @@ public class AvrcpControllerService extends ProfileService { if (mCoverArtManager != null) { sb.append("\n " + mCoverArtManager.toString()); } sb.append("\n " + BluetoothMediaBrowserService.dump() + "\n"); } /*JNI*/ Loading android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +30 −52 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ class AvrcpControllerStateMachine extends StateMachine { //0->99 Events from Outside public static final int CONNECT = 1; public static final int DISCONNECT = 2; public static final int ACTIVE_DEVICE_CHANGE = 3; //100->199 Internal Events protected static final int CLEANUP = 100; Loading Loading @@ -184,7 +185,7 @@ class AvrcpControllerStateMachine extends StateMachine { * * @return device in focus */ public synchronized BluetoothDevice getDevice() { public BluetoothDevice getDevice() { return mDevice; } Loading Loading @@ -223,55 +224,20 @@ class AvrcpControllerStateMachine extends StateMachine { ProfileService.println(sb, "mDevice: " + mDevice.getAddress() + "(" + mDevice.getName() + ") " + this.toString()); ProfileService.println(sb, "isActive: " + isActive()); ProfileService.println(sb, "Control: " + mRemoteControlConnected); ProfileService.println(sb, "Browsing: " + mBrowsingConnected); } @VisibleForTesting boolean isActive() { return mDevice == sActiveDevice; } /* * requestActive * * Set the current device active if nothing an already connected device isn't playing */ private boolean requestActive() { if (sActiveDevice == null || BluetoothMediaBrowserService.getPlaybackState() != PlaybackStateCompat.STATE_PLAYING) { return setActive(true); } return false; return mDevice.equals(mService.getActiveDevice()); } /** * Attempt to set the active status for this device */ boolean setActive(boolean becomeActive) { logD("setActive(" + becomeActive + ")"); A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); if (a2dpSinkService == null) { return false; } if (becomeActive) { if (isActive()) { return true; } if (a2dpSinkService.setActiveDevice(mDevice)) { sActiveDevice = mDevice; BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); } return mDevice == sActiveDevice; } else if (isActive()) { sActiveDevice = null; a2dpSinkService.setActiveDevice(null); BluetoothMediaBrowserService.trackChanged(null); BluetoothMediaBrowserService.addressedPlayerChanged(null); } return true; public void setDeviceState(int state) { sendMessage(ACTIVE_DEVICE_CHANGE, state); } @Override Loading @@ -286,10 +252,6 @@ class AvrcpControllerStateMachine extends StateMachine { } synchronized void onBrowsingConnected() { if (mBrowsingConnected) return; mService.sBrowseTree.mRootNode.addChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService .sBrowseTree.mRootNode); mBrowsingConnected = true; } Loading @@ -303,10 +265,6 @@ class AvrcpControllerStateMachine extends StateMachine { if (isActive()) { BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); } mService.sBrowseTree.mRootNode.removeChild( mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService .sBrowseTree.mRootNode); removeUnusedArtwork(previousTrackUuid); removeUnusedArtworkFromBrowseTree(); mBrowsingConnected = false; Loading Loading @@ -426,6 +384,10 @@ class AvrcpControllerStateMachine extends StateMachine { case CLEANUP: mService.removeStateMachine(AvrcpControllerStateMachine.this); break; case ACTIVE_DEVICE_CHANGE: // Wait until we're connected to process this deferMessage(message); break; } return true; } Loading @@ -448,8 +410,9 @@ class AvrcpControllerStateMachine extends StateMachine { @Override public void enter() { if (mMostRecentState == BluetoothProfile.STATE_CONNECTING) { requestActive(); broadcastConnectionStateChanged(BluetoothProfile.STATE_CONNECTED); mService.sBrowseTree.mRootNode.addChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService.sBrowseTree.mRootNode); connectCoverArt(); // only works if we have a valid PSM } else { logD("ReEnteringConnected"); Loading @@ -461,6 +424,21 @@ class AvrcpControllerStateMachine extends StateMachine { public boolean processMessage(Message msg) { logD(STATE_TAG + " processMessage " + msg.what); switch (msg.what) { case ACTIVE_DEVICE_CHANGE: int state = msg.arg1; if (state == AvrcpControllerService.DEVICE_STATE_ACTIVE) { BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); BluetoothMediaBrowserService.trackChanged( mAddressedPlayer.getCurrentTrack()); BluetoothMediaBrowserService.notifyChanged( mAddressedPlayer.getPlaybackState()); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); } else { sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE); } return true; case MESSAGE_PROCESS_SET_ABS_VOL_CMD: mVolumeChangedNotificationsToIgnore++; removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT); Loading Loading @@ -643,7 +621,6 @@ class AvrcpControllerStateMachine extends StateMachine { } private void processPlayItem(BrowseTree.BrowseNode node) { setActive(true); if (node == null) { Log.w(TAG, "Invalid item to play"); } else { Loading Loading @@ -970,7 +947,8 @@ class AvrcpControllerStateMachine extends StateMachine { public void enter() { disconnectCoverArt(); onBrowsingDisconnected(); setActive(false); mService.sBrowseTree.mRootNode.removeChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService.sBrowseTree.mRootNode); broadcastConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTING); transitionTo(mDisconnected); } Loading android/app/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java +62 −3 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.app.PendingIntent; import android.content.Intent; import android.os.Bundle; import android.support.v4.media.MediaBrowserCompat.MediaItem; 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; Loading Loading @@ -154,19 +155,21 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { private void updateNowPlayingQueue(BrowseTree.BrowseNode node) { List<MediaItem> songList = node.getContents(); mMediaQueue.clear(); if (songList != null) { if (songList != null && songList.size() > 0) { for (MediaItem song : songList) { mMediaQueue.add(new MediaSessionCompat.QueueItem( song.getDescription(), mMediaQueue.size())); } } mSession.setQueue(mMediaQueue); } else { mSession.setQueue(null); } } private void clearNowPlayingQueue() { mMediaQueue.clear(); mSession.setQueue(mMediaQueue); mSession.setQueue(null); } static synchronized void notifyChanged(BrowseTree.BrowseNode node) { Loading Loading @@ -284,4 +287,60 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { return null; } } /** * 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); if (DBG) Log.d(TAG, "Service state has been reset"); } else { Log.w(TAG, "reset unavailable"); } } /** * 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(); MediaControllerCompat controller = session.getController(); MediaMetadataCompat metadata = controller == null ? null : controller.getMetadata(); PlaybackStateCompat playbackState = controller == null ? null : controller.getPlaybackState(); List<MediaSessionCompat.QueueItem> queue = controller == null ? null : controller.getQueue(); if (metadata != null) { sb.append("\n track={"); sb.append("title=" + metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)); sb.append(", artist=" + metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)); sb.append(", album=" + metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)); sb.append(", track_number=" + metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)); sb.append(", total_tracks=" + metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS)); sb.append(", genre=" + metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE)); sb.append(", album_art=" + metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI)); sb.append("}"); } else { sb.append("\n track=" + metadata); } sb.append("\n playbackState=" + playbackState); sb.append("\n queue=" + queue); sb.append("\n internal_queue=" + sBluetoothMediaBrowserService.mMediaQueue); } else { Log.w(TAG, "dump Unavailable"); sb.append(" null"); } return sb.toString(); } } android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java +381 −186 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +86 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.util.Log; import com.android.bluetooth.R; import com.android.bluetooth.Utils; import com.android.bluetooth.a2dpsink.A2dpSinkService; import com.android.bluetooth.btservice.ProfileService; import java.util.ArrayList; Loading Loading @@ -88,12 +89,18 @@ public class AvrcpControllerService extends ProfileService { public static final int KEY_STATE_PRESSED = 0; public static final int KEY_STATE_RELEASED = 1; /* Active Device State Variables */ public static final int DEVICE_STATE_INACTIVE = 0; public static final int DEVICE_STATE_ACTIVE = 1; static BrowseTree sBrowseTree; private static AvrcpControllerService sService; private final BluetoothAdapter mAdapter; protected Map<BluetoothDevice, AvrcpControllerStateMachine> mDeviceStateMap = new ConcurrentHashMap<>(1); private BluetoothDevice mActiveDevice = null; private final Object mActiveDeviceLock = new Object(); private boolean mCoverArtEnabled = false; protected AvrcpCoverArtManager mCoverArtManager; Loading Loading @@ -139,11 +146,13 @@ public class AvrcpControllerService extends ProfileService { // Start the media browser service. Intent startIntent = new Intent(this, BluetoothMediaBrowserService.class); startService(startIntent); setActiveDevice(null); return true; } @Override protected synchronized boolean stop() { setActiveDevice(null); Intent stopIntent = new Intent(this, BluetoothMediaBrowserService.class); stopService(stopIntent); for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) { Loading @@ -163,6 +172,56 @@ public class AvrcpControllerService extends ProfileService { return sService; } /** * Get the current active device */ public BluetoothDevice getActiveDevice() { synchronized (mActiveDeviceLock) { return mActiveDevice; } } /** * Set the current active device, notify devices of activity status */ private boolean setActiveDevice(BluetoothDevice device) { A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); if (a2dpSinkService == null) { return false; } BluetoothDevice currentActiveDevice = getActiveDevice(); if ((device == null && currentActiveDevice == null) || (device != null && device.equals(currentActiveDevice))) { return true; } // Try and update the active device synchronized (mActiveDeviceLock) { if (a2dpSinkService.setActiveDevice(device)) { mActiveDevice = device; // Pause the old active device if (currentActiveDevice != null) { AvrcpControllerStateMachine oldStateMachine = getStateMachine(currentActiveDevice); if (oldStateMachine != null) { oldStateMachine.setDeviceState(DEVICE_STATE_INACTIVE); } } AvrcpControllerStateMachine stateMachine = getStateMachine(device); if (stateMachine != null) { stateMachine.setDeviceState(DEVICE_STATE_ACTIVE); } else { BluetoothMediaBrowserService.reset(); } return true; } } return false; } protected AvrcpControllerStateMachine newStateMachine(BluetoothDevice device) { return new AvrcpControllerStateMachine(device, this); } Loading Loading @@ -198,6 +257,10 @@ public class AvrcpControllerService extends ProfileService { requestedNode = stateMachine.findNode(parentMediaId); if (requestedNode != null) { if (DBG) Log.d(TAG, "Found a node"); BluetoothDevice device = stateMachine.getDevice(); if (device != null) { setActiveDevice(device); } stateMachine.playItem(requestedNode); break; } Loading Loading @@ -234,6 +297,12 @@ public class AvrcpControllerService extends ProfileService { if (DBG) Log.d(TAG, "Didn't find a node"); return new ArrayList(0); } else { // If we found a node and it belongs to a device then go ahead and make it active BluetoothDevice device = requestedNode.getDevice(); if (device != null) { setActiveDevice(device); } if (!requestedNode.isCached()) { if (DBG) Log.d(TAG, "node is not cached"); refreshContents(requestedNode); Loading Loading @@ -355,8 +424,15 @@ public class AvrcpControllerService extends ProfileService { AvrcpControllerStateMachine stateMachine = getOrCreateStateMachine(device); if (remoteControlConnected || browsingConnected) { stateMachine.connect(event); // The first device to connect gets to be the active device if (getActiveDevice() == null) { setActiveDevice(device); } } else { stateMachine.disconnect(); if (device.equals(getActiveDevice())) { setActiveDevice(null); } } } Loading Loading @@ -729,6 +805,10 @@ public class AvrcpControllerService extends ProfileService { * Remove state machine from device map once it is no longer needed. */ public void removeStateMachine(AvrcpControllerStateMachine stateMachine) { BluetoothDevice device = stateMachine.getDevice(); if (device.equals(getActiveDevice())) { setActiveDevice(null); } mDeviceStateMap.remove(stateMachine.getDevice()); } Loading @@ -737,6 +817,9 @@ public class AvrcpControllerService extends ProfileService { } protected AvrcpControllerStateMachine getStateMachine(BluetoothDevice device) { if (device == null) { return null; } return mDeviceStateMap.get(device); } Loading Loading @@ -783,6 +866,7 @@ public class AvrcpControllerService extends ProfileService { public void dump(StringBuilder sb) { super.dump(sb); ProfileService.println(sb, "Devices Tracked = " + mDeviceStateMap.size()); ProfileService.println(sb, "Active Device = " + mActiveDevice); for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) { ProfileService.println(sb, Loading @@ -795,6 +879,8 @@ public class AvrcpControllerService extends ProfileService { if (mCoverArtManager != null) { sb.append("\n " + mCoverArtManager.toString()); } sb.append("\n " + BluetoothMediaBrowserService.dump() + "\n"); } /*JNI*/ Loading
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +30 −52 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ class AvrcpControllerStateMachine extends StateMachine { //0->99 Events from Outside public static final int CONNECT = 1; public static final int DISCONNECT = 2; public static final int ACTIVE_DEVICE_CHANGE = 3; //100->199 Internal Events protected static final int CLEANUP = 100; Loading Loading @@ -184,7 +185,7 @@ class AvrcpControllerStateMachine extends StateMachine { * * @return device in focus */ public synchronized BluetoothDevice getDevice() { public BluetoothDevice getDevice() { return mDevice; } Loading Loading @@ -223,55 +224,20 @@ class AvrcpControllerStateMachine extends StateMachine { ProfileService.println(sb, "mDevice: " + mDevice.getAddress() + "(" + mDevice.getName() + ") " + this.toString()); ProfileService.println(sb, "isActive: " + isActive()); ProfileService.println(sb, "Control: " + mRemoteControlConnected); ProfileService.println(sb, "Browsing: " + mBrowsingConnected); } @VisibleForTesting boolean isActive() { return mDevice == sActiveDevice; } /* * requestActive * * Set the current device active if nothing an already connected device isn't playing */ private boolean requestActive() { if (sActiveDevice == null || BluetoothMediaBrowserService.getPlaybackState() != PlaybackStateCompat.STATE_PLAYING) { return setActive(true); } return false; return mDevice.equals(mService.getActiveDevice()); } /** * Attempt to set the active status for this device */ boolean setActive(boolean becomeActive) { logD("setActive(" + becomeActive + ")"); A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); if (a2dpSinkService == null) { return false; } if (becomeActive) { if (isActive()) { return true; } if (a2dpSinkService.setActiveDevice(mDevice)) { sActiveDevice = mDevice; BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); } return mDevice == sActiveDevice; } else if (isActive()) { sActiveDevice = null; a2dpSinkService.setActiveDevice(null); BluetoothMediaBrowserService.trackChanged(null); BluetoothMediaBrowserService.addressedPlayerChanged(null); } return true; public void setDeviceState(int state) { sendMessage(ACTIVE_DEVICE_CHANGE, state); } @Override Loading @@ -286,10 +252,6 @@ class AvrcpControllerStateMachine extends StateMachine { } synchronized void onBrowsingConnected() { if (mBrowsingConnected) return; mService.sBrowseTree.mRootNode.addChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService .sBrowseTree.mRootNode); mBrowsingConnected = true; } Loading @@ -303,10 +265,6 @@ class AvrcpControllerStateMachine extends StateMachine { if (isActive()) { BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); } mService.sBrowseTree.mRootNode.removeChild( mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService .sBrowseTree.mRootNode); removeUnusedArtwork(previousTrackUuid); removeUnusedArtworkFromBrowseTree(); mBrowsingConnected = false; Loading Loading @@ -426,6 +384,10 @@ class AvrcpControllerStateMachine extends StateMachine { case CLEANUP: mService.removeStateMachine(AvrcpControllerStateMachine.this); break; case ACTIVE_DEVICE_CHANGE: // Wait until we're connected to process this deferMessage(message); break; } return true; } Loading @@ -448,8 +410,9 @@ class AvrcpControllerStateMachine extends StateMachine { @Override public void enter() { if (mMostRecentState == BluetoothProfile.STATE_CONNECTING) { requestActive(); broadcastConnectionStateChanged(BluetoothProfile.STATE_CONNECTED); mService.sBrowseTree.mRootNode.addChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService.sBrowseTree.mRootNode); connectCoverArt(); // only works if we have a valid PSM } else { logD("ReEnteringConnected"); Loading @@ -461,6 +424,21 @@ class AvrcpControllerStateMachine extends StateMachine { public boolean processMessage(Message msg) { logD(STATE_TAG + " processMessage " + msg.what); switch (msg.what) { case ACTIVE_DEVICE_CHANGE: int state = msg.arg1; if (state == AvrcpControllerService.DEVICE_STATE_ACTIVE) { BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); BluetoothMediaBrowserService.trackChanged( mAddressedPlayer.getCurrentTrack()); BluetoothMediaBrowserService.notifyChanged( mAddressedPlayer.getPlaybackState()); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode); } else { sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE); } return true; case MESSAGE_PROCESS_SET_ABS_VOL_CMD: mVolumeChangedNotificationsToIgnore++; removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT); Loading Loading @@ -643,7 +621,6 @@ class AvrcpControllerStateMachine extends StateMachine { } private void processPlayItem(BrowseTree.BrowseNode node) { setActive(true); if (node == null) { Log.w(TAG, "Invalid item to play"); } else { Loading Loading @@ -970,7 +947,8 @@ class AvrcpControllerStateMachine extends StateMachine { public void enter() { disconnectCoverArt(); onBrowsingDisconnected(); setActive(false); mService.sBrowseTree.mRootNode.removeChild(mBrowseTree.mRootNode); BluetoothMediaBrowserService.notifyChanged(mService.sBrowseTree.mRootNode); broadcastConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTING); transitionTo(mDisconnected); } Loading
android/app/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java +62 −3 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.app.PendingIntent; import android.content.Intent; import android.os.Bundle; import android.support.v4.media.MediaBrowserCompat.MediaItem; 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; Loading Loading @@ -154,19 +155,21 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { private void updateNowPlayingQueue(BrowseTree.BrowseNode node) { List<MediaItem> songList = node.getContents(); mMediaQueue.clear(); if (songList != null) { if (songList != null && songList.size() > 0) { for (MediaItem song : songList) { mMediaQueue.add(new MediaSessionCompat.QueueItem( song.getDescription(), mMediaQueue.size())); } } mSession.setQueue(mMediaQueue); } else { mSession.setQueue(null); } } private void clearNowPlayingQueue() { mMediaQueue.clear(); mSession.setQueue(mMediaQueue); mSession.setQueue(null); } static synchronized void notifyChanged(BrowseTree.BrowseNode node) { Loading Loading @@ -284,4 +287,60 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat { return null; } } /** * 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); if (DBG) Log.d(TAG, "Service state has been reset"); } else { Log.w(TAG, "reset unavailable"); } } /** * 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(); MediaControllerCompat controller = session.getController(); MediaMetadataCompat metadata = controller == null ? null : controller.getMetadata(); PlaybackStateCompat playbackState = controller == null ? null : controller.getPlaybackState(); List<MediaSessionCompat.QueueItem> queue = controller == null ? null : controller.getQueue(); if (metadata != null) { sb.append("\n track={"); sb.append("title=" + metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)); sb.append(", artist=" + metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)); sb.append(", album=" + metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)); sb.append(", track_number=" + metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)); sb.append(", total_tracks=" + metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS)); sb.append(", genre=" + metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE)); sb.append(", album_art=" + metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI)); sb.append("}"); } else { sb.append("\n track=" + metadata); } sb.append("\n playbackState=" + playbackState); sb.append("\n queue=" + queue); sb.append("\n internal_queue=" + sBluetoothMediaBrowserService.mMediaQueue); } else { Log.w(TAG, "dump Unavailable"); sb.append(" null"); } return sb.toString(); } }
android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java +381 −186 File changed.Preview size limit exceeded, changes collapsed. Show changes