diff options
3 files changed, 133 insertions, 5 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 76b1201a8784..0c658379fa4d 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -140,6 +140,7 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; 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.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; @@ -307,6 +308,7 @@ import android.view.Display; import android.view.accessibility.AccessibilityManager; import android.widget.RemoteViews; import android.widget.Toast; + import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -358,7 +360,9 @@ import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.BackgroundActivityStartCallback; import com.android.server.wm.WindowManagerInternal; + import libcore.io.IoUtils; + import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParserException; @@ -11776,10 +11780,18 @@ public class NotificationManagerService extends SystemService { } 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 // has been lifetime extended. // (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 // only to System UI, and break as we don't need to continue checking other // Managed Services. @@ -11787,7 +11799,7 @@ public class NotificationManagerService extends SystemService { && (old.getNotification().flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) { final NotificationRankingUpdate update = makeRankingUpdateLocked(info); - listenerCalls.add(() -> notifyPosted(info, oldSbn, update)); + listenerCalls.add(() -> notifyPosted(info, sbnToPost, update)); break; } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index ae3683961d61..983e694a8f1a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -70,6 +70,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.INotificationListener; +import android.service.notification.IStatusBarNotificationHolder; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationRankingUpdate; @@ -90,6 +91,7 @@ import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -154,6 +156,11 @@ public class NotificationListenersTest extends UiServiceTestCase { .thenReturn(new ArrayList<>()); mNm.mHandler = mock(NotificationManagerService.WorkerHandler.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 @@ -827,6 +834,68 @@ public class NotificationListenersTest extends UiServiceTestCase { 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. * 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 011f2e39d6f8..aec47146e833 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -106,6 +106,7 @@ import static android.service.notification.NotificationListenerService.Ranking.U import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; 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.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER; @@ -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_POSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED; + import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; 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.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -131,6 +132,7 @@ import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; + import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.isNull; @@ -157,6 +159,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + import android.Manifest; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -270,8 +275,10 @@ import android.util.Pair; import android.util.Xml; import android.view.accessibility.AccessibilityManager; import android.widget.RemoteViews; + import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; + import com.android.internal.R; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.config.sysui.TestableFlagResolver; @@ -303,10 +310,12 @@ import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; + import com.google.android.collect.Lists; import com.google.common.collect.ImmutableList; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -5934,6 +5943,45 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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 public void testStats_updatedOnUserExpansion() throws Exception { NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); mService.addNotification(r); @@ -12602,7 +12650,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // the notifyPostedLocked function is called twice. verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong()); - //verify(mListeners, times(2)).notifyPostedLocked(any(), any()); } @Test |