1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18 package com.android.systemui.statusbar; 19 20 import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer; 21 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static junit.framework.Assert.assertFalse; 26 import static junit.framework.Assert.assertTrue; 27 28 import static org.junit.Assert.assertEquals; 29 import static org.mockito.Mockito.spy; 30 31 import android.app.ActivityManager; 32 import android.app.Notification; 33 import android.os.Handler; 34 import android.os.Looper; 35 import android.os.UserHandle; 36 import android.service.notification.StatusBarNotification; 37 import android.testing.AndroidTestingRunner; 38 import android.testing.TestableLooper; 39 40 import androidx.test.filters.SmallTest; 41 42 import com.android.systemui.R; 43 import com.android.systemui.SysuiTestCase; 44 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 45 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; 46 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 47 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; 48 49 import org.junit.After; 50 import org.junit.Before; 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 import org.mockito.Mock; 55 import org.mockito.junit.MockitoJUnit; 56 import org.mockito.junit.MockitoRule; 57 58 @SmallTest 59 @RunWith(AndroidTestingRunner.class) 60 @TestableLooper.RunWithLooper 61 public class AlertingNotificationManagerTest extends SysuiTestCase { 62 @Rule 63 public MockitoRule rule = MockitoJUnit.rule(); 64 65 private static final String TEST_PACKAGE_NAME = "test"; 66 private static final int TEST_UID = 0; 67 68 protected static final int TEST_MINIMUM_DISPLAY_TIME = 400; 69 protected static final int TEST_AUTO_DISMISS_TIME = 600; 70 protected static final int TEST_STICKY_AUTO_DISMISS_TIME = 800; 71 // Number of notifications to use in tests requiring multiple notifications 72 private static final int TEST_NUM_NOTIFICATIONS = 4; 73 protected static final int TEST_TIMEOUT_TIME = 2_000; 74 protected final Runnable mTestTimeoutRunnable = () -> mTimedOut = true; 75 76 protected Handler mTestHandler; 77 protected boolean mTimedOut = false; 78 79 @Mock protected ExpandableNotificationRow mRow; 80 81 private static class TestableAlertingNotificationManager extends AlertingNotificationManager { 82 private AlertEntry mLastCreatedEntry; 83 TestableAlertingNotificationManager(Handler handler)84 private TestableAlertingNotificationManager(Handler handler) { 85 super(new HeadsUpManagerLogger(logcatLogBuffer()), handler); 86 mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME; 87 mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME; 88 mStickyDisplayTime = TEST_STICKY_AUTO_DISMISS_TIME; 89 } 90 91 @Override onAlertEntryAdded(AlertEntry alertEntry)92 protected void onAlertEntryAdded(AlertEntry alertEntry) {} 93 94 @Override onAlertEntryRemoved(AlertEntry alertEntry)95 protected void onAlertEntryRemoved(AlertEntry alertEntry) {} 96 97 @Override createAlertEntry()98 protected AlertEntry createAlertEntry() { 99 mLastCreatedEntry = spy(super.createAlertEntry()); 100 return mLastCreatedEntry; 101 } 102 103 @Override getContentFlag()104 public int getContentFlag() { 105 return FLAG_CONTENT_VIEW_CONTRACTED; 106 } 107 } 108 createAlertingNotificationManager()109 protected AlertingNotificationManager createAlertingNotificationManager() { 110 return new TestableAlertingNotificationManager(mTestHandler); 111 } 112 createSbn(int id, Notification n)113 protected StatusBarNotification createSbn(int id, Notification n) { 114 return new StatusBarNotification( 115 TEST_PACKAGE_NAME /* pkg */, 116 TEST_PACKAGE_NAME, 117 id, 118 null /* tag */, 119 TEST_UID, 120 0 /* initialPid */, 121 n, 122 new UserHandle(ActivityManager.getCurrentUser()), 123 null /* overrideGroupKey */, 124 0 /* postTime */); 125 } 126 createSbn(int id, Notification.Builder n)127 protected StatusBarNotification createSbn(int id, Notification.Builder n) { 128 return createSbn(id, n.build()); 129 } 130 createSbn(int id)131 protected StatusBarNotification createSbn(int id) { 132 final Notification.Builder b = new Notification.Builder(mContext, "") 133 .setSmallIcon(R.drawable.ic_person) 134 .setContentTitle("Title") 135 .setContentText("Text"); 136 return createSbn(id, b); 137 } 138 createEntry(int id, Notification n)139 protected NotificationEntry createEntry(int id, Notification n) { 140 return new NotificationEntryBuilder().setSbn(createSbn(id, n)).build(); 141 } 142 createEntry(int id)143 protected NotificationEntry createEntry(int id) { 144 return new NotificationEntryBuilder().setSbn(createSbn(id)).build(); 145 } 146 verifyAlertingAtTime(AlertingNotificationManager anm, NotificationEntry entry, boolean shouldBeAlerting, int whenToCheckAlertingMillis, String whenCondition)147 protected void verifyAlertingAtTime(AlertingNotificationManager anm, NotificationEntry entry, 148 boolean shouldBeAlerting, int whenToCheckAlertingMillis, String whenCondition) { 149 final Boolean[] wasAlerting = {null}; 150 final Runnable checkAlerting = 151 () -> wasAlerting[0] = anm.isAlerting(entry.getKey()); 152 153 mTestHandler.postDelayed(checkAlerting, whenToCheckAlertingMillis); 154 mTestHandler.postDelayed(mTestTimeoutRunnable, TEST_TIMEOUT_TIME); 155 TestableLooper.get(this).processMessages(2); 156 157 assertFalse("Test timed out", mTimedOut); 158 if (shouldBeAlerting) { 159 assertTrue("Should still be alerting after " + whenCondition, wasAlerting[0]); 160 } else { 161 assertFalse("Should not still be alerting after " + whenCondition, wasAlerting[0]); 162 } 163 assertFalse("Should not still be alerting after processing", 164 anm.isAlerting(entry.getKey())); 165 } 166 167 @Before setUp()168 public void setUp() { 169 mTestHandler = Handler.createAsync(Looper.myLooper()); 170 171 assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME); 172 assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME); 173 assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_TIMEOUT_TIME); 174 } 175 176 @After tearDown()177 public void tearDown() { 178 mTestHandler.removeCallbacksAndMessages(null); 179 } 180 181 @Test testShowNotification_addsEntry()182 public void testShowNotification_addsEntry() { 183 final AlertingNotificationManager alm = createAlertingNotificationManager(); 184 final NotificationEntry entry = createEntry(/* id = */ 0); 185 186 alm.showNotification(entry); 187 188 assertTrue(alm.isAlerting(entry.getKey())); 189 assertTrue(alm.hasNotifications()); 190 assertEquals(entry, alm.getEntry(entry.getKey())); 191 } 192 193 @Test testShowNotification_autoDismisses()194 public void testShowNotification_autoDismisses() { 195 final AlertingNotificationManager alm = createAlertingNotificationManager(); 196 final NotificationEntry entry = createEntry(/* id = */ 0); 197 198 alm.showNotification(entry); 199 200 verifyAlertingAtTime(alm, entry, false, TEST_AUTO_DISMISS_TIME * 3 / 2, 201 "auto dismiss time"); 202 203 assertFalse(alm.isAlerting(entry.getKey())); 204 } 205 206 @Test testRemoveNotification_removeDeferred()207 public void testRemoveNotification_removeDeferred() { 208 final AlertingNotificationManager alm = createAlertingNotificationManager(); 209 final NotificationEntry entry = createEntry(/* id = */ 0); 210 211 alm.showNotification(entry); 212 213 // Try to remove but defer, since the notification has not been shown long enough. 214 final boolean removedImmediately = alm.removeNotification(entry.getKey(), 215 false /* releaseImmediately */); 216 217 assertFalse(removedImmediately); 218 assertTrue(alm.isAlerting(entry.getKey())); 219 } 220 221 @Test testRemoveNotification_forceRemove()222 public void testRemoveNotification_forceRemove() { 223 final AlertingNotificationManager alm = createAlertingNotificationManager(); 224 final NotificationEntry entry = createEntry(/* id = */ 0); 225 226 alm.showNotification(entry); 227 228 // Remove forcibly with releaseImmediately = true. 229 final boolean removedImmediately = alm.removeNotification(entry.getKey(), 230 true /* releaseImmediately */); 231 232 assertTrue(removedImmediately); 233 assertFalse(alm.isAlerting(entry.getKey())); 234 } 235 236 @Test testReleaseAllImmediately()237 public void testReleaseAllImmediately() { 238 final AlertingNotificationManager alm = createAlertingNotificationManager(); 239 for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) { 240 final NotificationEntry entry = createEntry(i); 241 entry.setRow(mRow); 242 alm.showNotification(entry); 243 } 244 245 alm.releaseAllImmediately(); 246 247 assertEquals(0, alm.getAllEntries().count()); 248 } 249 250 @Test testCanRemoveImmediately_notShownLongEnough()251 public void testCanRemoveImmediately_notShownLongEnough() { 252 final AlertingNotificationManager alm = createAlertingNotificationManager(); 253 final NotificationEntry entry = createEntry(/* id = */ 0); 254 255 alm.showNotification(entry); 256 257 // The entry has just been added so we should not remove immediately. 258 assertFalse(alm.canRemoveImmediately(entry.getKey())); 259 } 260 } 261