Commit b3c37170 by tonihei Committed by Andrew Lewis

Prevent NPE in PlayerNotificationManager.

The app can set the player to null while messages from the player are still
in flight. This may cause NPEs.

Issue:#4238

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=196504077
parent 1af93341
...@@ -36,6 +36,7 @@ dependencies { ...@@ -36,6 +36,7 @@ dependencies {
implementation project(modulePrefix + 'library-core') implementation project(modulePrefix + 'library-core')
implementation 'com.android.support:support-media-compat:' + supportLibraryVersion implementation 'com.android.support:support-media-compat:' + supportLibraryVersion
implementation 'com.android.support:support-annotations:' + supportLibraryVersion implementation 'com.android.support:support-annotations:' + supportLibraryVersion
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
} }
ext { ext {
......
...@@ -50,6 +50,7 @@ import java.util.Collections; ...@@ -50,6 +50,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** /**
* A notification manager to start, update and cancel a media style notification reflecting the * A notification manager to start, update and cancel a media style notification reflecting the
...@@ -205,7 +206,9 @@ public class PlayerNotificationManager { ...@@ -205,7 +206,9 @@ public class PlayerNotificationManager {
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
if (notificationTag == currentNotificationTag && isNotificationStarted) { if (player != null
&& notificationTag == currentNotificationTag
&& isNotificationStarted) {
updateNotification(bitmap); updateNotification(bitmap);
} }
} }
...@@ -260,7 +263,7 @@ public class PlayerNotificationManager { ...@@ -260,7 +263,7 @@ public class PlayerNotificationManager {
private final String channelId; private final String channelId;
private final int notificationId; private final int notificationId;
private final MediaDescriptionAdapter mediaDescriptionAdapter; private final MediaDescriptionAdapter mediaDescriptionAdapter;
private final CustomActionReceiver customActionReceiver; private final @Nullable CustomActionReceiver customActionReceiver;
private final Handler mainHandler; private final Handler mainHandler;
private final NotificationManagerCompat notificationManager; private final NotificationManagerCompat notificationManager;
private final IntentFilter intentFilter; private final IntentFilter intentFilter;
...@@ -269,12 +272,12 @@ public class PlayerNotificationManager { ...@@ -269,12 +272,12 @@ public class PlayerNotificationManager {
private final Map<String, NotificationCompat.Action> playbackActions; private final Map<String, NotificationCompat.Action> playbackActions;
private final Map<String, NotificationCompat.Action> customActions; private final Map<String, NotificationCompat.Action> customActions;
private Player player; private @Nullable Player player;
private ControlDispatcher controlDispatcher; private ControlDispatcher controlDispatcher;
private boolean isNotificationStarted; private boolean isNotificationStarted;
private int currentNotificationTag; private int currentNotificationTag;
private NotificationListener notificationListener; private @Nullable NotificationListener notificationListener;
private MediaSessionCompat.Token mediaSessionToken; private @Nullable MediaSessionCompat.Token mediaSessionToken;
private boolean useNavigationActions; private boolean useNavigationActions;
private boolean usePlayPauseActions; private boolean usePlayPauseActions;
private @Nullable String stopAction; private @Nullable String stopAction;
...@@ -365,6 +368,20 @@ public class PlayerNotificationManager { ...@@ -365,6 +368,20 @@ public class PlayerNotificationManager {
playerListener = new PlayerListener(); playerListener = new PlayerListener();
notificationBroadcastReceiver = new NotificationBroadcastReceiver(); notificationBroadcastReceiver = new NotificationBroadcastReceiver();
intentFilter = new IntentFilter(); intentFilter = new IntentFilter();
useNavigationActions = true;
usePlayPauseActions = true;
ongoing = true;
colorized = true;
useChronometer = true;
color = Color.TRANSPARENT;
smallIconResourceId = R.drawable.exo_notification_small_icon;
defaults = 0;
priority = NotificationCompat.PRIORITY_LOW;
fastForwardMs = DEFAULT_FAST_FORWARD_MS;
rewindMs = DEFAULT_REWIND_MS;
stopAction = ACTION_STOP;
badgeIconType = NotificationCompat.BADGE_ICON_SMALL;
visibility = NotificationCompat.VISIBILITY_PUBLIC;
// initialize actions // initialize actions
playbackActions = createPlaybackActions(context); playbackActions = createPlaybackActions(context);
...@@ -378,22 +395,7 @@ public class PlayerNotificationManager { ...@@ -378,22 +395,7 @@ public class PlayerNotificationManager {
for (String action : customActions.keySet()) { for (String action : customActions.keySet()) {
intentFilter.addAction(action); intentFilter.addAction(action);
} }
stopPendingIntent = Assertions.checkNotNull(playbackActions.get(ACTION_STOP)).actionIntent;
setStopAction(ACTION_STOP);
useNavigationActions = true;
usePlayPauseActions = true;
ongoing = true;
colorized = true;
useChronometer = true;
color = Color.TRANSPARENT;
smallIconResourceId = R.drawable.exo_notification_small_icon;
defaults = 0;
priority = NotificationCompat.PRIORITY_LOW;
fastForwardMs = DEFAULT_FAST_FORWARD_MS;
rewindMs = DEFAULT_REWIND_MS;
setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL);
setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
} }
/** /**
...@@ -512,10 +514,9 @@ public class PlayerNotificationManager { ...@@ -512,10 +514,9 @@ public class PlayerNotificationManager {
} }
this.stopAction = stopAction; this.stopAction = stopAction;
if (ACTION_STOP.equals(stopAction)) { if (ACTION_STOP.equals(stopAction)) {
stopPendingIntent = playbackActions.get(ACTION_STOP).actionIntent; stopPendingIntent = Assertions.checkNotNull(playbackActions.get(ACTION_STOP)).actionIntent;
} else if (stopAction != null) { } else if (stopAction != null) {
Assertions.checkArgument(customActions.containsKey(stopAction)); stopPendingIntent = Assertions.checkNotNull(customActions.get(stopAction)).actionIntent;
stopPendingIntent = customActions.get(stopAction).actionIntent;
} else { } else {
stopPendingIntent = null; stopPendingIntent = null;
} }
...@@ -698,25 +699,28 @@ public class PlayerNotificationManager { ...@@ -698,25 +699,28 @@ public class PlayerNotificationManager {
maybeUpdateNotification(); maybeUpdateNotification();
} }
private Notification updateNotification(Bitmap bitmap) { @RequiresNonNull("player")
private Notification updateNotification(@Nullable Bitmap bitmap) {
Notification notification = createNotification(player, bitmap); Notification notification = createNotification(player, bitmap);
notificationManager.notify(notificationId, notification); notificationManager.notify(notificationId, notification);
return notification; return notification;
} }
private void startOrUpdateNotification() { private void startOrUpdateNotification() {
Notification notification = updateNotification(null); if (player != null) {
if (!isNotificationStarted) { Notification notification = updateNotification(null);
isNotificationStarted = true; if (!isNotificationStarted) {
context.registerReceiver(notificationBroadcastReceiver, intentFilter); isNotificationStarted = true;
if (notificationListener != null) { context.registerReceiver(notificationBroadcastReceiver, intentFilter);
notificationListener.onNotificationStarted(notificationId, notification); if (notificationListener != null) {
notificationListener.onNotificationStarted(notificationId, notification);
}
} }
} }
} }
private void maybeUpdateNotification() { private void maybeUpdateNotification() {
if (isNotificationStarted) { if (isNotificationStarted && player != null) {
updateNotification(null); updateNotification(null);
} }
} }
...@@ -732,64 +736,6 @@ public class PlayerNotificationManager { ...@@ -732,64 +736,6 @@ public class PlayerNotificationManager {
} }
} }
private Map<String, NotificationCompat.Action> createPlaybackActions(Context context) {
Map<String, NotificationCompat.Action> actions = new HashMap<>();
Intent playIntent = new Intent(ACTION_PLAY).setPackage(context.getPackageName());
actions.put(
ACTION_PLAY,
new NotificationCompat.Action(
R.drawable.exo_notification_play,
context.getString(R.string.exo_controls_play_description),
PendingIntent.getBroadcast(context, 0, playIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent pauseIntent = new Intent(ACTION_PAUSE).setPackage(context.getPackageName());
actions.put(
ACTION_PAUSE,
new NotificationCompat.Action(
R.drawable.exo_notification_pause,
context.getString(R.string.exo_controls_pause_description),
PendingIntent.getBroadcast(
context, 0, pauseIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent stopIntent = new Intent(ACTION_STOP).setPackage(context.getPackageName());
actions.put(
ACTION_STOP,
new NotificationCompat.Action(
R.drawable.exo_notification_stop,
context.getString(R.string.exo_controls_stop_description),
PendingIntent.getBroadcast(context, 0, stopIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent rewindIntent = new Intent(ACTION_REWIND).setPackage(context.getPackageName());
actions.put(
ACTION_REWIND,
new NotificationCompat.Action(
R.drawable.exo_notification_rewind,
context.getString(R.string.exo_controls_rewind_description),
PendingIntent.getBroadcast(
context, 0, rewindIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent fastForwardIntent = new Intent(ACTION_FAST_FORWARD).setPackage(context.getPackageName());
actions.put(
ACTION_FAST_FORWARD,
new NotificationCompat.Action(
R.drawable.exo_notification_fastforward,
context.getString(R.string.exo_controls_fastforward_description),
PendingIntent.getBroadcast(
context, 0, fastForwardIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent previousIntent = new Intent(ACTION_PREVIOUS).setPackage(context.getPackageName());
actions.put(
ACTION_PREVIOUS,
new NotificationCompat.Action(
R.drawable.exo_notification_previous,
context.getString(R.string.exo_controls_previous_description),
PendingIntent.getBroadcast(
context, 0, previousIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent nextIntent = new Intent(ACTION_NEXT).setPackage(context.getPackageName());
actions.put(
ACTION_NEXT,
new NotificationCompat.Action(
R.drawable.exo_notification_next,
context.getString(R.string.exo_controls_next_description),
PendingIntent.getBroadcast(context, 0, nextIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
return actions;
}
/** /**
* Creates the notification given the current player state. * Creates the notification given the current player state.
* *
...@@ -821,7 +767,7 @@ public class PlayerNotificationManager { ...@@ -821,7 +767,7 @@ public class PlayerNotificationManager {
// Configure stop action (eg. when user dismisses the notification when !isOngoing). // Configure stop action (eg. when user dismisses the notification when !isOngoing).
boolean useStopAction = stopAction != null && !isPlayingAd; boolean useStopAction = stopAction != null && !isPlayingAd;
mediaStyle.setShowCancelButton(useStopAction); mediaStyle.setShowCancelButton(useStopAction);
if (useStopAction) { if (useStopAction && stopPendingIntent != null) {
builder.setDeleteIntent(stopPendingIntent); builder.setDeleteIntent(stopPendingIntent);
mediaStyle.setCancelButtonIntent(stopPendingIntent); mediaStyle.setCancelButtonIntent(stopPendingIntent);
} }
...@@ -905,7 +851,7 @@ public class PlayerNotificationManager { ...@@ -905,7 +851,7 @@ public class PlayerNotificationManager {
if (useNavigationActions && player.getNextWindowIndex() != C.INDEX_UNSET) { if (useNavigationActions && player.getNextWindowIndex() != C.INDEX_UNSET) {
stringActions.add(ACTION_NEXT); stringActions.add(ACTION_NEXT);
} }
if (!customActions.isEmpty()) { if (customActionReceiver != null) {
stringActions.addAll(customActionReceiver.getCustomActions(player)); stringActions.addAll(customActionReceiver.getCustomActions(player));
} }
if (ACTION_STOP.equals(stopAction)) { if (ACTION_STOP.equals(stopAction)) {
...@@ -932,6 +878,64 @@ public class PlayerNotificationManager { ...@@ -932,6 +878,64 @@ public class PlayerNotificationManager {
return new int[] {actionIndex}; return new int[] {actionIndex};
} }
private static Map<String, NotificationCompat.Action> createPlaybackActions(Context context) {
Map<String, NotificationCompat.Action> actions = new HashMap<>();
Intent playIntent = new Intent(ACTION_PLAY).setPackage(context.getPackageName());
actions.put(
ACTION_PLAY,
new NotificationCompat.Action(
R.drawable.exo_notification_play,
context.getString(R.string.exo_controls_play_description),
PendingIntent.getBroadcast(context, 0, playIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent pauseIntent = new Intent(ACTION_PAUSE).setPackage(context.getPackageName());
actions.put(
ACTION_PAUSE,
new NotificationCompat.Action(
R.drawable.exo_notification_pause,
context.getString(R.string.exo_controls_pause_description),
PendingIntent.getBroadcast(
context, 0, pauseIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent stopIntent = new Intent(ACTION_STOP).setPackage(context.getPackageName());
actions.put(
ACTION_STOP,
new NotificationCompat.Action(
R.drawable.exo_notification_stop,
context.getString(R.string.exo_controls_stop_description),
PendingIntent.getBroadcast(context, 0, stopIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent rewindIntent = new Intent(ACTION_REWIND).setPackage(context.getPackageName());
actions.put(
ACTION_REWIND,
new NotificationCompat.Action(
R.drawable.exo_notification_rewind,
context.getString(R.string.exo_controls_rewind_description),
PendingIntent.getBroadcast(
context, 0, rewindIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent fastForwardIntent = new Intent(ACTION_FAST_FORWARD).setPackage(context.getPackageName());
actions.put(
ACTION_FAST_FORWARD,
new NotificationCompat.Action(
R.drawable.exo_notification_fastforward,
context.getString(R.string.exo_controls_fastforward_description),
PendingIntent.getBroadcast(
context, 0, fastForwardIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent previousIntent = new Intent(ACTION_PREVIOUS).setPackage(context.getPackageName());
actions.put(
ACTION_PREVIOUS,
new NotificationCompat.Action(
R.drawable.exo_notification_previous,
context.getString(R.string.exo_controls_previous_description),
PendingIntent.getBroadcast(
context, 0, previousIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
Intent nextIntent = new Intent(ACTION_NEXT).setPackage(context.getPackageName());
actions.put(
ACTION_NEXT,
new NotificationCompat.Action(
R.drawable.exo_notification_next,
context.getString(R.string.exo_controls_next_description),
PendingIntent.getBroadcast(context, 0, nextIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
return actions;
}
private class PlayerListener extends Player.DefaultEventListener { private class PlayerListener extends Player.DefaultEventListener {
@Override @Override
...@@ -946,7 +950,7 @@ public class PlayerNotificationManager { ...@@ -946,7 +950,7 @@ public class PlayerNotificationManager {
@Override @Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) { public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
if (player.getPlaybackState() == Player.STATE_IDLE) { if (player == null || player.getPlaybackState() == Player.STATE_IDLE) {
return; return;
} }
startOrUpdateNotification(); startOrUpdateNotification();
...@@ -954,7 +958,7 @@ public class PlayerNotificationManager { ...@@ -954,7 +958,7 @@ public class PlayerNotificationManager {
@Override @Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
if (player.getPlaybackState() == Player.STATE_IDLE) { if (player == null || player.getPlaybackState() == Player.STATE_IDLE) {
return; return;
} }
startOrUpdateNotification(); startOrUpdateNotification();
...@@ -967,7 +971,7 @@ public class PlayerNotificationManager { ...@@ -967,7 +971,7 @@ public class PlayerNotificationManager {
@Override @Override
public void onRepeatModeChanged(int repeatMode) { public void onRepeatModeChanged(int repeatMode) {
if (player.getPlaybackState() == Player.STATE_IDLE) { if (player == null || player.getPlaybackState() == Player.STATE_IDLE) {
return; return;
} }
startOrUpdateNotification(); startOrUpdateNotification();
...@@ -985,7 +989,8 @@ public class PlayerNotificationManager { ...@@ -985,7 +989,8 @@ public class PlayerNotificationManager {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (!isNotificationStarted) { Player player = PlayerNotificationManager.this.player;
if (player == null || !isNotificationStarted) {
return; return;
} }
String action = intent.getAction(); String action = intent.getAction();
...@@ -1013,7 +1018,7 @@ public class PlayerNotificationManager { ...@@ -1013,7 +1018,7 @@ public class PlayerNotificationManager {
} else if (ACTION_STOP.equals(action)) { } else if (ACTION_STOP.equals(action)) {
controlDispatcher.dispatchStop(player, true); controlDispatcher.dispatchStop(player, true);
stopNotification(); stopNotification();
} else if (customActions.containsKey(action)) { } else if (customActionReceiver != null && customActions.containsKey(action)) {
customActionReceiver.onCustomAction(player, action, intent); customActionReceiver.onCustomAction(player, action, intent);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment