From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jan Tomljanovic Date: Fri, 6 Nov 2020 11:28:09 +0000 Subject: [PATCH] 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 --- .../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 32873445b5e6..11c89bbdf4d9 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -493,6 +493,10 @@ public class NotificationManagerService extends SystemService { private KeyguardManager mKeyguardManager; + // 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<>(); @@ -7444,10 +7448,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); @@ -7463,6 +7472,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 9ed2b17de423..3a108144ad66 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -5341,6 +5341,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"; @@ -5377,6 +5403,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 testTextToastsCallStatusBar() throws Exception { final String testPackage = "testPackageName";