Loading services/core/java/com/android/server/notification/NotificationManagerService.java +14 −2 Original line number Original line Diff line number Diff line Loading @@ -140,6 +140,7 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.contentprotection.flags.Flags.rapidClearNotificationsByListenerAppOpEnabled; import static android.view.contentprotection.flags.Flags.rapidClearNotificationsByListenerAppOpEnabled; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; Loading Loading @@ -307,6 +308,7 @@ import android.view.Display; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager; import android.widget.RemoteViews; import android.widget.RemoteViews; import android.widget.Toast; import android.widget.Toast; import com.android.internal.R; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -358,7 +360,9 @@ import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.BackgroundActivityStartCallback; import com.android.server.wm.BackgroundActivityStartCallback; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal; import libcore.io.IoUtils; import libcore.io.IoUtils; import org.json.JSONException; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -11776,10 +11780,18 @@ public class NotificationManagerService extends SystemService { } } if (lifetimeExtensionRefactor()) { if (lifetimeExtensionRefactor()) { if (sendRedacted && redactedSbn == null) { redactedSbn = redactStatusBarNotification(sbn); redactedCache = new TrimCache(redactedSbn); } final StatusBarNotification sbnToPost = sendRedacted ? redactedCache.ForListener(info) : trimCache.ForListener(info); // Checks if this is a request to notify system UI about a notification that // Checks if this is a request to notify system UI about a notification that // has been lifetime extended. // has been lifetime extended. // (We only need to check old for the flag, because in both cancellation and // (We only need to check old for the flag, because in both cancellation and // update cases, old should have the flag.) // update cases, old should have the flag, whereas in update cases the // new will NOT have the flag.) // If it is such a request, and this is system UI, we send the post request // If it is such a request, and this is system UI, we send the post request // only to System UI, and break as we don't need to continue checking other // only to System UI, and break as we don't need to continue checking other // Managed Services. // Managed Services. Loading @@ -11787,7 +11799,7 @@ public class NotificationManagerService extends SystemService { && (old.getNotification().flags && (old.getNotification().flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) { & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) { final NotificationRankingUpdate update = makeRankingUpdateLocked(info); final NotificationRankingUpdate update = makeRankingUpdateLocked(info); listenerCalls.add(() -> notifyPosted(info, oldSbn, update)); listenerCalls.add(() -> notifyPosted(info, sbnToPost, update)); break; break; } } } } services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +69 −0 Original line number Original line Diff line number Diff line Loading @@ -70,6 +70,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserHandle; import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.INotificationListener; import android.service.notification.INotificationListener; import android.service.notification.IStatusBarNotificationHolder; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationRankingUpdate; import android.service.notification.NotificationRankingUpdate; Loading @@ -90,6 +91,7 @@ import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Before; import org.junit.Rule; import org.junit.Rule; import org.junit.Test; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations; Loading Loading @@ -154,6 +156,11 @@ public class NotificationListenersTest extends UiServiceTestCase { .thenReturn(new ArrayList<>()); .thenReturn(new ArrayList<>()); mNm.mHandler = mock(NotificationManagerService.WorkerHandler.class); mNm.mHandler = mock(NotificationManagerService.WorkerHandler.class); mNm.mAssistants = mock(NotificationManagerService.NotificationAssistants.class); mNm.mAssistants = mock(NotificationManagerService.NotificationAssistants.class); FieldSetter.setField(mNm, NotificationManagerService.class.getDeclaredField("mListeners"), mListeners); doReturn(android.service.notification.NotificationListenerService.TRIM_FULL) .when(mListeners).getOnNotificationPostedTrim(any()); } } @Test @Test Loading Loading @@ -827,6 +834,68 @@ public class NotificationListenersTest extends UiServiceTestCase { verify(mListeners, never()).redactStatusBarNotification(eq(sbn)); verify(mListeners, never()).redactStatusBarNotification(eq(sbn)); } } @Test public void testListenerPost_UpdateLifetimeExtended() throws Exception { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR); // Create original notification, with FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY. String pkg = "pkg"; int uid = 9; UserHandle userHandle = UserHandle.getUserHandleForUid(uid); NotificationChannel channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH); Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon) .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true); StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0, nb.build(), userHandle, null, 0); NotificationRecord old = new NotificationRecord(mContext, sbn, channel); // Creates updated notification (without FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) Notification.Builder nb2 = new Notification.Builder(mContext, channel.getId()) .setContentTitle("new title") .setSmallIcon(android.R.drawable.sym_def_app_icon) .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, false); StatusBarNotification sbn2 = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0, nb2.build(), userHandle, null, 0); NotificationRecord toPost = new NotificationRecord(mContext, sbn2, channel); // Create system ui-like service. ManagedServices.ManagedServiceInfo info = mListeners.new ManagedServiceInfo( null, new ComponentName("a", "a"), sbn2.getUserId(), false, null, 33, 33); info.isSystemUi = true; INotificationListener l1 = mock(INotificationListener.class); info.service = l1; List<ManagedServices.ManagedServiceInfo> services = ImmutableList.of(info); when(mListeners.getServices()).thenReturn(services); FieldSetter.setField(mNm, NotificationManagerService.class.getDeclaredField("mHandler"), mock(NotificationManagerService.WorkerHandler.class)); doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any()); doReturn(mock(NotificationRankingUpdate.class)).when(mNm).makeRankingUpdateLocked(info); doReturn(false).when(mNm).isInLockDownMode(anyInt()); doNothing().when(mNm).updateUriPermissions(any(), any(), any(), anyInt()); doReturn(sbn2).when(mListeners).redactStatusBarNotification(sbn2); doReturn(sbn2).when(mListeners).redactStatusBarNotification(any()); // The notification change is posted to the service listener. mListeners.notifyPostedLocked(toPost, old); // Verify that the post occcurs with the updated notification value. ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); verify(mNm.mHandler, times(1)).post(runnableCaptor.capture()); runnableCaptor.getValue().run(); ArgumentCaptor<IStatusBarNotificationHolder> sbnCaptor = ArgumentCaptor.forClass(IStatusBarNotificationHolder.class); verify(l1, times(1)).onNotificationPosted(sbnCaptor.capture(), any()); StatusBarNotification sbnResult = sbnCaptor.getValue().get(); assertThat(sbnResult.getNotification() .extras.getCharSequence(Notification.EXTRA_TITLE).toString()) .isEqualTo("new title"); } /** /** * Helper method to test the thread safety of some operations. * Helper method to test the thread safety of some operations. * * Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +50 −3 Original line number Original line Diff line number Diff line Loading @@ -106,6 +106,7 @@ import static android.service.notification.NotificationListenerService.Ranking.U import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER; Loading @@ -118,11 +119,11 @@ import static com.android.server.notification.NotificationManagerService.TAG; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth.assertWithMessage; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNotNull; Loading @@ -131,6 +132,7 @@ import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; import static junit.framework.Assert.fail; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.isNull; Loading @@ -157,6 +159,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import android.Manifest; import android.Manifest; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SuppressLint; Loading Loading @@ -270,8 +275,10 @@ import android.util.Pair; import android.util.Xml; import android.util.Xml; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager; import android.widget.RemoteViews; import android.widget.RemoteViews; import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.R; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.config.sysui.TestableFlagResolver; import com.android.internal.config.sysui.TestableFlagResolver; Loading Loading @@ -303,10 +310,12 @@ import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.google.android.collect.Lists; import com.google.android.collect.Lists; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import org.junit.After; import org.junit.After; import org.junit.Assert; import org.junit.Assert; import org.junit.Before; import org.junit.Before; Loading Loading @@ -5933,6 +5942,45 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertThat(captor.getValue().shouldPostSilently()).isTrue(); assertThat(captor.getValue().shouldPostSilently()).isTrue(); } } @Test @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR) public void testUpdate_DirectReplyLifetimeExtendedUpdateSucceeds() throws Exception { // Creates a lifetime extended notification. NotificationRecord original = generateNotificationRecord(mTestNotificationChannel); original.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY; mService.addNotification(original); // Post an update for that notification. StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, original.getSbn().getId(), original.getSbn().getTag(), mUid, 0, new Notification.Builder(mContext, mTestNotificationChannel.getId()) .setSmallIcon(android.R.drawable.sym_def_app_icon) .setContentTitle("new title").build(), UserHandle.getUserHandleForUid(mUid), null, 0); NotificationRecord update = new NotificationRecord(mContext, sbn, mTestNotificationChannel); mService.addEnqueuedNotification(update); NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(update.getKey(), update.getSbn().getPackageName(), update.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); // Checks the update was sent, and that update contains the new title, and does not contain // the lifetime extension flag. ArgumentCaptor<NotificationRecord> captor = ArgumentCaptor.forClass(NotificationRecord.class); verify(mListeners, times(1)).prepareNotifyPostedLocked(captor.capture(), any(), anyBoolean()); assertThat(captor.getValue().getNotification().flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0); assertThat(captor.getValue() .getNotification().extras.getCharSequence(Notification.EXTRA_TITLE).toString()) .isEqualTo("new title"); } @Test @Test public void testStats_updatedOnUserExpansion() throws Exception { public void testStats_updatedOnUserExpansion() throws Exception { NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); Loading Loading @@ -12602,7 +12650,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // the notifyPostedLocked function is called twice. // the notifyPostedLocked function is called twice. verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong()); verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong()); //verify(mListeners, times(2)).notifyPostedLocked(any(), any()); } } @Test @Test Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +14 −2 Original line number Original line Diff line number Diff line Loading @@ -140,6 +140,7 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.contentprotection.flags.Flags.rapidClearNotificationsByListenerAppOpEnabled; import static android.view.contentprotection.flags.Flags.rapidClearNotificationsByListenerAppOpEnabled; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; Loading Loading @@ -307,6 +308,7 @@ import android.view.Display; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager; import android.widget.RemoteViews; import android.widget.RemoteViews; import android.widget.Toast; import android.widget.Toast; import com.android.internal.R; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -358,7 +360,9 @@ import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.BackgroundActivityStartCallback; import com.android.server.wm.BackgroundActivityStartCallback; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal; import libcore.io.IoUtils; import libcore.io.IoUtils; import org.json.JSONException; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -11776,10 +11780,18 @@ public class NotificationManagerService extends SystemService { } } if (lifetimeExtensionRefactor()) { if (lifetimeExtensionRefactor()) { if (sendRedacted && redactedSbn == null) { redactedSbn = redactStatusBarNotification(sbn); redactedCache = new TrimCache(redactedSbn); } final StatusBarNotification sbnToPost = sendRedacted ? redactedCache.ForListener(info) : trimCache.ForListener(info); // Checks if this is a request to notify system UI about a notification that // Checks if this is a request to notify system UI about a notification that // has been lifetime extended. // has been lifetime extended. // (We only need to check old for the flag, because in both cancellation and // (We only need to check old for the flag, because in both cancellation and // update cases, old should have the flag.) // update cases, old should have the flag, whereas in update cases the // new will NOT have the flag.) // If it is such a request, and this is system UI, we send the post request // If it is such a request, and this is system UI, we send the post request // only to System UI, and break as we don't need to continue checking other // only to System UI, and break as we don't need to continue checking other // Managed Services. // Managed Services. Loading @@ -11787,7 +11799,7 @@ public class NotificationManagerService extends SystemService { && (old.getNotification().flags && (old.getNotification().flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) { & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) { final NotificationRankingUpdate update = makeRankingUpdateLocked(info); final NotificationRankingUpdate update = makeRankingUpdateLocked(info); listenerCalls.add(() -> notifyPosted(info, oldSbn, update)); listenerCalls.add(() -> notifyPosted(info, sbnToPost, update)); break; break; } } } }
services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +69 −0 Original line number Original line Diff line number Diff line Loading @@ -70,6 +70,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserHandle; import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.INotificationListener; import android.service.notification.INotificationListener; import android.service.notification.IStatusBarNotificationHolder; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationRankingUpdate; import android.service.notification.NotificationRankingUpdate; Loading @@ -90,6 +91,7 @@ import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Before; import org.junit.Rule; import org.junit.Rule; import org.junit.Test; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations; Loading Loading @@ -154,6 +156,11 @@ public class NotificationListenersTest extends UiServiceTestCase { .thenReturn(new ArrayList<>()); .thenReturn(new ArrayList<>()); mNm.mHandler = mock(NotificationManagerService.WorkerHandler.class); mNm.mHandler = mock(NotificationManagerService.WorkerHandler.class); mNm.mAssistants = mock(NotificationManagerService.NotificationAssistants.class); mNm.mAssistants = mock(NotificationManagerService.NotificationAssistants.class); FieldSetter.setField(mNm, NotificationManagerService.class.getDeclaredField("mListeners"), mListeners); doReturn(android.service.notification.NotificationListenerService.TRIM_FULL) .when(mListeners).getOnNotificationPostedTrim(any()); } } @Test @Test Loading Loading @@ -827,6 +834,68 @@ public class NotificationListenersTest extends UiServiceTestCase { verify(mListeners, never()).redactStatusBarNotification(eq(sbn)); verify(mListeners, never()).redactStatusBarNotification(eq(sbn)); } } @Test public void testListenerPost_UpdateLifetimeExtended() throws Exception { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR); // Create original notification, with FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY. String pkg = "pkg"; int uid = 9; UserHandle userHandle = UserHandle.getUserHandleForUid(uid); NotificationChannel channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH); Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon) .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true); StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0, nb.build(), userHandle, null, 0); NotificationRecord old = new NotificationRecord(mContext, sbn, channel); // Creates updated notification (without FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) Notification.Builder nb2 = new Notification.Builder(mContext, channel.getId()) .setContentTitle("new title") .setSmallIcon(android.R.drawable.sym_def_app_icon) .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, false); StatusBarNotification sbn2 = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0, nb2.build(), userHandle, null, 0); NotificationRecord toPost = new NotificationRecord(mContext, sbn2, channel); // Create system ui-like service. ManagedServices.ManagedServiceInfo info = mListeners.new ManagedServiceInfo( null, new ComponentName("a", "a"), sbn2.getUserId(), false, null, 33, 33); info.isSystemUi = true; INotificationListener l1 = mock(INotificationListener.class); info.service = l1; List<ManagedServices.ManagedServiceInfo> services = ImmutableList.of(info); when(mListeners.getServices()).thenReturn(services); FieldSetter.setField(mNm, NotificationManagerService.class.getDeclaredField("mHandler"), mock(NotificationManagerService.WorkerHandler.class)); doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any()); doReturn(mock(NotificationRankingUpdate.class)).when(mNm).makeRankingUpdateLocked(info); doReturn(false).when(mNm).isInLockDownMode(anyInt()); doNothing().when(mNm).updateUriPermissions(any(), any(), any(), anyInt()); doReturn(sbn2).when(mListeners).redactStatusBarNotification(sbn2); doReturn(sbn2).when(mListeners).redactStatusBarNotification(any()); // The notification change is posted to the service listener. mListeners.notifyPostedLocked(toPost, old); // Verify that the post occcurs with the updated notification value. ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); verify(mNm.mHandler, times(1)).post(runnableCaptor.capture()); runnableCaptor.getValue().run(); ArgumentCaptor<IStatusBarNotificationHolder> sbnCaptor = ArgumentCaptor.forClass(IStatusBarNotificationHolder.class); verify(l1, times(1)).onNotificationPosted(sbnCaptor.capture(), any()); StatusBarNotification sbnResult = sbnCaptor.getValue().get(); assertThat(sbnResult.getNotification() .extras.getCharSequence(Notification.EXTRA_TITLE).toString()) .isEqualTo("new title"); } /** /** * Helper method to test the thread safety of some operations. * Helper method to test the thread safety of some operations. * * Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +50 −3 Original line number Original line Diff line number Diff line Loading @@ -106,6 +106,7 @@ import static android.service.notification.NotificationListenerService.Ranking.U import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER; Loading @@ -118,11 +119,11 @@ import static com.android.server.notification.NotificationManagerService.TAG; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth.assertWithMessage; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNotNull; Loading @@ -131,6 +132,7 @@ import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; import static junit.framework.Assert.fail; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.isNull; Loading @@ -157,6 +159,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.Mockito.when; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import android.Manifest; import android.Manifest; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SuppressLint; Loading Loading @@ -270,8 +275,10 @@ import android.util.Pair; import android.util.Xml; import android.util.Xml; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager; import android.widget.RemoteViews; import android.widget.RemoteViews; import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.R; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.config.sysui.TestableFlagResolver; import com.android.internal.config.sysui.TestableFlagResolver; Loading Loading @@ -303,10 +310,12 @@ import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.google.android.collect.Lists; import com.google.android.collect.Lists; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import org.junit.After; import org.junit.After; import org.junit.Assert; import org.junit.Assert; import org.junit.Before; import org.junit.Before; Loading Loading @@ -5933,6 +5942,45 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertThat(captor.getValue().shouldPostSilently()).isTrue(); assertThat(captor.getValue().shouldPostSilently()).isTrue(); } } @Test @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR) public void testUpdate_DirectReplyLifetimeExtendedUpdateSucceeds() throws Exception { // Creates a lifetime extended notification. NotificationRecord original = generateNotificationRecord(mTestNotificationChannel); original.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY; mService.addNotification(original); // Post an update for that notification. StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, original.getSbn().getId(), original.getSbn().getTag(), mUid, 0, new Notification.Builder(mContext, mTestNotificationChannel.getId()) .setSmallIcon(android.R.drawable.sym_def_app_icon) .setContentTitle("new title").build(), UserHandle.getUserHandleForUid(mUid), null, 0); NotificationRecord update = new NotificationRecord(mContext, sbn, mTestNotificationChannel); mService.addEnqueuedNotification(update); NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(update.getKey(), update.getSbn().getPackageName(), update.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); // Checks the update was sent, and that update contains the new title, and does not contain // the lifetime extension flag. ArgumentCaptor<NotificationRecord> captor = ArgumentCaptor.forClass(NotificationRecord.class); verify(mListeners, times(1)).prepareNotifyPostedLocked(captor.capture(), any(), anyBoolean()); assertThat(captor.getValue().getNotification().flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0); assertThat(captor.getValue() .getNotification().extras.getCharSequence(Notification.EXTRA_TITLE).toString()) .isEqualTo("new title"); } @Test @Test public void testStats_updatedOnUserExpansion() throws Exception { public void testStats_updatedOnUserExpansion() throws Exception { NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); Loading Loading @@ -12602,7 +12650,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // the notifyPostedLocked function is called twice. // the notifyPostedLocked function is called twice. verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong()); verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong()); //verify(mListeners, times(2)).notifyPostedLocked(any(), any()); } } @Test @Test