Fixed 'Service started without startForeground'

When we called stopForeground(), the service went to background state.
If we then somehow receive a second intent to onStartCommand (without onCreate),
we assume that the service already is in foreground, what is wrong.

This commit moves the service to foreground in onStartCommand if
it is no longer in foreground.
This commit is contained in:
ByteHamster 2019-08-23 18:54:05 +02:00
parent df1d43a254
commit b5c63db0e9
3 changed files with 81 additions and 41 deletions

View File

@ -195,10 +195,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
* Is true if service is running.
*/
public static boolean isRunning = false;
/**
* Is true if service has received a valid start command.
*/
public static boolean started = false;
/**
* Is true if the service was running, but paused due to headphone disconnect
*/
@ -214,6 +210,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
private PlaybackServiceMediaPlayer mediaPlayer;
private PlaybackServiceTaskManager taskManager;
private PlaybackServiceFlavorHelper flavorHelper;
private PlaybackServiceStateManager stateManager;
/**
* Used for Lollipop notifications, Android Wear, and Android Auto.
@ -268,8 +265,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Log.d(TAG, "Service created.");
isRunning = true;
stateManager = new PlaybackServiceStateManager(this);
PlaybackServiceNotificationBuilder notificationBuilder = new PlaybackServiceNotificationBuilder(this);
startForeground(NOTIFICATION_ID, notificationBuilder.build());
stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build());
registerReceiver(autoStateUpdated, new IntentFilter("com.google.android.gms.car.media.STATUS"));
registerReceiver(headsetDisconnected, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
@ -328,9 +326,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Service is about to be destroyed");
stopForeground(true);
stateManager.stopForeground(true);
isRunning = false;
started = false;
currentMediaType = MediaType.UNKNOWN;
PreferenceManager.getDefaultSharedPreferences(this)
@ -351,11 +348,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
mediaPlayer.shutdown();
taskManager.shutdown();
}
private void stopService() {
stopForeground(true);
stopSelf();
}
@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) {
@ -449,28 +441,34 @@ public class PlaybackService extends MediaBrowserServiceCompat {
super.onStartCommand(intent, flags, startId);
Log.d(TAG, "OnStartCommand called");
if (!stateManager.isInForeground()) {
PlaybackServiceNotificationBuilder notificationBuilder = new PlaybackServiceNotificationBuilder(this);
startForeground(NOTIFICATION_ID, notificationBuilder.build());
}
final int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1);
final boolean castDisconnect = intent.getBooleanExtra(EXTRA_CAST_DISCONNECT, false);
Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE);
if (keycode == -1 && playable == null && !castDisconnect) {
Log.e(TAG, "PlaybackService was started with no arguments");
stopService();
stateManager.stopService();
return Service.START_NOT_STICKY;
}
if ((flags & Service.START_FLAG_REDELIVERY) != 0) {
Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.");
stopForeground(true);
stateManager.stopForeground(true);
} else {
if (keycode != -1) {
Log.d(TAG, "Received media button event");
boolean handled = handleKeycode(keycode, true);
if (!handled) {
stopService();
stateManager.stopService();
return Service.START_NOT_STICKY;
}
} else if (!flavorHelper.castDisconnect(castDisconnect) && playable != null) {
started = true;
stateManager.validStartCommandWasReceived();
boolean stream = intent.getBooleanExtra(EXTRA_SHOULD_STREAM, true);
boolean allowStreamThisTime = intent.getBooleanExtra(EXTRA_ALLOW_STREAM_THIS_TIME, false);
boolean startWhenPrepared = intent.getBooleanExtra(EXTRA_START_WHEN_PREPARED, false);
@ -484,7 +482,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (stream && !NetworkUtils.isStreamingAllowed() && !allowStreamThisTime) {
displayStreamingNotAllowedNotification(intent);
writePlaybackPreferencesNoMediaPlaying();
stopService();
stateManager.stopService();
return Service.START_NOT_STICKY;
}
mediaPlayer.playMediaObject(playable, stream, startWhenPrepared, prepareImmediately);
@ -590,10 +588,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case KeyEvent.KEYCODE_MEDIA_STOP:
if (status == PlayerStatus.PLAYING) {
mediaPlayer.pause(true, true);
started = false;
}
stopForeground(true); // gets rid of persistent notification
stateManager.stopForeground(true); // gets rid of persistent notification
return true;
default:
Log.d(TAG, "Unhandled key code: " + keycode);
@ -609,10 +606,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Playable playable = Playable.PlayableUtils.createInstanceFromPreferences(getApplicationContext());
if (playable != null) {
mediaPlayer.playMediaObject(playable, false, true, true);
started = true;
stateManager.validStartCommandWasReceived();
PlaybackService.this.updateMediaSessionMetadata(playable);
} else {
stopService();
stateManager.stopService();
}
}
@ -629,7 +626,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
mediaPlayer.pause(true, false);
mediaPlayer.resetVideoSurface();
setupNotification(getPlayable());
stopForeground(!UserPreferences.isPersistNotify());
stateManager.stopForeground(!UserPreferences.isPersistNotify());
}
private final PlaybackServiceTaskManager.PSTMCallback taskManagerCallback = new PlaybackServiceTaskManager.PSTMCallback() {
@ -699,7 +696,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
setupNotification(newInfo);
} else if (!UserPreferences.isPersistNotify() && !isCasting) {
// remove notification on pause
stopForeground(true);
stateManager.stopForeground(true);
}
writePlayerStatusPlaybackPreferences();
break;
@ -712,7 +709,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case PLAYING:
writePlayerStatusPlaybackPreferences();
setupNotification(newInfo);
started = true;
stateManager.validStartCommandWasReceived();
// set sleep timer if auto-enabled
if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING &&
SleepTimerPreferences.autoEnable() && !sleepTimerActive()) {
@ -723,7 +720,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case ERROR:
writePlaybackPreferencesNoMediaPlaying();
stopService();
stateManager.stopService();
break;
}
@ -736,7 +733,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void shouldStop() {
stopService();
stateManager.stopService();
}
@Override
@ -785,7 +782,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
writePlaybackPreferencesNoMediaPlaying();
stopService();
stateManager.stopService();
return true;
}
@ -870,7 +867,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
.shouldStream(true)
.getIntent());
writePlaybackPreferencesNoMediaPlaying();
stopService();
stateManager.stopService();
return null;
}
return nextItem.getMedia();
@ -886,7 +883,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
taskManager.cancelPositionSaver();
writePlaybackPreferencesNoMediaPlaying();
if (!isCasting) {
stopForeground(true);
stateManager.stopForeground(true);
}
}
if (mediaType == null) {
@ -1218,7 +1215,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, imageLocation);
}
}
if (!Thread.currentThread().isInterrupted() && started) {
if (!Thread.currentThread().isInterrupted() && stateManager.hasReceivedValidStartCommand()) {
mediaSession.setSessionActivity(PendingIntent.getActivity(this, 0,
PlaybackService.getPlayerActivityIntent(this),
PendingIntent.FLAG_UPDATE_CURRENT));
@ -1254,8 +1251,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
if (playable == null) {
Log.d(TAG, "setupNotification: playable is null" + Log.getStackTraceString(new Exception()));
if (!started) {
stopService();
if (!stateManager.hasReceivedValidStartCommand()) {
stateManager.stopService();
}
return;
}
@ -1266,8 +1263,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (mediaPlayer == null) {
Log.d(TAG, "notificationSetupTask: mediaPlayer is null");
if (!started) {
stopService();
if (!stateManager.hasReceivedValidStartCommand()) {
stateManager.stopService();
}
return;
}
@ -1279,20 +1276,20 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (!notificationBuilder.isIconCached(playable)) {
// To make sure that the notification is shown instantly
notificationBuilder.loadDefaultIcon();
startForeground(NOTIFICATION_ID, notificationBuilder.build());
stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build());
}
notificationBuilder.loadIcon(playable);
if (!Thread.currentThread().isInterrupted() && started) {
if (!Thread.currentThread().isInterrupted() && stateManager.hasReceivedValidStartCommand()) {
Notification notification = notificationBuilder.build();
if (playerStatus == PlayerStatus.PLAYING ||
playerStatus == PlayerStatus.PREPARING ||
playerStatus == PlayerStatus.SEEKING ||
isCasting) {
startForeground(NOTIFICATION_ID, notification);
stateManager.startForeground(NOTIFICATION_ID, notification);
} else {
stopForeground(false);
stateManager.stopForeground(false);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotificationManager.notify(NOTIFICATION_ID, notification);
}
@ -1481,7 +1478,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
stopService();
stateManager.stopService();
}
}
@ -1832,7 +1829,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
PlaybackService.this.setupNotification(info);
} else if (!UserPreferences.isPersistNotify()) {
PlaybackService.this.stopForeground(true);
stateManager.stopForeground(true);
}
}
}

View File

@ -0,0 +1,43 @@
package de.danoeh.antennapod.core.service.playback;
import android.app.Notification;
class PlaybackServiceStateManager {
private final PlaybackService playbackService;
private volatile boolean isInForeground = false;
private volatile boolean hasReceivedValidStartCommand = false;
PlaybackServiceStateManager(PlaybackService playbackService) {
this.playbackService = playbackService;
}
void startForeground(int notificationId, Notification notification) {
playbackService.startForeground(notificationId, notification);
isInForeground = true;
}
void stopService() {
stopForeground(true);
isInForeground = false;
playbackService.stopSelf();
}
void stopForeground(boolean removeNotification) {
playbackService.stopForeground(removeNotification);
isInForeground = false;
hasReceivedValidStartCommand = false;
}
boolean isInForeground() {
return isInForeground;
}
boolean hasReceivedValidStartCommand() {
return hasReceivedValidStartCommand;
}
void validStartCommandWasReceived() {
this.hasReceivedValidStartCommand = true;
}
}

View File

@ -210,7 +210,7 @@ public abstract class PlaybackController {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(optionalIntent -> {
boolean bound = false;
if (!PlaybackService.started) {
if (!PlaybackService.isRunning) {
if (optionalIntent.isPresent()) {
Log.d(TAG, "Calling start service");
ContextCompat.startForegroundService(activity, optionalIntent.get());