[Media, Android] Fix lockscreen and other remote controls on KK-

The cause of the crash is the following:
- user starts playback on KK
- user tries to use remote control, e.g. lock screen
- the service gets an intent without the notification id and stops itself
- onServiceDestroy gets called with an invalid notification id so it doesn't clean up the manager
- next time the playback is started, the manager calls getIntent() with an invalid notification id
- the method returns null in this case

The fix: use distinct MediaButtonReceiver subclasses for the two supported notification ids to pass the right one with the intent MediaButtonReceiver sends to the service.

BUG==551411

Review URL: https://codereview.chromium.org/1431023004

Cr-Commit-Position: refs/heads/master@{#358714}
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 0e29e6a..b90bd68b 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -641,7 +641,12 @@
         </service>
 
 
-        <receiver android:name="org.chromium.chrome.browser.media.ui.MediaButtonReceiver">
+        <receiver android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackMediaButtonReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.MEDIA_BUTTON" />
+            </intent-filter>
+        </receiver>
+        <receiver android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationMediaButtonReceiver">
             <intent-filter>
                 <action android:name="android.intent.action.MEDIA_BUTTON" />
             </intent-filter>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java
index f8f795b..c18df6c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java
@@ -19,10 +19,14 @@
  * to the service listening to them.
  * This is there for backward compatibility with JB_MR0 and JB_MR1.
  */
-public class MediaButtonReceiver extends BroadcastReceiver {
+public abstract class MediaButtonReceiver extends BroadcastReceiver {
     private static final String LISTENER_SERVICE_CLASS_NAME =
             "org.chromium.chrome.browser.media.ui"
             + "MediaNotificationManager$ListenerService";
+    public static final String EXTRA_NOTIFICATION_ID =
+            "MediaNotificationManager.ListenerService.NOTIFICATION_ID";
+
+    public abstract int getNotificationId();
 
     @Override
     public void onReceive(Context context, Intent intent) {
@@ -39,6 +43,7 @@
         assert LISTENER_SERVICE_CLASS_NAME.equals(component.getClassName());
 
         intent.setComponent(component);
+        intent.putExtra(EXTRA_NOTIFICATION_ID, getNotificationId());
         context.startService(intent);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
index 2c71d30..8ed80d55 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -26,6 +26,7 @@
 import android.widget.RemoteViews;
 
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.Log;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.tab.Tab;
 
@@ -37,6 +38,8 @@
  */
 public class MediaNotificationManager {
 
+    private static final String TAG = "cr_MediaNotification";
+
     // We're always used on the UI thread but the LOCK is required by lint when creating the
     // singleton.
     private static final Object LOCK = new Object();
@@ -57,7 +60,7 @@
         private static final String ACTION_STOP =
                 "MediaNotificationManager.ListenerService.STOP";
         private static final String EXTRA_NOTIFICATION_ID =
-                "MediaNotificationManager.ListenerService.NOTIFICATION_ID";
+                MediaButtonReceiver.EXTRA_NOTIFICATION_ID;
 
         // The notification id this service instance corresponds to.
         private int mNotificationId = MediaNotificationInfo.INVALID_ID;
@@ -92,9 +95,22 @@
         private boolean processIntent(Intent intent) {
             if (intent == null) return false;
 
-            mNotificationId = intent.getIntExtra(
+            int notificationId = intent.getIntExtra(
                     EXTRA_NOTIFICATION_ID, MediaNotificationInfo.INVALID_ID);
-            if (mNotificationId == MediaNotificationInfo.INVALID_ID) return false;
+
+            // The notification id must always be valid and should match the first notification id
+            // the service got via the intent.
+            if (notificationId == MediaNotificationInfo.INVALID_ID
+                    || (mNotificationId != MediaNotificationInfo.INVALID_ID
+                        && mNotificationId != notificationId)) {
+                Log.w(TAG, "The service intent's notification id is invalid: ", notificationId);
+                return false;
+            }
+
+            // Either the notification id matches or it's the first intent we've got.
+            mNotificationId = notificationId;
+
+            assert mNotificationId != MediaNotificationInfo.INVALID_ID;
 
             MediaNotificationManager manager = getManager(mNotificationId);
             if (manager == null || manager.mMediaNotificationInfo == null) return false;
@@ -164,6 +180,28 @@
         private static final int NOTIFICATION_ID = R.id.presentation_notification;
     }
 
+    // Two classes to specify the right notification id in the intent.
+
+    /**
+     * This class is used internally but have to be public to be able to launch the service.
+     */
+    public static final class PlaybackMediaButtonReceiver extends MediaButtonReceiver {
+        @Override
+        public int getNotificationId() {
+            return PlaybackListenerService.NOTIFICATION_ID;
+        }
+    }
+
+    /**
+     * This class is used internally but have to be public to be able to launch the service.
+     */
+    public static final class PresentationMediaButtonReceiver extends MediaButtonReceiver {
+        @Override
+        public int getNotificationId() {
+            return PresentationListenerService.NOTIFICATION_ID;
+        }
+    }
+
     private static Intent getIntent(Context context, int notificationId) {
         Intent intent = null;
         if (notificationId == PlaybackListenerService.NOTIFICATION_ID) {
@@ -176,6 +214,19 @@
         return intent.putExtra(ListenerService.EXTRA_NOTIFICATION_ID, notificationId);
     }
 
+    private static String getButtonReceiverClassName(int notificationId) {
+        if (notificationId == PlaybackListenerService.NOTIFICATION_ID) {
+            return PlaybackMediaButtonReceiver.class.getName();
+        }
+
+        if (notificationId == PresentationListenerService.NOTIFICATION_ID) {
+            return PresentationMediaButtonReceiver.class.getName();
+        }
+
+        assert false;
+        return null;
+    }
+
     /**
      * Shows the notification with media controls with the specified media info. Replaces/updates
      * the current notification if already showing. Does nothing if |mediaNotificationInfo| hasn't
@@ -535,7 +586,7 @@
                 mContext,
                 mContext.getString(R.string.app_name),
                 new ComponentName(mContext.getPackageName(),
-                        MediaButtonReceiver.class.getName()),
+                        getButtonReceiverClassName(mMediaNotificationInfo.id)),
                 null);
         mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
                 | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);