Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 08ece225 authored by Alexander Roederer's avatar Alexander Roederer Committed by Android (Google) Code Review
Browse files

Merge "Send cancelation on Lifetime Extension action" into main

parents 3a79b1d7 210c39d6
Loading
Loading
Loading
Loading
+33 −7
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR;
import static android.app.Flags.lifetimeExtensionRefactor;
import static android.app.Flags.updateRankingTime;
import static android.app.Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
import static android.app.Notification.EXTRA_BUILDER_APPLICATION_INFO;
import static android.app.Notification.EXTRA_LARGE_ICON_BIG;
@@ -1321,7 +1320,29 @@ public class NotificationManagerService extends SystemService {
                // Notifications that have been interacted with should no longer be lifetime
                // extended.
                if (lifetimeExtensionRefactor()) {
                    r.getSbn().getNotification().flags &= ~FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
                    // Enqueue a cancellation; this cancellation should only work if
                    // the notification still has FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY
                    // We wait for 200 milliseconds before posting the cancel, to allow the app
                    // time to update the notification in response instead.
                    // If that update goes through, the notification won't have the lifetime
                    // extended flag, and this cancellation will be dropped.
                    mHandler.scheduleCancelNotification(
                            new CancelNotificationRunnable(
                                    callingUid,
                                    callingPid,
                                    r.getSbn().getPackageName(),
                                    r.getSbn().getTag(),
                                    r.getSbn().getId(),
                                    FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY /*=mustHaveFlags*/,
                                    FLAG_NO_DISMISS /*=mustNotHaveFlags*/,
                                    false /*=sendDelete*/,
                                    r.getUserId(),
                                    REASON_APP_CANCEL,
                                    -1 /*=rank*/,
                                    -1 /*=count*/,
                                    null /*=listener*/,
                                    SystemClock.elapsedRealtime()),
                            200);
                }
            }
        }
@@ -9337,7 +9358,11 @@ public class NotificationManagerService extends SystemService {
            }
        }
        protected void scheduleCancelNotification(CancelNotificationRunnable cancelRunnable) {
        protected void scheduleCancelNotification(CancelNotificationRunnable cancelRunnable,
                                                  int delay) {
            if (lifetimeExtensionRefactor()) {
                sendMessageDelayed(Message.obtain(this, cancelRunnable), delay);
            } else {
                if (Flags.notificationReduceMessagequeueUsage()) {
                    sendMessage(Message.obtain(this, cancelRunnable));
                } else {
@@ -9346,6 +9371,7 @@ public class NotificationManagerService extends SystemService {
                    }
                }
            }
        }
        protected void scheduleOnPackageChanged(boolean removingPackage, int changeUserId,
                String[] pkgList, int[] uidList) {
@@ -9698,7 +9724,7 @@ public class NotificationManagerService extends SystemService {
        // remove notification call ends up in not removing the notification.
        mHandler.scheduleCancelNotification(new CancelNotificationRunnable(callingUid, callingPid,
                pkg, tag, id, mustHaveFlags, mustNotHaveFlags, sendDelete, userId, reason, rank,
                count, listener, SystemClock.elapsedRealtime()));
                count, listener, SystemClock.elapsedRealtime()), 0);
    }
    /**
+109 −13
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import static android.app.Notification.FLAG_CAN_COLORIZE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_NO_DISMISS;
import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
import static android.app.Notification.FLAG_USER_INITIATED_JOB;
@@ -6048,7 +6049,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        waitForIdle();
        verify(handler, timeout(300).times(0)).scheduleSendRankingUpdate();
        verify(handler, times(1)).scheduleCancelNotification(any());
        verify(handler, times(1)).scheduleCancelNotification(any(), eq(0));
    }
    @Test
@@ -6307,7 +6308,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                        NotificationManagerService.CancelNotificationRunnable.class);
        verify(handler, times(1)).scheduleCancelNotification(
                captor.capture());
                captor.capture(), eq(0));
        // Run the runnable given to the cancel notification, and see if it logs properly
        NotificationManagerService.CancelNotificationRunnable runnable = captor.getValue();
@@ -8651,34 +8652,128 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    }
    @Test
    public void testOnNotificationActionClickLifetimeExtendedEnds() {
    public void testActionClickLifetimeExtendedCancel() throws Exception {
        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
        final Notification.Action action =
                new Notification.Action.Builder(null, "text", PendingIntent.getActivity(
                        mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build();
        final boolean generatedByAssistant = false;
        // Creates a notification marked as being lifetime extended.
        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
        r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
        mService.addNotification(r);
        StatusBarNotification[] notifs =
                mBinderService.getActiveNotifications(mPkg);
        assertThat(notifs.length).isEqualTo(1);
        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
        // Call on action click.
        NotificationVisibility notificationVisibility =
                NotificationVisibility.obtain(r.getKey(), 1, 2, true);
        mService.mNotificationDelegate.onNotificationActionClick(
                10, 10, r.getKey(), /*actionIndex=*/2, action, notificationVisibility,
                /*generatedByAssistant=*/false);
        // The flag is removed, so the notification is no longer lifetime extended.
        // Lifetime extended flag persists.
        assertThat(r.getSbn().getNotification().flags
                & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
                & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isGreaterThan(0);
        // The record is sent out without the flag.
        ArgumentCaptor<NotificationRecord> captor =
                ArgumentCaptor.forClass(NotificationRecord.class);
        verify(mAssistants, times(1)).notifyAssistantActionClicked(
                captor.capture(), eq(action), eq(generatedByAssistant));
        assertThat(captor.getValue().getNotification().flags
                & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
        mTestableLooper.moveTimeForward(210);
        waitForIdle();
        verify(mWorkerHandler, times(1))
                .scheduleCancelNotification(
                        any(NotificationManagerService.CancelNotificationRunnable.class), eq(200));
        // Check that the cancelation occurred and the notification is gone.
        notifs = mBinderService.getActiveNotifications(mPkg);
        assertThat(notifs.length).isEqualTo(0);
        assertThat(mService.getNotificationRecordCount()).isEqualTo(0);
    }
    @Test
    public void testActionClickLifetimeExtendedCancel_PreventByNoDismiss() throws Exception {
        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
        final Notification.Action action =
                new Notification.Action.Builder(null, "text", PendingIntent.getActivity(
                        mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build();
        // Creates a notification marked as being lifetime extended.
        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
        r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
        // Make the notification non-dismissable
        r.getSbn().getNotification().flags |= FLAG_NO_DISMISS;
        mService.addNotification(r);
        StatusBarNotification[] notifs =
                mBinderService.getActiveNotifications(mPkg);
        assertThat(notifs.length).isEqualTo(1);
        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
        // Call on action click.
        NotificationVisibility notificationVisibility =
                NotificationVisibility.obtain(r.getKey(), 1, 2, true);
        mService.mNotificationDelegate.onNotificationActionClick(
                10, 10, r.getKey(), /*actionIndex=*/2, action, notificationVisibility,
                /*generatedByAssistant=*/false);
        // Lifetime extended flag persists.
        assertThat(r.getSbn().getNotification().flags
                & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isGreaterThan(0);
        mTestableLooper.moveTimeForward(210);
        waitForIdle();
        verify(mWorkerHandler, times(1))
                .scheduleCancelNotification(
                        any(NotificationManagerService.CancelNotificationRunnable.class), eq(200));
        // The cancellation is dropped and the notification is still present, with the update.
        notifs = mBinderService.getActiveNotifications(mPkg);
        assertThat(notifs.length).isEqualTo(1);
        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
    }
    @Test
    public void testUpdateOnActionClickDropsLifetimeExtendedCancel() throws Exception {
        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
        final Notification.Action action =
                new Notification.Action.Builder(null, "text", PendingIntent.getActivity(
                        mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build();
        // Creates a notification marked as being lifetime extended.
        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
        r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
        mService.addNotification(r);
        StatusBarNotification[] notifs =
                mBinderService.getActiveNotifications(mPkg);
        assertThat(notifs.length).isEqualTo(1);
        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
        // Call on action click.
        NotificationVisibility notificationVisibility =
                NotificationVisibility.obtain(r.getKey(), 1, 2, true);
        mService.mNotificationDelegate.onNotificationActionClick(
                10, 10, r.getKey(), /*actionIndex=*/2, action, notificationVisibility,
                /*generatedByAssistant=*/false);
        // The "app" sends an update of the notification in response.
        mBinderService.enqueueNotificationWithTag(mPkg, mPkg, r.getSbn().getTag(),
                r.getSbn().getId(), r.getSbn().getNotification(), r.getSbn().getUserId());
        mTestableLooper.moveTimeForward(210);
        waitForIdle();
        verify(mWorkerHandler, times(1))
                .scheduleCancelNotification(
                        any(NotificationManagerService.CancelNotificationRunnable.class), eq(200));
        // The cancellation is dropped and the notification is still present, with the update.
        notifs = mBinderService.getActiveNotifications(mPkg);
        assertThat(notifs.length).isEqualTo(1);
        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
    }
    @Test
@@ -8706,6 +8801,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                mNotificationRecordLogger.event(0));
    }
    @Test
    public void testLogSmartSuggestionsVisible_triggerOnExpandAndVisible() {
        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);