Merge "Force FGS notifications to show for a minimum time" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
88c651eab1
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.Notification;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.statusbar.NotificationData;
|
||||
|
||||
/**
|
||||
* Extends the lifetime of foreground notification services such that they show for at least
|
||||
* five seconds
|
||||
*/
|
||||
public class ForegroundServiceLifetimeExtender implements NotificationLifetimeExtender {
|
||||
private static final String TAG = "FGSLifetimeExtender";
|
||||
|
||||
@VisibleForTesting
|
||||
public static final int MIN_FGS_TIME_MS = 5000;
|
||||
|
||||
private NotificationSafeToRemoveCallback mNotificationSafeToRemoveCallback;
|
||||
private ArraySet<NotificationData.Entry> mManagedEntries = new ArraySet<>();
|
||||
private Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
public ForegroundServiceLifetimeExtender() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCallback(@NonNull NotificationSafeToRemoveCallback callback) {
|
||||
mNotificationSafeToRemoveCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
|
||||
if ((entry.notification.getNotification().flags
|
||||
& Notification.FLAG_FOREGROUND_SERVICE) == 0) {
|
||||
return false;
|
||||
}
|
||||
long currentTime = System.currentTimeMillis();
|
||||
return currentTime - entry.notification.getPostTime() < MIN_FGS_TIME_MS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExtendLifetimeForPendingNotification(
|
||||
@NonNull NotificationData.Entry entry) {
|
||||
return shouldExtendLifetime(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShouldManageLifetime(
|
||||
@NonNull NotificationData.Entry entry, boolean shouldManage) {
|
||||
android.util.Log.d("FGSExtender", "setShouldManageLifetime " + shouldManage);
|
||||
if (!shouldManage) {
|
||||
mManagedEntries.remove(entry);
|
||||
return;
|
||||
}
|
||||
mManagedEntries.add(entry);
|
||||
Runnable r = () -> {
|
||||
if (mManagedEntries.contains(entry)) {
|
||||
mManagedEntries.remove(entry);
|
||||
if (mNotificationSafeToRemoveCallback != null) {
|
||||
mNotificationSafeToRemoveCallback.onSafeToRemove(entry.key);
|
||||
}
|
||||
}
|
||||
};
|
||||
long delayAmt = MIN_FGS_TIME_MS
|
||||
- (System.currentTimeMillis() - entry.notification.getPostTime());
|
||||
mHandler.postDelayed(r, delayAmt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import com.android.systemui.statusbar.NotificationData;
|
||||
|
||||
/**
|
||||
* Interface for anything that may need to keep notifications managed even after
|
||||
* {@link NotificationListener} removes it. The lifetime extender is in charge of performing the
|
||||
* callback when the notification is then safe to remove.
|
||||
*/
|
||||
public interface NotificationLifetimeExtender {
|
||||
|
||||
/**
|
||||
* Set the handler to callback to when the notification is safe to remove.
|
||||
*
|
||||
* @param callback the handler to callback
|
||||
*/
|
||||
void setCallback(@NonNull NotificationSafeToRemoveCallback callback);
|
||||
|
||||
/**
|
||||
* Determines whether or not the extender needs the notification kept after removal.
|
||||
*
|
||||
* @param entry the entry containing the notification to check
|
||||
* @return true if the notification lifetime should be extended
|
||||
*/
|
||||
boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry);
|
||||
|
||||
/**
|
||||
* It's possible that a notification was canceled before it ever became visible. This callback
|
||||
* gives lifetime extenders a chance to make sure it shows up. For example if a foreground
|
||||
* service is canceled too quickly but we still want to make sure a FGS notification shows.
|
||||
* @param pendingEntry the canceled (but pending) entry
|
||||
* @return true if the notification lifetime should be extended
|
||||
*/
|
||||
default boolean shouldExtendLifetimeForPendingNotification(
|
||||
@NonNull NotificationData.Entry pendingEntry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not the lifetime should be managed by the extender. In practice, if
|
||||
* shouldManage is true, this is where the extender starts managing the entry internally and is
|
||||
* now responsible for calling {@link NotificationSafeToRemoveCallback#onSafeToRemove(String)}
|
||||
* when the entry is safe to remove. If shouldManage is false, the extender no longer needs to
|
||||
* worry about it (either because we will be removing it anyway or the entry is no longer
|
||||
* removed due to an update).
|
||||
*
|
||||
* @param entry the entry that needs an extended lifetime
|
||||
* @param shouldManage true if the extender should manage the entry now, false otherwise
|
||||
*/
|
||||
void setShouldManageLifetime(@NonNull NotificationData.Entry entry, boolean shouldManage);
|
||||
|
||||
/**
|
||||
* The callback for when the notification is now safe to remove (i.e. its lifetime has ended).
|
||||
*/
|
||||
interface NotificationSafeToRemoveCallback {
|
||||
/**
|
||||
* Called when the lifetime extender determines it's safe to remove.
|
||||
*
|
||||
* @param key key of the entry that is now safe to remove
|
||||
*/
|
||||
void onSafeToRemove(String key);
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,7 @@ import android.service.vr.IVrManager;
|
||||
import android.service.vr.IVrStateCallbacks;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
@@ -134,6 +135,7 @@ import android.widget.RemoteViews;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
||||
@@ -191,6 +193,7 @@ import com.android.systemui.statusbar.DismissView;
|
||||
import com.android.systemui.statusbar.DragDownHelper;
|
||||
import com.android.systemui.statusbar.EmptyShadeView;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.ForegroundServiceLifetimeExtender;
|
||||
import com.android.systemui.statusbar.GestureRecorder;
|
||||
import com.android.systemui.statusbar.KeyboardShortcuts;
|
||||
import com.android.systemui.statusbar.KeyguardIndicationController;
|
||||
@@ -198,6 +201,7 @@ import com.android.systemui.statusbar.NotificationData;
|
||||
import com.android.systemui.statusbar.NotificationData.Entry;
|
||||
import com.android.systemui.statusbar.NotificationGuts;
|
||||
import com.android.systemui.statusbar.NotificationInfo;
|
||||
import com.android.systemui.statusbar.NotificationLifetimeExtender;
|
||||
import com.android.systemui.statusbar.NotificationShelf;
|
||||
import com.android.systemui.statusbar.NotificationSnooze;
|
||||
import com.android.systemui.statusbar.RemoteInputController;
|
||||
@@ -715,7 +719,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
private ConfigurationListener mConfigurationListener;
|
||||
private boolean mReinflateNotificationsOnUserSwitched;
|
||||
private HashMap<String, Entry> mPendingNotifications = new HashMap<>();
|
||||
private ForegroundServiceController mForegroundServiceController;
|
||||
protected ForegroundServiceController mForegroundServiceController;
|
||||
|
||||
private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
|
||||
final int N = array.size();
|
||||
@@ -760,6 +764,9 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
|
||||
|
||||
mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
|
||||
mFGSExtender = new ForegroundServiceLifetimeExtender();
|
||||
mFGSExtender.setCallback(key -> removeNotification(key, mLatestRankingMap));
|
||||
|
||||
|
||||
mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||
mDisplay = mWindowManager.getDefaultDisplay();
|
||||
@@ -1733,6 +1740,11 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
}
|
||||
Entry entry = mNotificationData.get(key);
|
||||
|
||||
if (entry != null && mFGSExtender.shouldExtendLifetime(entry)) {
|
||||
extendLifetime(entry, mFGSExtender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
|
||||
&& (entry.row != null && !entry.row.isDismissed())) {
|
||||
mLatestRankingMap = ranking;
|
||||
@@ -1763,9 +1775,35 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make sure no lifetime extension is happening anymore
|
||||
cancelLifetimeExtension(entry);
|
||||
setAreThereNotifications();
|
||||
}
|
||||
|
||||
/** Lifetime extension keeps entries around after they would've otherwise been canceled */
|
||||
private void extendLifetime(Entry entry, NotificationLifetimeExtender extender) {
|
||||
// Cancel any other extender which might be holding on to this notification entry
|
||||
NotificationLifetimeExtender activeExtender = mRetainedNotifications.get(entry);
|
||||
if (activeExtender != null && activeExtender != extender) {
|
||||
activeExtender.setShouldManageLifetime(entry, false);
|
||||
}
|
||||
mRetainedNotifications.put(entry, extender);
|
||||
extender.setShouldManageLifetime(entry, true);
|
||||
}
|
||||
|
||||
/** Tells the current extender (if any) to stop extending the entry's lifetime */
|
||||
private void cancelLifetimeExtension(NotificationData.Entry entry) {
|
||||
NotificationLifetimeExtender activeExtender = mRetainedNotifications.remove(entry);
|
||||
if (activeExtender != null) {
|
||||
activeExtender.setShouldManageLifetime(entry, false);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Map<Entry, NotificationLifetimeExtender> getRetainedNotificationMap() {
|
||||
return mRetainedNotifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the group children are cancelled immediately when the group summary is cancelled
|
||||
* instead of waiting for the notification manager to send all cancels. Otherwise this could
|
||||
@@ -3369,6 +3407,17 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
}
|
||||
}
|
||||
|
||||
pw.println(" Lifetime-extended notifications:");
|
||||
if (mRetainedNotifications.isEmpty()) {
|
||||
pw.println(" None");
|
||||
} else {
|
||||
for (Map.Entry<NotificationData.Entry, NotificationLifetimeExtender> entry
|
||||
: mRetainedNotifications.entrySet()) {
|
||||
pw.println(" " + entry.getKey().notification + " retained by "
|
||||
+ entry.getValue().getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
pw.print(" mInteractingWindows="); pw.println(mInteractingWindows);
|
||||
pw.print(" mStatusBarWindowState=");
|
||||
pw.println(windowStateToString(mStatusBarWindowState));
|
||||
@@ -5212,6 +5261,11 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
|
||||
protected RemoteInputController mRemoteInputController;
|
||||
|
||||
// A lifetime extender that watches for foreground service notifications
|
||||
@VisibleForTesting protected NotificationLifetimeExtender mFGSExtender;
|
||||
private final Map<Entry, NotificationLifetimeExtender> mRetainedNotifications =
|
||||
new ArrayMap<>();
|
||||
|
||||
// for heads up notifications
|
||||
protected HeadsUpManager mHeadsUpManager;
|
||||
|
||||
@@ -6825,6 +6879,9 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
|
||||
}
|
||||
|
||||
// No need to keep the lifetime extension around if an update comes in for it
|
||||
cancelLifetimeExtension(entry);
|
||||
|
||||
Notification n = notification.getNotification();
|
||||
mNotificationData.updateRanking(ranking);
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import static com.android.systemui.statusbar.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.service.notification.NotificationListenerService.Ranking;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.statusbar.NotificationData.Entry;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ForegroundServiceLifetimeExtenderTest extends SysuiTestCase {
|
||||
private ForegroundServiceLifetimeExtender mExtender = new ForegroundServiceLifetimeExtender();
|
||||
private StatusBarNotification mSbn;
|
||||
private NotificationData.Entry mEntry;
|
||||
private Notification mNotif;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mNotif = new Notification.Builder(mContext, "")
|
||||
.setSmallIcon(R.drawable.ic_person)
|
||||
.setContentTitle("Title")
|
||||
.setContentText("Text")
|
||||
.build();
|
||||
mSbn = mock(StatusBarNotification.class);
|
||||
when(mSbn.getNotification()).thenReturn(mNotif);
|
||||
mEntry = new NotificationData.Entry(mSbn);
|
||||
}
|
||||
|
||||
/**
|
||||
* ForegroundServiceLifetimeExtenderTest
|
||||
*/
|
||||
@Test
|
||||
public void testShouldExtendLifetime_should_foreground() {
|
||||
// Extend the lifetime of a FGS notification iff it has not been visible
|
||||
// for the minimum time
|
||||
mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
|
||||
when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis());
|
||||
assertTrue(mExtender.shouldExtendLifetime(mEntry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldExtendLifetime_shouldNot_foreground() {
|
||||
mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
|
||||
when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
|
||||
assertFalse(mExtender.shouldExtendLifetime(mEntry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldExtendLifetime_shouldNot_notForeground() {
|
||||
mNotif.flags = 0;
|
||||
when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
|
||||
assertFalse(mExtender.shouldExtendLifetime(mEntry));
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_HIGH;
|
||||
|
||||
import static com.android.systemui.statusbar.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static junit.framework.TestCase.fail;
|
||||
@@ -37,6 +39,7 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Notification;
|
||||
import android.metrics.LogMaker;
|
||||
import android.os.Handler;
|
||||
@@ -47,6 +50,7 @@ import android.os.PowerManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.service.notification.NotificationListenerService.RankingMap;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.metricshelper.MetricsAsserts;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
@@ -61,6 +65,11 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.logging.testing.FakeMetricsLogger;
|
||||
import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.keyguard.KeyguardHostView.OnDismissAction;
|
||||
import com.android.systemui.ForegroundServiceController;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.ForegroundServiceLifetimeExtender;
|
||||
import com.android.systemui.statusbar.NotificationLifetimeExtender;
|
||||
import com.android.systemui.statusbar.RemoteInputController;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.recents.misc.SystemServicesProxy;
|
||||
import com.android.systemui.statusbar.ActivatableNotificationView;
|
||||
@@ -75,13 +84,19 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
public class StatusBarTest extends SysuiTestCase {
|
||||
|
||||
private static final String TEST_PACKAGE_NAME = "test";
|
||||
private static final int TEST_UID = 123;
|
||||
|
||||
StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
|
||||
UnlockMethodCache mUnlockMethodCache;
|
||||
KeyguardIndicationController mKeyguardIndicationController;
|
||||
@@ -94,8 +109,12 @@ public class StatusBarTest extends SysuiTestCase {
|
||||
SystemServicesProxy mSystemServicesProxy;
|
||||
NotificationPanelView mNotificationPanelView;
|
||||
IStatusBarService mBarService;
|
||||
RemoteInputController mRemoteInputController;
|
||||
ForegroundServiceController mForegroundServiceController;
|
||||
ArrayList<Entry> mNotificationList;
|
||||
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
|
||||
private ForegroundServiceLifetimeExtender mFGSExtender =
|
||||
new ForegroundServiceLifetimeExtender();
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
@@ -110,6 +129,8 @@ public class StatusBarTest extends SysuiTestCase {
|
||||
mNotificationPanelView = mock(NotificationPanelView.class);
|
||||
mNotificationList = mock(ArrayList.class);
|
||||
IPowerManager powerManagerService = mock(IPowerManager.class);
|
||||
mRemoteInputController = mock(RemoteInputController.class);
|
||||
mForegroundServiceController = mock(ForegroundServiceController.class);
|
||||
HandlerThread handlerThread = new HandlerThread("TestThread");
|
||||
handlerThread.start();
|
||||
mPowerManager = new PowerManager(mContext, powerManagerService,
|
||||
@@ -118,10 +139,11 @@ public class StatusBarTest extends SysuiTestCase {
|
||||
mBarService = mock(IStatusBarService.class);
|
||||
|
||||
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
|
||||
mFGSExtender.setCallback(key -> mStatusBar.removeNotification(key, mock(RankingMap.class)));
|
||||
mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
|
||||
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
|
||||
mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView,
|
||||
mBarService);
|
||||
mBarService, mFGSExtender, mRemoteInputController, mForegroundServiceController);
|
||||
doAnswer(invocation -> {
|
||||
OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
|
||||
onDismissAction.onDismiss();
|
||||
@@ -317,6 +339,53 @@ public class StatusBarTest extends SysuiTestCase {
|
||||
assertFalse(mStatusBar.shouldPeek(entry, sbn));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForegroundServiceNotificationKeptForFiveSeconds() throws Exception {
|
||||
RankingMap rm = mock(RankingMap.class);
|
||||
|
||||
// sbn posted "just now"
|
||||
Notification n = new Notification.Builder(mContext, "")
|
||||
.setSmallIcon(R.drawable.ic_person)
|
||||
.setContentTitle("Title")
|
||||
.setContentText("Text")
|
||||
.build();
|
||||
n.flags |= Notification.FLAG_FOREGROUND_SERVICE;
|
||||
StatusBarNotification sbn =
|
||||
new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
|
||||
0, n, new UserHandle(ActivityManager.getCurrentUser()), null,
|
||||
System.currentTimeMillis());
|
||||
NotificationData.Entry entry = new NotificationData.Entry(sbn);
|
||||
when(mNotificationData.get(any())).thenReturn(entry);
|
||||
mStatusBar.removeNotification(sbn.getKey(), rm);
|
||||
Map<NotificationData.Entry, NotificationLifetimeExtender> map =
|
||||
mStatusBar.getRetainedNotificationMap();
|
||||
Assert.assertTrue(map.containsKey(entry));
|
||||
}
|
||||
@Test
|
||||
public void testForegroundServiceNotification_notRetainedIfShownForFiveSeconds()
|
||||
throws Exception {
|
||||
|
||||
RankingMap rm = mock(RankingMap.class);
|
||||
|
||||
// sbn posted "more than 5 seconds ago"
|
||||
Notification n = new Notification.Builder(mContext, "")
|
||||
.setSmallIcon(R.drawable.ic_person)
|
||||
.setContentTitle("Title")
|
||||
.setContentText("Text")
|
||||
.build();
|
||||
n.flags |= Notification.FLAG_FOREGROUND_SERVICE;
|
||||
StatusBarNotification sbn =
|
||||
new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
|
||||
0, n, new UserHandle(ActivityManager.getCurrentUser()), null,
|
||||
System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
|
||||
NotificationData.Entry entry = new NotificationData.Entry(sbn);
|
||||
when(mNotificationData.get(any())).thenReturn(entry);
|
||||
mStatusBar.removeNotification(sbn.getKey(), rm);
|
||||
Map<NotificationData.Entry, NotificationLifetimeExtender> map =
|
||||
mStatusBar.getRetainedNotificationMap();
|
||||
Assert.assertFalse(map.containsKey(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogHidden() {
|
||||
try {
|
||||
@@ -390,7 +459,8 @@ public class StatusBarTest extends SysuiTestCase {
|
||||
UnlockMethodCache unlock, KeyguardIndicationController key,
|
||||
NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd,
|
||||
PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView,
|
||||
IStatusBarService barService) {
|
||||
IStatusBarService barService, ForegroundServiceLifetimeExtender fgsExtender,
|
||||
RemoteInputController ric, ForegroundServiceController fsc) {
|
||||
mStatusBarKeyguardViewManager = man;
|
||||
mUnlockMethodCache = unlock;
|
||||
mKeyguardIndicationController = key;
|
||||
@@ -402,10 +472,13 @@ public class StatusBarTest extends SysuiTestCase {
|
||||
mSystemServicesProxy = ssp;
|
||||
mNotificationPanel = panelView;
|
||||
mBarService = barService;
|
||||
mFGSExtender = fgsExtender;
|
||||
mRemoteInputController = ric;
|
||||
mForegroundServiceController = fsc;
|
||||
}
|
||||
|
||||
public void setBarStateForTest(int state) {
|
||||
mState = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4385,8 +4385,15 @@ public class NotificationManagerService extends SystemService {
|
||||
userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
|
||||
|
||||
synchronized (mNotificationLock) {
|
||||
// Look for the notification, searching both the posted and enqueued lists.
|
||||
NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
|
||||
// If the notification is currently enqueued, repost this runnable so it has a
|
||||
// chance to notify listeners
|
||||
if ((findNotificationByListLocked(
|
||||
mEnqueuedNotifications, pkg, tag, id, userId)) != null) {
|
||||
mHandler.post(this);
|
||||
}
|
||||
// Look for the notification in the posted list, since we already checked enq
|
||||
NotificationRecord r = findNotificationByListLocked(
|
||||
mNotificationList, pkg, tag, id, userId);
|
||||
if (r != null) {
|
||||
// The notification was found, check if it should be removed.
|
||||
|
||||
|
||||
@@ -319,6 +319,21 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
|
||||
assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCancelImmediatelyAfterEnqueueNotifiesListeners_ForegroundServiceFlag()
|
||||
throws Exception {
|
||||
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
|
||||
sbn.getNotification().flags =
|
||||
Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE;
|
||||
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
|
||||
sbn.getId(), sbn.getNotification(), sbn.getUserId());
|
||||
mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
|
||||
waitForIdle();
|
||||
verify(mNotificationListeners, times(1)).notifyPostedLocked(any(), any());
|
||||
verify(mNotificationListeners, times(1)).notifyRemovedLocked(any(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelNotificationWhilePostedAndEnqueued() throws Exception {
|
||||
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
|
||||
|
||||
Reference in New Issue
Block a user