From ea90e9501b7925338f3e61fa8ea1b5d0902a53bb Mon Sep 17 00:00:00 2001 From: Jan Tomljanovic Date: Fri, 6 Nov 2020 11:28:09 +0000 Subject: Don't try to show the current toast again while it's showing. By doing this we avoid a few bad things: - mechanism that hides the current toast by trying to show it again - delaying the call to hide and remove the current toast from the queue when it's duration expires (which in the case of repeated calls can delay this indefinitely) Test: atest NotificationManagerServiceTest Test: atest android.widget.cts.ToastTest Bug: 167672740 Change-Id: Ie4953109314113efae49fa0c5e0c236e6e0dbb23 --- .../notification/NotificationManagerService.java | 13 ++++++ .../NotificationManagerServiceTest.java | 53 ++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 1516cded50a6..1e2898b91a27 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -494,6 +494,10 @@ public class NotificationManagerService extends SystemService { final ArrayList mToastQueue = new ArrayList<>(); final ArrayMap mSummaryByGroupKey = new ArrayMap<>(); + // True if the toast that's on top of the queue is being shown at the moment. + @GuardedBy("mToastQueue") + private boolean mIsCurrentToastShown = false; + // The last key in this list owns the hardware. ArrayList mLights = new ArrayList<>(); @@ -7297,10 +7301,15 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mToastQueue") void showNextToastLocked() { + if (mIsCurrentToastShown) { + return; // Don't show the same toast twice. + } + ToastRecord record = mToastQueue.get(0); while (record != null) { if (record.show()) { scheduleDurationReachedLocked(record); + mIsCurrentToastShown = true; return; } int index = mToastQueue.indexOf(record); @@ -7316,6 +7325,10 @@ public class NotificationManagerService extends SystemService { ToastRecord record = mToastQueue.get(index); record.hide(); + if (index == 0) { + mIsCurrentToastShown = false; + } + ToastRecord lastToast = mToastQueue.remove(index); mWindowManagerInternal.removeWindowToken(lastToast.windowToken, false /* removeWindows */, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 87aaba2b164a..70d3f988f1f7 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4922,6 +4922,32 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(0, mService.mToastQueue.size()); } + @Test + public void testDontCallShowToastAgainOnTheSameCustomToast() throws Exception { + final String testPackage = "testPackageName"; + assertEquals(0, mService.mToastQueue.size()); + mService.isSystemUid = false; + + // package is not suspended + when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) + .thenReturn(false); + + setAppInForegroundForToasts(mUid, true); + + Binder token = new Binder(); + ITransientNotification callback = mock(ITransientNotification.class); + INotificationManager nmService = (INotificationManager) mService.mService; + + // first time trying to show the toast, showToast gets called + nmService.enqueueToast(testPackage, token, callback, 2000, 0); + verify(callback, times(1)).show(any()); + + // second time trying to show the same toast, showToast isn't called again (total number of + // invocations stays at one) + nmService.enqueueToast(testPackage, token, callback, 2000, 0); + verify(callback, times(1)).show(any()); + } + @Test public void testAllowForegroundTextToasts() throws Exception { final String testPackage = "testPackageName"; @@ -4958,6 +4984,33 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(1, mService.mToastQueue.size()); } + @Test + public void testDontCallShowToastAgainOnTheSameTextToast() throws Exception { + final String testPackage = "testPackageName"; + assertEquals(0, mService.mToastQueue.size()); + mService.isSystemUid = false; + + // package is not suspended + when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) + .thenReturn(false); + + setAppInForegroundForToasts(mUid, true); + + Binder token = new Binder(); + INotificationManager nmService = (INotificationManager) mService.mService; + + // first time trying to show the toast, showToast gets called + nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null); + verify(mStatusBar, times(1)) + .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any()); + + // second time trying to show the same toast, showToast isn't called again (total number of + // invocations stays at one) + nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null); + verify(mStatusBar, times(1)) + .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any()); + } + @Test public void backgroundSystemCustomToast_callsSetProcessImportantAsForegroundForToast() throws Exception { -- cgit v1.2.3