title: MediaSession not getting any callbacks from MediaStyle Notification
body: <p>I created a service that extends MediaBrowserServiceCompat. This service holds a reference to my player and creates a new MediaSession with a callback. Everytime the player changes state, I update the MediaSession's playback state and create a MediaStyle notification. The notification is showing when I start to play something in my player, but the buttons in the notification are not triggering the MediaSession callback, they don't do anything. I'm setting the right flags in the MediaSession, I'm setting the session as active, I'm setting the correct actions in the playback state, I'm passing the session token to the notification but still not getting any callbacks from it. I really don't know what I'm doing wrong. All this code is inside a module imported by my app.</p>
  
  <p>My NotificationHelper class:</p>
  
  <pre><code>private final MusicService mService;
  
  private final NotificationCompat.Action mPlayAction;
  private final NotificationCompat.Action mPauseAction;
  private final NotificationCompat.Action mNextAction;
  private final NotificationCompat.Action mPrevAction;
  private final NotificationManager mNotificationManager;
  
  public MediaNotificationManager(MusicService service) {
      mService = service;
  
      mNotificationManager =
              (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
  
      mPlayAction =
              new NotificationCompat.Action(
                      R.drawable.exo_icon_play,
                      "Play",
                      MediaButtonReceiver.buildMediaButtonPendingIntent(
                              mService,
                              PlaybackStateCompat.ACTION_PLAY));
      mPauseAction =
              new NotificationCompat.Action(
                      R.drawable.exo_icon_pause,
                      "Pause",
                      MediaButtonReceiver.buildMediaButtonPendingIntent(
                              mService,
                              PlaybackStateCompat.ACTION_PAUSE));
      mNextAction =
              new NotificationCompat.Action(
                      R.drawable.exo_icon_next,
                      "Next",
                      MediaButtonReceiver.buildMediaButtonPendingIntent(
                              mService,
                              PlaybackStateCompat.ACTION_SKIP_TO_NEXT));
      mPrevAction =
              new NotificationCompat.Action(
                      R.drawable.exo_icon_previous,
                      "Previous",
                      MediaButtonReceiver.buildMediaButtonPendingIntent(
                              mService,
                              PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS));
  
      // Cancel all notifications to handle the case where the Service was killed and
      // restarted by the system.
      mNotificationManager.cancelAll();
  }
  
  public Notification getNotification(MediaMetadataCompat metadata,
                                      @NonNull PlaybackStateCompat state,
                                      MediaSessionCompat.Token token) {
      boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
      MediaDescriptionCompat description = metadata.getDescription();
      NotificationCompat.Builder builder =
              buildNotification(state, token, isPlaying, description);
      return builder.build();
  }
  
  private NotificationCompat.Builder buildNotification(@NonNull PlaybackStateCompat state,
                                                       MediaSessionCompat.Token token,
                                                       boolean isPlaying,
                                                       MediaDescriptionCompat description) {
  
      // Create the (mandatory) notification channel when running on Android Oreo.
      if (isAndroidOOrHigher()) {
          createChannel();
      }
  
      NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, CHANNEL_ID)
              .setSmallIcon(R.drawable.exo_notification_small_icon)
              .setContentTitle("Track title")
              .setContentText("Artist - Album")
              .setLargeIcon(BitmapFactory.decodeResource(mService.getResources(), R.drawable.exo_notification_small_icon))
              .setStyle(new MediaStyle().setShowActionsInCompactView(0).setMediaSession(token));
  
  
      builder.addAction(mPrevAction);
      builder.addAction(isPlaying ? mPauseAction : mPlayAction);
      builder.addAction(mNextAction);
  
      return builder;
  }
  
  // Does nothing on versions of Android earlier than O.
  @RequiresApi(Build.VERSION_CODES.O)
  private void createChannel() {
      if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) {
          // The user-visible name of the channel.
          CharSequence name = "MediaSession";
          // The user-visible description of the channel.
          String description = "MediaSession and MediaPlayer";
          int importance = NotificationManager.IMPORTANCE_LOW;
          NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
          // Configure the notification channel.
          mChannel.setDescription(description);
          mChannel.enableLights(true);
          // Sets the notification light color for notifications posted to this
          // channel, if the device supports this feature.
          mChannel.setLightColor(Color.RED);
          mChannel.enableVibration(true);
          mChannel.setVibrationPattern(
                  new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
          mNotificationManager.createNotificationChannel(mChannel);
          Log.d(TAG, "createChannel: New channel created");
      } else {
          Log.d(TAG, "createChannel: Existing channel reused");
      }
  }
  
  private boolean isAndroidOOrHigher() {
      return Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.O;
  }
  </code></pre>
  
  <p>My Service class:</p>
  
  <pre><code>public class MusicService extends MediaBrowserServiceCompat {
  
  private static final String TAG = MusicService.class.getSimpleName();
  
  private MediaSessionCompat mSession;
  private PlayerManager playerManager;
  private MediaSessionCallback mCallback;
  private MediaNotificationManager mediaNotificationManager;
  
  @Override
  public void onCreate() {
      super.onCreate();
  
      playerManager = PlayerManager.getInstance(this);
      playerManager.addListener(new PlayerManagerServiceListener());
  
      mediaNotificationManager = new MediaNotificationManager(this);
  
      // Create a new MediaSession.
      mSession = new MediaSessionCompat(this, "MusicService");
      mCallback = new MediaSessionCallback();
      mSession.setCallback(mCallback);
      mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
      setSessionToken(mSession.getSessionToken());
      mSession.setActive(true);
  
  }
  
  @Override
  public void onDestroy() {
      mSession.release();
      Log.d(TAG, "onDestroy: MediaPlayerAdapter stopped, and MediaSession released");
  }
  
  @Override
  public BrowserRoot onGetRoot(@NonNull String clientPackageName,
                               int clientUid,
                               Bundle rootHints) {
      return new BrowserRoot("root", null);
  }
  
  @Override
  public void onLoadChildren(
          @NonNull final String parentMediaId,
          @NonNull final Result&lt;List&lt;MediaBrowserCompat.MediaItem&gt;&gt; result) {
      result.sendResult(null);
  }
  
  // MediaSession Callback: Transport Controls -&gt; MediaPlayerAdapter
  public class MediaSessionCallback extends MediaSessionCompat.Callback {
  
      @Override
      public void onPlay() {
          playerManager.play();
      }
  
      @Override
      public void onPause() {
          playerManager.pause();
      }
  
      @Override
      public void onStop() {
         playerManager.stop();
      }
  
      @Override
      public void onSkipToNext() {
          playerManager.next();
      }
  
      @Override
      public void onSkipToPrevious() {
          playerManager.previous();
      }
  
      @Override
      public void onSeekTo(long pos) {
          playerManager.seekTo(pos);
      }
  }
  
  public class PlayerManagerServiceListener implements PlayerManager.PlayerManagerListener {
  
      @Override
      public void onError(@Nullable Exception error) {
  
      }
  
      @Override
      public void onProgress(long duration, long position) {
  
      }
  
      @Override
      public void onPlayerChange(int change) {
  
      }
  
      @Override
      public void onTrackChange(TrackVO track) {
      }
  
      @Override
      public void onListChange(List tracks) {
  
      }
  
      @Override
      public void onPlaybackStateChange(int playbackState) {
          PlaybackStateCompat.Builder playbackstateBuilder = new PlaybackStateCompat.Builder();
          int playbackStateCompat = -1;
  
          switch(playbackState) {
              case PlaybackStateListener.STATE_PLAYING:
                  playbackStateCompat = PlaybackStateCompat.STATE_PLAYING;
                  //playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PAUSE);
                  break;
  
              case PlaybackStateListener.STATE_PAUSED:
                  playbackStateCompat = PlaybackStateCompat.STATE_PAUSED;
                  //playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY);
                  break;
          }
  
          if (playbackStateCompat == -1) {
              return;
          }
  
          mSession.setActive(true);
  
          playbackstateBuilder.setActions(
                  PlaybackStateCompat.ACTION_PLAY |
                          PlaybackStateCompat.ACTION_PLAY_PAUSE |
                          PlaybackStateCompat.ACTION_SKIP_TO_NEXT |
                          PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
          playbackstateBuilder.setState(playbackStateCompat, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 0);
  
          PlaybackStateCompat state = playbackstateBuilder.build();
  
          MediaMetadataCompat mediaMetadata = new MediaMetadataCompat.Builder()
                  .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, playerManager.getCurrenTrack().getName())
                  .build();
          mSession.setMetadata(mediaMetadata);
  
          mSession.setPlaybackState(state);
  
          Notification notification = mediaNotificationManager.getNotification(
                  mediaMetadata,
                  state,
                  getSessionToken()
          );
  
          Intent intent = new Intent(MusicService.this, MusicService.class);
          ContextCompat.startForegroundService(MusicService.this, intent);
  
          startForeground(417, notification);
      }
  
  }
  </code></pre>
  
  <p>}</p>
  
  <p>MediaBrowserHelper to initialize the service:</p>
  
  <pre><code>public class MediaBrowserHelper {
  
  private static final String TAG = MediaBrowserHelper.class.getSimpleName();
  
  private final Context mContext;
  private final Class&lt;? extends MediaBrowserServiceCompat&gt; mMediaBrowserServiceClass;
  
  private final List&lt;Callback&gt; mCallbackList = new ArrayList&lt;&gt;();
  
  private final MediaBrowserConnectionCallback mMediaBrowserConnectionCallback;
  private final MediaControllerCallback mMediaControllerCallback;
  private final MediaBrowserSubscriptionCallback mMediaBrowserSubscriptionCallback;
  
  private MediaBrowserCompat mMediaBrowser;
  
  @Nullable
  private MediaControllerCompat mMediaController;
  
  public MediaBrowserHelper(Context context,
                            Class&lt;? extends MediaBrowserServiceCompat&gt; serviceClass) {
      mContext = context;
      mMediaBrowserServiceClass = serviceClass;
  
      mMediaBrowserConnectionCallback = new MediaBrowserConnectionCallback();
      mMediaControllerCallback = new MediaControllerCallback();
      mMediaBrowserSubscriptionCallback = new MediaBrowserSubscriptionCallback();
  }
  
  public void onStart() {
      if (mMediaBrowser == null) {
          mMediaBrowser =
                  new MediaBrowserCompat(
                          mContext,
                          new ComponentName(mContext, mMediaBrowserServiceClass),
                          mMediaBrowserConnectionCallback,
                          null);
          mMediaBrowser.connect();
      }
      Log.d(TAG, "onStart: Creating MediaBrowser, and connecting");
  }
  
  public void onStop() {
      if (mMediaController != null) {
          mMediaController.unregisterCallback(mMediaControllerCallback);
          mMediaController = null;
      }
      if (mMediaBrowser != null &amp;&amp; mMediaBrowser.isConnected()) {
          mMediaBrowser.disconnect();
          mMediaBrowser = null;
      }
      resetState();
      Log.d(TAG, "onStop: Releasing MediaController, Disconnecting from MediaBrowser");
  }
  
  /**
   * Called after connecting with a {@link MediaBrowserServiceCompat}.
   * &lt;p&gt;
   * Override to perform processing after a connection is established.
   *
   * @param mediaController {@link MediaControllerCompat} associated with the connected
   *                        MediaSession.
   */
  protected void onConnected(@NonNull MediaControllerCompat mediaController) {
  }
  
  /**
   * Called after loading a browsable {@link MediaBrowserCompat.MediaItem}
   *
   * @param parentId The media ID of the parent item.
   * @param children List (possibly empty) of child items.
   */
  protected void onChildrenLoaded(@NonNull String parentId,
                                  @NonNull List&lt;MediaBrowserCompat.MediaItem&gt; children) {
  }
  
  /**
   * Called when the {@link MediaBrowserServiceCompat} connection is lost.
   */
  protected void onDisconnected() {
  }
  
  @NonNull
  protected final MediaControllerCompat getMediaController() {
      if (mMediaController == null) {
          throw new IllegalStateException("MediaController is null!");
      }
      return mMediaController;
  }
  
  /**
   * The internal state of the app needs to revert to what it looks like when it started before
   * any connections to the {@link MusicService} happens via the {@link MediaSessionCompat}.
   */
  private void resetState() {
      performOnAllCallbacks(new CallbackCommand() {
          @Override
          public void perform(@NonNull Callback callback) {
              callback.onPlaybackStateChanged(null);
          }
      });
      Log.d(TAG, "resetState: ");
  }
  
  public MediaControllerCompat.TransportControls getTransportControls() {
      if (mMediaController == null) {
          Log.d(TAG, "getTransportControls: MediaController is null!");
          throw new IllegalStateException("MediaController is null!");
      }
      return mMediaController.getTransportControls();
  }
  
  public void registerCallback(Callback callback) {
      if (callback != null) {
          mCallbackList.add(callback);
  
          // Update with the latest metadata/playback state.
          if (mMediaController != null) {
              final MediaMetadataCompat metadata = mMediaController.getMetadata();
              if (metadata != null) {
                  callback.onMetadataChanged(metadata);
              }
  
              final PlaybackStateCompat playbackState = mMediaController.getPlaybackState();
              if (playbackState != null) {
                  callback.onPlaybackStateChanged(playbackState);
              }
          }
      }
  }
  
  private void performOnAllCallbacks(@NonNull CallbackCommand command) {
      for (Callback callback : mCallbackList) {
          if (callback != null) {
              command.perform(callback);
          }
      }
  }
  
  /**
   * Helper for more easily performing operations on all listening clients.
   */
  private interface CallbackCommand {
      void perform(@NonNull Callback callback);
  }
  
  // Receives callbacks from the MediaBrowser when it has successfully connected to the
  // MediaBrowserService (MusicService).
  private class MediaBrowserConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
  
      // Happens as a result of onStart().
      @Override
      public void onConnected() {
          try {
              // Get a MediaController for the MediaSession.
              mMediaController =
                      new MediaControllerCompat(mContext, mMediaBrowser.getSessionToken());
              mMediaController.registerCallback(mMediaControllerCallback);
  
              // Sync existing MediaSession state to the UI.
              mMediaControllerCallback.onMetadataChanged(mMediaController.getMetadata());
              mMediaControllerCallback.onPlaybackStateChanged(
                      mMediaController.getPlaybackState());
  
              MediaBrowserHelper.this.onConnected(mMediaController);
          } catch (RemoteException e) {
              Log.d(TAG, String.format("onConnected: Problem: %s", e.toString()));
              throw new RuntimeException(e);
          }
  
          mMediaBrowser.subscribe(mMediaBrowser.getRoot(), mMediaBrowserSubscriptionCallback);
      }
  }
  
  // Receives callbacks from the MediaBrowser when the MediaBrowserService has loaded new media
  // that is ready for playback.
  public class MediaBrowserSubscriptionCallback extends MediaBrowserCompat.SubscriptionCallback {
  
      @Override
      public void onChildrenLoaded(@NonNull String parentId,
                                   @NonNull List&lt;MediaBrowserCompat.MediaItem&gt; children) {
          MediaBrowserHelper.this.onChildrenLoaded(parentId, children);
      }
  }
  
  // Receives callbacks from the MediaController and updates the UI state,
  // i.e.: Which is the current item, whether it's playing or paused, etc.
  private class MediaControllerCallback extends MediaControllerCompat.Callback {
  
      @Override
      public void onMetadataChanged(final MediaMetadataCompat metadata) {
          performOnAllCallbacks(new CallbackCommand() {
              @Override
              public void perform(@NonNull Callback callback) {
                  callback.onMetadataChanged(metadata);
              }
          });
      }
  
      @Override
      public void onPlaybackStateChanged(@Nullable final PlaybackStateCompat state) {
          performOnAllCallbacks(new CallbackCommand() {
              @Override
              public void perform(@NonNull Callback callback) {
                  callback.onPlaybackStateChanged(state);
              }
          });
      }
  
      // This might happen if the MusicService is killed while the Activity is in the
      // foreground and onStart() has been called (but not onStop()).
      @Override
      public void onSessionDestroyed() {
          resetState();
          onPlaybackStateChanged(null);
  
          MediaBrowserHelper.this.onDisconnected();
      }
  }
  </code></pre>
  
  <p>}</p>
  
  <p>Manifest: </p>
  
  <pre><code>&lt;service android:name="com.amco.playermanager.MusicService"&gt;
          &lt;intent-filter&gt;
              &lt;action android:name="android.media.browse.MediaBrowserService"/&gt;
          &lt;/intent-filter&gt;
      &lt;/service&gt;
  
      &lt;receiver android:name="android.support.v4.media.session.MediaButtonReceiver"&gt;
          &lt;intent-filter&gt;
              &lt;action android:name="android.intent.action.MEDIA_BUTTON"/&gt;
          &lt;/intent-filter&gt;
      &lt;/receiver&gt;
  </code></pre>