OSDN Git Service

A2DP MediaSession active state management and NPE
authorSanket Agarwal <sanketa@google.com>
Fri, 22 Apr 2016 20:06:24 +0000 (13:06 -0700)
committerSanket Agarwal <sanketa@google.com>
Fri, 22 Apr 2016 23:56:17 +0000 (23:56 +0000)
UIs (such as SystemUI) depend on MediaSession.isActive state to
determine whether to show it on the overview (or shades). Currently we
are keeping the session active ever since the first bluetooth connect (A2DP) happens.

This change makes the session to be active when the device connects and
the first play happens (i.e. the play state of remote device
irrespective of who initated it) and in-active when device disconnects.

Also, avoid a NPE that may happen due to races between delay in
broadcasts and accesing bluetooth state machine.

Bug: b/28345602
Bug: b/28330860

Change-Id: Id7686308ada0b608b959258d137772ee5f24c18f
(cherry picked from commit faa30bec93650414fb90b60b5a488b32dd54b9ac)

src/com/android/bluetooth/a2dpsink/mbs/A2dpMediaBrowserService.java
src/com/android/bluetooth/hfpclient/connserv/HfpClientConnectionService.java

index 169f6ff..28dad09 100644 (file)
@@ -44,6 +44,20 @@ import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 
+/**
+ * Implements the MediaBrowserService interface to AVRCP and A2DP
+ *
+ * This service provides a means for external applications to access A2DP and AVRCP.
+ * The applications are expected to use MediaBrowser (see API) and all the music
+ * browsing/playback/metadata can be controlled via MediaBrowser and MediaController.
+ *
+ * The current behavior of MediaSession exposed by this service is as follows:
+ * 1. MediaSession is active (i.e. SystemUI and other overview UIs can see updates) when device is
+ * connected and first starts playing. Before it starts playing we do not active the session.
+ * 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)
+ * happens.
+ */
 public class A2dpMediaBrowserService extends MediaBrowserService {
     private static final String TAG = "A2dpMediaBrowserService";
     private static final String MEDIA_ID_ROOT = "__ROOT__";
@@ -125,7 +139,6 @@ public class A2dpMediaBrowserService extends MediaBrowserService {
         mSession.setCallback(mSessionCallbacks);
         mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
                 MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
-        mSession.setActive(true);
         mAvrcpCommandQueue = new AvrcpCommandQueueHandler(Looper.getMainLooper(), this);
 
         mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -268,6 +281,11 @@ public class A2dpMediaBrowserService extends MediaBrowserService {
                 } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
                     // Set the playback state to unconnected.
                     mAvrcpCommandQueue.obtainMessage(MSG_DEVICE_DISCONNECT, btDev).sendToTarget();
+                    // If we have been pushing updates via the session then stop sending them since
+                    // we are not connected anymore.
+                    if (mSession.isActive()) {
+                        mSession.setActive(false);
+                    }
                 }
             } else if (BluetoothAvrcpController.ACTION_TRACK_EVENT.equals(action)) {
                 PlaybackState pbb =
@@ -386,6 +404,12 @@ public class A2dpMediaBrowserService extends MediaBrowserService {
             PlaybackState.Builder pbb = new PlaybackState.Builder(pb);
             pb = pbb.setActions(mTransportControlFlags).build();
             mSession.setPlaybackState(pb);
+
+            // If we are now playing then we should start pushing updates via MediaSession so that
+            // external UI (such as SystemUI) can show the currently playing music.
+            if (pb.getState() == PlaybackState.STATE_PLAYING && !mSession.isActive()) {
+                mSession.setActive(true);
+            }
         }
     }
 
index a2c2430..5c50f82 100644 (file)
@@ -75,10 +75,18 @@ public class HfpClientConnectionService extends ConnectionService {
                         List<BluetoothHeadsetClientCall> calls =
                                 mHeadsetProfile.getCurrentCalls(mDevice);
                         Log.d(TAG, "Got calls " + calls);
+                        if (calls == null) {
+                            // We can get null as a return if we are not connected. Hence there may
+                            // be a race in getting the broadcast and HFP Client getting
+                            // disconnected before broadcast gets delivered.
+                            Log.w(TAG, "Got connected but calls were null, ignoring the broadcast");
+                            return;
+                        }
                         for (BluetoothHeadsetClientCall call : calls) {
                             handleCall(call);
                         }
                     } else {
+                        Log.e(TAG, "headset profile is null, ignoring broadcast.");
                     }
                 } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                     // Disconnect any inflight calls from the connection service.