Commit d746fde1 authored by Min Qin's avatar Min Qin Committed by Commit Bot

Fix an issue that notification will disappear when pausing a download on M and L

on M and L, detaching a notification from foreground service is not supported.
As a result, the current implementation just put the notification in a
sharedPreference and persist it across service restarts.
However, the recent introduction of stopSelf() will remove the notification,
and service will not restart.
The logic for M and L really complicates the implementation, and the only side
effect it tries to save is the notification flicker when pausing a download.
This CL removes the notification persisting logic for L and M. When stopping
the foreground service, we simply kill the existing notification,
and launch a new one.

BUG=919026

Change-Id: Ie9df5d17714d7ff9c54eed8f9b3ca1ddfaaf3199
Reviewed-on: https://chromium-review.googlesource.com/c/1432875Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarXing Liu <xingliu@chromium.org>
Commit-Queue: Min Qin <qinmin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#625803}
parent 58e7a126
...@@ -11,7 +11,6 @@ import android.app.NotificationManager; ...@@ -11,7 +11,6 @@ import android.app.NotificationManager;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Binder; import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
...@@ -32,18 +31,17 @@ import java.lang.annotation.RetentionPolicy; ...@@ -32,18 +31,17 @@ import java.lang.annotation.RetentionPolicy;
*/ */
public class DownloadForegroundService extends Service { public class DownloadForegroundService extends Service {
private static final String TAG = "DownloadFg"; private static final String TAG = "DownloadFg";
// Deprecated, remove this after M75.
private static final String KEY_PERSISTED_NOTIFICATION_ID = "PersistedNotificationId"; private static final String KEY_PERSISTED_NOTIFICATION_ID = "PersistedNotificationId";
private final IBinder mBinder = new LocalBinder(); private final IBinder mBinder = new LocalBinder();
private NotificationManager mNotificationManager; private NotificationManager mNotificationManager;
@IntDef({StopForegroundNotification.KILL, StopForegroundNotification.DETACH_OR_PERSIST, @IntDef({StopForegroundNotification.KILL, StopForegroundNotification.DETACH})
StopForegroundNotification.DETACH_OR_ADJUST})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface StopForegroundNotification { public @interface StopForegroundNotification {
int KILL = 0; // Kill notification regardless of ability to detach. int KILL = 0; // Kill notification regardless of ability to detach.
int DETACH_OR_PERSIST = 1; // Try to detach, otherwise persist info. int DETACH = 1; // Try to detach, otherwise kill and relaunch.
int DETACH_OR_ADJUST = 2; // Try detach, otherwise kill and relaunch.
} }
@Override @Override
...@@ -53,6 +51,7 @@ public class DownloadForegroundService extends Service { ...@@ -53,6 +51,7 @@ public class DownloadForegroundService extends Service {
mNotificationManager = mNotificationManager =
(NotificationManager) ContextUtils.getApplicationContext().getSystemService( (NotificationManager) ContextUtils.getApplicationContext().getSystemService(
Context.NOTIFICATION_SERVICE); Context.NOTIFICATION_SERVICE);
clearPersistedNotificationId();
} }
/** /**
...@@ -123,11 +122,8 @@ public class DownloadForegroundService extends Service { ...@@ -123,11 +122,8 @@ public class DownloadForegroundService extends Service {
* and would need adjustment. * and would need adjustment.
* @param pinnedNotification The actual notification that is pinned to the foreground * @param pinnedNotification The actual notification that is pinned to the foreground
* and would need adjustment. * and would need adjustment.
* @return Whether the notification was handled properly (ie. if it
* was detached or killed as intended). If this is false,
* the notification will be persisted and handled later.
*/ */
public boolean stopDownloadForegroundService( public void stopDownloadForegroundService(
@StopForegroundNotification int stopForegroundNotification, int pinnedNotificationId, @StopForegroundNotification int stopForegroundNotification, int pinnedNotificationId,
Notification pinnedNotification) { Notification pinnedNotification) {
Log.w(TAG, Log.w(TAG,
...@@ -140,7 +136,6 @@ public class DownloadForegroundService extends Service { ...@@ -140,7 +136,6 @@ public class DownloadForegroundService extends Service {
DownloadNotificationUmaHelper.ServiceStopped.STOPPED, true /* withForeground */); DownloadNotificationUmaHelper.ServiceStopped.STOPPED, true /* withForeground */);
// Handle notifications and stop foreground. // Handle notifications and stop foreground.
boolean notificationHandledProperly = true;
if (stopForegroundNotification == StopForegroundNotification.KILL) { if (stopForegroundNotification == StopForegroundNotification.KILL) {
// Regardless of the SDK level, stop foreground and kill if so indicated. // Regardless of the SDK level, stop foreground and kill if so indicated.
stopForegroundInternal(ServiceCompat.STOP_FOREGROUND_REMOVE); stopForegroundInternal(ServiceCompat.STOP_FOREGROUND_REMOVE);
...@@ -152,40 +147,19 @@ public class DownloadForegroundService extends Service { ...@@ -152,40 +147,19 @@ public class DownloadForegroundService extends Service {
} else if (getCurrentSdk() >= 23) { } else if (getCurrentSdk() >= 23) {
// Android M+ can't detach the notification but doesn't have other caveats. Kill the // Android M+ can't detach the notification but doesn't have other caveats. Kill the
// notification and relaunch if detach was desired. // notification and relaunch if detach was desired.
if (stopForegroundNotification == StopForegroundNotification.DETACH_OR_ADJUST) { stopForegroundInternal(ServiceCompat.STOP_FOREGROUND_REMOVE);
stopForegroundInternal(ServiceCompat.STOP_FOREGROUND_REMOVE); relaunchOldNotification(pinnedNotificationId, pinnedNotification);
relaunchOldNotification(pinnedNotificationId, pinnedNotification); } else {
} else {
updatePersistedNotificationId(pinnedNotificationId);
stopForegroundInternal(ServiceCompat.STOP_FOREGROUND_DETACH);
notificationHandledProperly = false;
}
} else if (getCurrentSdk() >= 21) {
// In phones that are Lollipop and older (API < 23), the service gets killed with // In phones that are Lollipop and older (API < 23), the service gets killed with
// the task, which might result in the notification being unable to be relaunched // the task, which might result in the notification being unable to be relaunched
// where it needs to be. Relaunch the old notification before stopping the service. // where it needs to be. kill and relaunch the old notification before stopping the
if (stopForegroundNotification == StopForegroundNotification.DETACH_OR_ADJUST) { // service.
// Clear pinned id in case the service stops before we can do this properly.
relaunchOldNotification(
getNewNotificationIdFor(pinnedNotificationId), pinnedNotification);
stopForegroundInternal(ServiceCompat.STOP_FOREGROUND_REMOVE);
} else {
updatePersistedNotificationId(pinnedNotificationId);
stopForegroundInternal(ServiceCompat.STOP_FOREGROUND_DETACH);
notificationHandledProperly = false;
}
} else {
// For pre-Lollipop phones (API < 21), we need to kill all notifications because
// otherwise the notification gets stuck in the ongoing state.
relaunchOldNotification( relaunchOldNotification(
getNewNotificationIdFor(pinnedNotificationId), pinnedNotification); getNewNotificationIdFor(pinnedNotificationId), pinnedNotification);
stopForegroundInternal(ServiceCompat.STOP_FOREGROUND_REMOVE); stopForegroundInternal(ServiceCompat.STOP_FOREGROUND_REMOVE);
} }
} }
stopSelf(); stopSelf();
return notificationHandledProperly;
} }
@VisibleForTesting @VisibleForTesting
...@@ -202,15 +176,6 @@ public class DownloadForegroundService extends Service { ...@@ -202,15 +176,6 @@ public class DownloadForegroundService extends Service {
DownloadNotificationUmaHelper.recordServiceStoppedHistogram( DownloadNotificationUmaHelper.recordServiceStoppedHistogram(
DownloadNotificationUmaHelper.ServiceStopped.START_STICKY, true); DownloadNotificationUmaHelper.ServiceStopped.START_STICKY, true);
// Alert observers that the service restarted with null intent.
// Pass along the id of the notification that was pinned to the service when it died so
// that the observers can do any corrections (ie. relaunch notification) if needed.
int persistedNotificationId = getPersistedNotificationId();
Log.w(TAG, "onStartCommand intent: " + null + ", id: " + persistedNotificationId);
DownloadForegroundServiceObservers.alertObserversServiceRestarted(
persistedNotificationId);
clearPersistedNotificationId();
// Allow observers to restart service on their own, if needed. // Allow observers to restart service on their own, if needed.
stopSelf(); stopSelf();
} }
...@@ -257,28 +222,6 @@ public class DownloadForegroundService extends Service { ...@@ -257,28 +222,6 @@ public class DownloadForegroundService extends Service {
} }
} }
/**
* Get stored value for the id of the notification pinned to the service.
* This has to be persisted in the case that the service dies and the notification dies with it.
* @return Id of the notification pinned to the service.
*/
@VisibleForTesting
static int getPersistedNotificationId() {
SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences();
return sharedPrefs.getInt(KEY_PERSISTED_NOTIFICATION_ID, INVALID_NOTIFICATION_ID);
}
/**
* Set stored value for the id of the notification pinned to the service.
* This has to be persisted in the case that the service dies and the notification dies with it.
* @param pinnedNotificationId Id of the notification pinned to the service.
*/
private static void updatePersistedNotificationId(int pinnedNotificationId) {
ContextUtils.getAppSharedPreferences()
.edit()
.putInt(KEY_PERSISTED_NOTIFICATION_ID, pinnedNotificationId)
.apply();
}
/** /**
* Clear stored value for the id of the notification pinned to the service. * Clear stored value for the id of the notification pinned to the service.
......
...@@ -274,41 +274,33 @@ public class DownloadForegroundServiceManager { ...@@ -274,41 +274,33 @@ public class DownloadForegroundServiceManager {
int stopForegroundNotification; int stopForegroundNotification;
if (downloadStatus == DownloadNotificationService.DownloadStatus.CANCELLED) { if (downloadStatus == DownloadNotificationService.DownloadStatus.CANCELLED) {
stopForegroundNotification = DownloadForegroundService.StopForegroundNotification.KILL; stopForegroundNotification = DownloadForegroundService.StopForegroundNotification.KILL;
} else if (downloadStatus == DownloadNotificationService.DownloadStatus.PAUSED) {
stopForegroundNotification =
DownloadForegroundService.StopForegroundNotification.DETACH_OR_PERSIST;
} else { } else {
stopForegroundNotification = stopForegroundNotification =
DownloadForegroundService.StopForegroundNotification.DETACH_OR_ADJUST; DownloadForegroundService.StopForegroundNotification.DETACH;
} }
DownloadUpdate downloadUpdate = mDownloadUpdateQueue.get(mPinnedNotificationId); DownloadUpdate downloadUpdate = mDownloadUpdateQueue.get(mPinnedNotificationId);
Notification oldNotification = Notification oldNotification =
(downloadUpdate == null) ? null : downloadUpdate.mNotification; (downloadUpdate == null) ? null : downloadUpdate.mNotification;
boolean notificationHandledProperly = stopAndUnbindServiceInternal( stopAndUnbindServiceInternal(
stopForegroundNotification, mPinnedNotificationId, oldNotification); stopForegroundNotification, mPinnedNotificationId, oldNotification);
mBoundService = null; mBoundService = null;
// Only if the notification was handled properly (ie. detached or killed), reset stored ID. mPinnedNotificationId = INVALID_NOTIFICATION_ID;
if (notificationHandledProperly) mPinnedNotificationId = INVALID_NOTIFICATION_ID;
} }
@VisibleForTesting @VisibleForTesting
boolean stopAndUnbindServiceInternal( void stopAndUnbindServiceInternal(
@DownloadForegroundService.StopForegroundNotification int stopForegroundStatus, @DownloadForegroundService.StopForegroundNotification int stopForegroundStatus,
int pinnedNotificationId, Notification pinnedNotification) { int pinnedNotificationId, Notification pinnedNotification) {
boolean notificationHandledProperly = mBoundService.stopDownloadForegroundService( mBoundService.stopDownloadForegroundService(
stopForegroundStatus, pinnedNotificationId, pinnedNotification); stopForegroundStatus, pinnedNotificationId, pinnedNotification);
ContextUtils.getApplicationContext().unbindService(mConnection); ContextUtils.getApplicationContext().unbindService(mConnection);
if (notificationHandledProperly) { DownloadForegroundServiceObservers.removeObserver(
DownloadForegroundServiceObservers.removeObserver( DownloadNotificationServiceObserver.class);
DownloadNotificationServiceObserver.class);
}
return notificationHandledProperly;
} }
/** Helper code for testing. */ /** Helper code for testing. */
......
...@@ -71,11 +71,10 @@ public final class DownloadForegroundServiceManagerTest { ...@@ -71,11 +71,10 @@ public final class DownloadForegroundServiceManagerTest {
} }
@Override @Override
boolean stopAndUnbindServiceInternal(@DownloadForegroundService.StopForegroundNotification void stopAndUnbindServiceInternal(@DownloadForegroundService.StopForegroundNotification
int stopForegroundNotification, int stopForegroundNotification,
int pinnedNotificationId, Notification pinnedNotification) { int pinnedNotificationId, Notification pinnedNotification) {
mStopForegroundNotificationFlag = stopForegroundNotification; mStopForegroundNotificationFlag = stopForegroundNotification;
return true;
} }
@Override @Override
...@@ -231,7 +230,7 @@ public final class DownloadForegroundServiceManagerTest { ...@@ -231,7 +230,7 @@ public final class DownloadForegroundServiceManagerTest {
mDownloadServiceManager.updateDownloadStatus(mContext, mDownloadServiceManager.updateDownloadStatus(mContext,
DownloadNotificationService.DownloadStatus.PAUSED, FAKE_DOWNLOAD_1, mNotification); DownloadNotificationService.DownloadStatus.PAUSED, FAKE_DOWNLOAD_1, mNotification);
assertFalse(mDownloadServiceManager.mIsServiceBound); assertFalse(mDownloadServiceManager.mIsServiceBound);
assertEquals(DownloadForegroundService.StopForegroundNotification.DETACH_OR_PERSIST, assertEquals(DownloadForegroundService.StopForegroundNotification.DETACH,
mDownloadServiceManager.mStopForegroundNotificationFlag); mDownloadServiceManager.mStopForegroundNotificationFlag);
// Service restarts and then is cancelled, so notification is killed. // Service restarts and then is cancelled, so notification is killed.
...@@ -259,7 +258,7 @@ public final class DownloadForegroundServiceManagerTest { ...@@ -259,7 +258,7 @@ public final class DownloadForegroundServiceManagerTest {
DownloadNotificationService.DownloadStatus.COMPLETED, FAKE_DOWNLOAD_2, DownloadNotificationService.DownloadStatus.COMPLETED, FAKE_DOWNLOAD_2,
mNotification); mNotification);
assertFalse(mDownloadServiceManager.mIsServiceBound); assertFalse(mDownloadServiceManager.mIsServiceBound);
assertEquals(DownloadForegroundService.StopForegroundNotification.DETACH_OR_ADJUST, assertEquals(DownloadForegroundService.StopForegroundNotification.DETACH,
mDownloadServiceManager.mStopForegroundNotificationFlag); mDownloadServiceManager.mStopForegroundNotificationFlag);
} }
......
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 to comment