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

Commit db7d09e1 authored by Yining Liu's avatar Yining Liu Committed by Android (Google) Code Review
Browse files

Merge "Improve data of non-dismissible notifications behaviour"

parents 29b6bbbb 9e16d20a
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -703,6 +703,16 @@ public class Notification implements Parcelable
     */
    public static final int FLAG_BUBBLE = 0x00001000;
    /**
     * Bit to be bitswised-ored into the {@link #flags} field that should be
     * set by the system if this notification is not dismissible.
     *
     * This flag is for internal use only; applications cannot set this flag directly.
     * @hide
     */
    public static final int FLAG_NO_DISMISS = 0x00002000;
    private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
            BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
            DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
+38 −4
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.FLAG_INSISTENT;
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.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
@@ -576,6 +577,9 @@ public class NotificationManagerService extends SystemService {
    private float mInCallNotificationVolume;
    private Binder mCallNotificationToken = null;
    private static final boolean ONGOING_DISMISSAL = SystemProperties.getBoolean(
            "persist.sysui.notification.ongoing_dismissal", true);
    // used as a mutex for access to all active notifications & listeners
    final Object mNotificationLock = new Object();
    @GuardedBy("mNotificationLock")
@@ -1201,10 +1205,13 @@ public class NotificationManagerService extends SystemService {
                    id = r.getSbn().getId();
                }
            }
            int mustNotHaveFlags = FLAG_ONGOING_EVENT;
            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
                    mustNotHaveFlags,
                    true, userId, REASON_CANCEL, nv.rank, nv.count,null);
            int mustNotHaveFlags = ONGOING_DISMISSAL ? FLAG_NO_DISMISS : FLAG_ONGOING_EVENT;
            cancelNotification(callingUid, callingPid, pkg, tag, id,
                    /* mustHaveFlags= */ 0,
                    /* mustNotHaveFlags= */ mustNotHaveFlags,
                    /* sendDelete= */ true,
                    userId, REASON_CANCEL, nv.rank, nv.count, /* listener= */ null);
            nv.recycle();
        }
@@ -6689,6 +6696,16 @@ public class NotificationManagerService extends SystemService {
                (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
        Notification.addFieldsFromContext(ai, notification);
        // Only notifications that can be non-dismissible can have the flag FLAG_NO_DISMISS
        if (ONGOING_DISMISSAL) {
            if (((notification.flags & FLAG_ONGOING_EVENT) > 0)
                    && canBeNonDismissible(ai, notification)) {
                notification.flags |= FLAG_NO_DISMISS;
            } else {
                notification.flags &= ~FLAG_NO_DISMISS;
            }
        }
        int canColorize = mPackageManagerClient.checkPermission(
                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
        if (canColorize == PERMISSION_GRANTED) {
@@ -6775,6 +6792,23 @@ public class NotificationManagerService extends SystemService {
        checkRemoteViews(pkg, tag, id, notification);
    }
    /**
     * Whether a notification can be non-dismissible.
     * A notification should be dismissible, unless it's exempted for some reason.
     */
    private boolean canBeNonDismissible(ApplicationInfo ai, Notification notification) {
        // Check if the app is on the system partition, or an update to an app on the system
        // partition.
        boolean isSystemAppExempt = (ai.flags
                & (ApplicationInfo.FLAG_UPDATED_SYSTEM_APP | ApplicationInfo.FLAG_SYSTEM)) > 0;
        return isSystemAppExempt || notification.isMediaNotification() || isEnterpriseExempted();
    }
    // TODO: b/266237746 Enterprise app exemptions
    private boolean isEnterpriseExempted() {
        return false;
    }
    private void checkRemoteViews(String pkg, String tag, int id, Notification notification) {
        if (removeRemoteView(pkg, tag, id, notification.contentView)) {
            notification.contentView = null;
+208 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -10083,4 +10084,211 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        mInternalService.sendReviewPermissionsNotification();
        verify(mMockNm, never()).notify(anyString(), anyInt(), any(Notification.class));
    }

    @Test
    public void fixSystemNotification_withOnGoingFlag_shouldBeNonDismissible()
            throws Exception {
        // Given: a notification from an app on the system partition has the flag
        // FLAG_ONGOING_EVENT set
        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
        mService.setOngoingDismissal(true);
        Notification n = new Notification.Builder(mContext, "test")
                .setOngoing(true)
                .build();

        final ApplicationInfo systemAppInfo = new ApplicationInfo();
        systemAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
                .thenReturn(systemAppInfo);

        // When: fix the notification with NotificationManagerService
        mService.fixNotification(n, PKG, "tag", 9, 0);

        // Then: the notification's flag FLAG_NO_DISMISS should be set
        assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);

        // Avoid affecting other tests
        mService.setOngoingDismissal(false);
    }

    @Test
    public void fixMediaNotification_withOnGoingFlag_shouldBeNonDismissible()
            throws Exception {
        // Given: a media notification has the flag FLAG_ONGOING_EVENT set
        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
        mService.setOngoingDismissal(true);
        Notification n = new Notification.Builder(mContext, "test")
                .setOngoing(true)
                .setStyle(new Notification.MediaStyle()
                        .setMediaSession(mock(MediaSession.Token.class)))
                .build();

        // When: fix the notification with NotificationManagerService
        mService.fixNotification(n, PKG, "tag", 9, 0);

        // Then: the notification's flag FLAG_NO_DISMISS should be set
        assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);

        // Avoid affecting other tests
        mService.setOngoingDismissal(false);
    }

    @Test
    public void fixNonExemptNotification_withOnGoingFlag_shouldBeDismissible() throws Exception {
        // Given: a non-exempt notification has the flag FLAG_ONGOING_EVENT set
        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
        mService.setOngoingDismissal(true);
        Notification n = new Notification.Builder(mContext, "test")
                .setOngoing(true)
                .build();

        // When: fix the notification with NotificationManagerService
        mService.fixNotification(n, PKG, "tag", 9, 0);

        // Then: the notification's flag FLAG_NO_DISMISS should not be set
        assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);

        // Avoid affecting other tests
        mService.setOngoingDismissal(false);
    }

    @Test
    public void fixNonExemptNotification_withNoDismissFlag_shouldBeDismissible()
            throws Exception {
        // Given: a non-exempt notification has the flag FLAG_NO_DISMISS set (even though this is
        // not allowed)
        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
        mService.setOngoingDismissal(true);
        Notification n = new Notification.Builder(mContext, "test")
                .build();
        n.flags |= Notification.FLAG_NO_DISMISS;

        // When: fix the notification with NotificationManagerService
        mService.fixNotification(n, PKG, "tag", 9, 0);

        // Then: the notification's flag FLAG_NO_DISMISS should be cleared
        assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);

        // Avoid affecting other tests
        mService.setOngoingDismissal(false);
    }

    @Test
    public void fixSystemNotification_withoutOnGoingFlag_shouldBeDismissible() throws Exception {
        // Given: a notification from an app on the system partition doesn't have the flag
        // FLAG_ONGOING_EVENT set
        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
        mService.setOngoingDismissal(true);
        Notification n = new Notification.Builder(mContext, "test")
                .setOngoing(false)
                .build();

        final ApplicationInfo systemAppInfo = new ApplicationInfo();
        systemAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
                .thenReturn(systemAppInfo);

        // When: fix the notification with NotificationManagerService
        mService.fixNotification(n, PKG, "tag", 9, 0);

        // Then: the notification's flag FLAG_NO_DISMISS should not be set
        assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);

        // Avoid affecting other tests
        mService.setOngoingDismissal(false);
    }

    @Test
    public void fixSystemNotification_withoutOnGoingFlag_withNoDismissFlag_shouldBeDismissible()
            throws Exception {
        // Given: a notification from an app on the system partition doesn't have the flag
        // FLAG_ONGOING_EVENT set, but has the flag FLAG_NO_DISMISS set
        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
        mService.setOngoingDismissal(true);
        Notification n = new Notification.Builder(mContext, "test")
                .setOngoing(false)
                .build();
        n.flags |= Notification.FLAG_NO_DISMISS;

        final ApplicationInfo systemAppInfo = new ApplicationInfo();
        systemAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
                .thenReturn(systemAppInfo);

        // When: fix the notification with NotificationManagerService
        mService.fixNotification(n, PKG, "tag", 9, 0);

        // Then: the notification's flag FLAG_NO_DISMISS should be cleared
        assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);

        // Avoid affecting other tests
        mService.setOngoingDismissal(false);
    }

    @Test
    public void fixMediaNotification_withoutOnGoingFlag_shouldBeDismissible() throws Exception {
        // Given: a media notification doesn't have the flag FLAG_ONGOING_EVENT set
        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
        mService.setOngoingDismissal(true);
        Notification n = new Notification.Builder(mContext, "test")
                .setOngoing(false)
                .setStyle(new Notification.MediaStyle()
                        .setMediaSession(mock(MediaSession.Token.class)))
                .build();

        // When: fix the notification with NotificationManagerService
        mService.fixNotification(n, PKG, "tag", 9, 0);

        // Then: the notification's flag FLAG_NO_DISMISS should not be set
        assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);

        // Avoid affecting other tests
        mService.setOngoingDismissal(false);
    }

    @Test
    public void fixMediaNotification_withoutOnGoingFlag_withNoDismissFlag_shouldBeDismissible()
            throws Exception {
        // Given: a media notification doesn't have the flag FLAG_ONGOING_EVENT set,
        // but has the flag FLAG_NO_DISMISS set
        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
        mService.setOngoingDismissal(true);
        Notification n = new Notification.Builder(mContext, "test")
                .setOngoing(false)
                .setStyle(new Notification.MediaStyle()
                        .setMediaSession(mock(MediaSession.Token.class)))
                .build();
        n.flags |= Notification.FLAG_NO_DISMISS;

        // When: fix the notification with NotificationManagerService
        mService.fixNotification(n, PKG, "tag", 9, 0);

        // Then: the notification's flag FLAG_NO_DISMISS should be cleared
        assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);

        // Avoid affecting other tests
        mService.setOngoingDismissal(false);
    }

    @Test
    public void fixNonExempt_Notification_withoutOnGoingFlag_shouldBeDismissible()
            throws Exception {
        // Given: a non-exempt notification has the flag FLAG_ONGOING_EVENT set
        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
        mService.setOngoingDismissal(true);
        mService.setOngoingDismissal(true);
        Notification n = new Notification.Builder(mContext, "test")
                .setOngoing(false)
                .build();

        // When: fix the notification with NotificationManagerService
        mService.fixNotification(n, PKG, "tag", 9, 0);

        // Then: the notification's flag FLAG_NO_DISMISS should not be set
        assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);

        // Avoid affecting other tests
        mService.setOngoingDismissal(false);
    }

}
+7 −0
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ public class TestableNotificationManagerService extends NotificationManagerServi
    int countLogSmartSuggestionsVisible = 0;
    Set<Integer> mChannelToastsSent = new HashSet<>();

    public boolean ONGOING_DISMISSAL = false;

    String stringArrayResourceValue;
    @Nullable
    NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;
@@ -159,4 +161,9 @@ public class TestableNotificationManagerService extends NotificationManagerServi
            return mGetStrongAuthForUserReturnValue;
        }
    }

    // Mock SystemProperties
    protected void setOngoingDismissal(boolean ongoingDismissal) {
        ONGOING_DISMISSAL = ongoingDismissal;
    }
}