Loading core/java/android/app/INotificationManager.aidl +7 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,13 @@ interface INotificationManager void setShowBadge(String pkg, int uid, boolean showBadge); boolean canShowBadge(String pkg, int uid); void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled); /** * Updates the notification's enabled state. Additionally locks importance for all of the * notifications belonging to the app, such that future notifications aren't reconsidered for * blocking helper. */ void setNotificationsEnabledWithImportanceLockForPackage(String pkg, int uid, boolean enabled); boolean areNotificationsEnabledForPackage(String pkg, int uid); boolean areNotificationsEnabled(String pkg); int getPackageImportance(String pkg); Loading packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +1 −1 Original line number Diff line number Diff line Loading @@ -523,7 +523,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } else { // For notifications with more than one channel, update notification enabled // state. If the importance was lowered, we disable notifications. mINotificationManager.setNotificationsEnabledForPackage( mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage( mPackageName, mAppUid, mNewImportance >= mCurrentImportance); } } catch (RemoteException e) { Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +60 −0 Original line number Diff line number Diff line Loading @@ -420,6 +420,66 @@ public class NotificationInfoTest extends SysuiTestCase { assertEquals(IMPORTANCE_UNSPECIFIED, mNotificationChannel.getImportance()); } @Test public void testHandleCloseControls_setsNotificationsDisabledForMultipleChannelNotifications() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , false /* isNonblockable */); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, times(1)) .setNotificationsEnabledWithImportanceLockForPackage( anyString(), eq(TEST_UID), eq(false)); } @Test public void testHandleCloseControls_keepsNotificationsEnabledForMultipleChannelNotifications() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , false /* isNonblockable */); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, times(1)) .setNotificationsEnabledWithImportanceLockForPackage( anyString(), eq(TEST_UID), eq(false)); } @Test public void testCloseControls_blockingHelperSavesImportanceForMultipleChannelNotifications() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , false /* isNonblockable */, true /* isForBlockingHelper */, true /* isUserSentimentNegative */); mNotificationInfo.findViewById(R.id.keep).performClick(); verify(mBlockingHelperManager).dismissCurrentBlockingHelper(); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, times(1)) .setNotificationsEnabledWithImportanceLockForPackage( anyString(), eq(TEST_UID), eq(true)); } @Test public void testCloseControls_blockingHelperDismissedIfShown() throws Exception { mNotificationInfo.bindNotification( Loading services/core/java/com/android/server/notification/NotificationManagerService.java +23 −0 Original line number Diff line number Diff line Loading @@ -2130,6 +2130,26 @@ public class NotificationManagerService extends SystemService { savePolicyFile(); } /** * Updates the enabled state for notifications for the given package (and uid). * Additionally, this method marks the app importance as locked by the user, which means * that notifications from the app will <b>not</b> be considered for showing a * blocking helper. * * @param pkg package that owns the notifications to update * @param uid uid of the app providing notifications * @param enabled whether notifications should be enabled for the app * * @see #setNotificationsEnabledForPackage(String, int, boolean) */ @Override public void setNotificationsEnabledWithImportanceLockForPackage( String pkg, int uid, boolean enabled) { setNotificationsEnabledForPackage(pkg, uid, enabled); mRankingHelper.setAppImportanceLocked(pkg, uid); } /** * Use this when you just want to know if notifications are OK for this package. */ Loading Loading @@ -3616,6 +3636,8 @@ public class NotificationManagerService extends SystemService { System.currentTimeMillis()); summaryRecord = new NotificationRecord(getContext(), summarySbn, notificationRecord.getChannel()); summaryRecord.setIsAppImportanceLocked( notificationRecord.getIsAppImportanceLocked()); summaries.put(pkg, summarySbn.getKey()); } } Loading Loading @@ -4012,6 +4034,7 @@ public class NotificationManagerService extends SystemService { pkg, opPkg, id, tag, notificationUid, callingPid, notification, user, null, System.currentTimeMillis()); final NotificationRecord r = new NotificationRecord(getContext(), n, channel); r.setIsAppImportanceLocked(mRankingHelper.getIsAppImportanceLocked(pkg, callingUid)); if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0 && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0 Loading services/core/java/com/android/server/notification/NotificationRecord.java +25 −7 Original line number Diff line number Diff line Loading @@ -151,11 +151,14 @@ public final class NotificationRecord { private boolean mIsInterruptive; private int mNumberOfSmartRepliesAdded; private boolean mHasSeenSmartReplies; /** * Whether this notification (and its channels) should be considered user locked. Used in * conjunction with user sentiment calculation. */ private boolean mIsAppImportanceLocked; @VisibleForTesting public NotificationRecord(Context context, StatusBarNotification sbn, NotificationChannel channel) { NotificationChannel channel) { this.sbn = sbn; mOriginalFlags = sbn.getNotification().flags; mRankingTimeMs = calculateRankingTimeMs(0L); Loading Loading @@ -503,6 +506,7 @@ public final class NotificationRecord { pw.println(prefix + "mImportance=" + NotificationListenerService.Ranking.importanceToString(mImportance)); pw.println(prefix + "mImportanceExplanation=" + mImportanceExplanation); pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked); pw.println(prefix + "mIntercept=" + mIntercept); pw.println(prefix + "mHidden==" + mHidden); pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey); Loading Loading @@ -564,11 +568,11 @@ public final class NotificationRecord { public final String toString() { return String.format( "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" + ": %s)", "appImportanceLocked=%s: %s)", System.identityHashCode(this), this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(), this.sbn.getTag(), this.mImportance, this.sbn.getKey(), this.sbn.getNotification()); mIsAppImportanceLocked, this.sbn.getNotification()); } public void addAdjustment(Adjustment adjustment) { Loading Loading @@ -600,7 +604,8 @@ public final class NotificationRecord { if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) { // Only allow user sentiment update from assistant if user hasn't already // expressed a preference for this channel if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) { if (!mIsAppImportanceLocked && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) { setUserSentiment(adjustment.getSignals().getInt( Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL)); } Loading @@ -609,6 +614,11 @@ public final class NotificationRecord { } } public void setIsAppImportanceLocked(boolean isAppImportanceLocked) { mIsAppImportanceLocked = isAppImportanceLocked; calculateUserSentiment(); } public void setContactAffinity(float contactAffinity) { mContactAffinity = contactAffinity; if (mImportance < IMPORTANCE_DEFAULT && Loading Loading @@ -870,6 +880,13 @@ public final class NotificationRecord { return mChannel; } /** * @see RankingHelper#getIsAppImportanceLocked(String, int) */ public boolean getIsAppImportanceLocked() { return mIsAppImportanceLocked; } protected void updateNotificationChannel(NotificationChannel channel) { if (channel != null) { mChannel = channel; Loading Loading @@ -927,7 +944,8 @@ public final class NotificationRecord { } private void calculateUserSentiment() { if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0) { if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0 || mIsAppImportanceLocked) { mUserSentiment = USER_SENTIMENT_POSITIVE; } } Loading Loading
core/java/android/app/INotificationManager.aidl +7 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,13 @@ interface INotificationManager void setShowBadge(String pkg, int uid, boolean showBadge); boolean canShowBadge(String pkg, int uid); void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled); /** * Updates the notification's enabled state. Additionally locks importance for all of the * notifications belonging to the app, such that future notifications aren't reconsidered for * blocking helper. */ void setNotificationsEnabledWithImportanceLockForPackage(String pkg, int uid, boolean enabled); boolean areNotificationsEnabledForPackage(String pkg, int uid); boolean areNotificationsEnabled(String pkg); int getPackageImportance(String pkg); Loading
packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +1 −1 Original line number Diff line number Diff line Loading @@ -523,7 +523,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } else { // For notifications with more than one channel, update notification enabled // state. If the importance was lowered, we disable notifications. mINotificationManager.setNotificationsEnabledForPackage( mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage( mPackageName, mAppUid, mNewImportance >= mCurrentImportance); } } catch (RemoteException e) { Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +60 −0 Original line number Diff line number Diff line Loading @@ -420,6 +420,66 @@ public class NotificationInfoTest extends SysuiTestCase { assertEquals(IMPORTANCE_UNSPECIFIED, mNotificationChannel.getImportance()); } @Test public void testHandleCloseControls_setsNotificationsDisabledForMultipleChannelNotifications() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , false /* isNonblockable */); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, times(1)) .setNotificationsEnabledWithImportanceLockForPackage( anyString(), eq(TEST_UID), eq(false)); } @Test public void testHandleCloseControls_keepsNotificationsEnabledForMultipleChannelNotifications() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , false /* isNonblockable */); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, times(1)) .setNotificationsEnabledWithImportanceLockForPackage( anyString(), eq(TEST_UID), eq(false)); } @Test public void testCloseControls_blockingHelperSavesImportanceForMultipleChannelNotifications() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , false /* isNonblockable */, true /* isForBlockingHelper */, true /* isUserSentimentNegative */); mNotificationInfo.findViewById(R.id.keep).performClick(); verify(mBlockingHelperManager).dismissCurrentBlockingHelper(); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, times(1)) .setNotificationsEnabledWithImportanceLockForPackage( anyString(), eq(TEST_UID), eq(true)); } @Test public void testCloseControls_blockingHelperDismissedIfShown() throws Exception { mNotificationInfo.bindNotification( Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +23 −0 Original line number Diff line number Diff line Loading @@ -2130,6 +2130,26 @@ public class NotificationManagerService extends SystemService { savePolicyFile(); } /** * Updates the enabled state for notifications for the given package (and uid). * Additionally, this method marks the app importance as locked by the user, which means * that notifications from the app will <b>not</b> be considered for showing a * blocking helper. * * @param pkg package that owns the notifications to update * @param uid uid of the app providing notifications * @param enabled whether notifications should be enabled for the app * * @see #setNotificationsEnabledForPackage(String, int, boolean) */ @Override public void setNotificationsEnabledWithImportanceLockForPackage( String pkg, int uid, boolean enabled) { setNotificationsEnabledForPackage(pkg, uid, enabled); mRankingHelper.setAppImportanceLocked(pkg, uid); } /** * Use this when you just want to know if notifications are OK for this package. */ Loading Loading @@ -3616,6 +3636,8 @@ public class NotificationManagerService extends SystemService { System.currentTimeMillis()); summaryRecord = new NotificationRecord(getContext(), summarySbn, notificationRecord.getChannel()); summaryRecord.setIsAppImportanceLocked( notificationRecord.getIsAppImportanceLocked()); summaries.put(pkg, summarySbn.getKey()); } } Loading Loading @@ -4012,6 +4034,7 @@ public class NotificationManagerService extends SystemService { pkg, opPkg, id, tag, notificationUid, callingPid, notification, user, null, System.currentTimeMillis()); final NotificationRecord r = new NotificationRecord(getContext(), n, channel); r.setIsAppImportanceLocked(mRankingHelper.getIsAppImportanceLocked(pkg, callingUid)); if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0 && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0 Loading
services/core/java/com/android/server/notification/NotificationRecord.java +25 −7 Original line number Diff line number Diff line Loading @@ -151,11 +151,14 @@ public final class NotificationRecord { private boolean mIsInterruptive; private int mNumberOfSmartRepliesAdded; private boolean mHasSeenSmartReplies; /** * Whether this notification (and its channels) should be considered user locked. Used in * conjunction with user sentiment calculation. */ private boolean mIsAppImportanceLocked; @VisibleForTesting public NotificationRecord(Context context, StatusBarNotification sbn, NotificationChannel channel) { NotificationChannel channel) { this.sbn = sbn; mOriginalFlags = sbn.getNotification().flags; mRankingTimeMs = calculateRankingTimeMs(0L); Loading Loading @@ -503,6 +506,7 @@ public final class NotificationRecord { pw.println(prefix + "mImportance=" + NotificationListenerService.Ranking.importanceToString(mImportance)); pw.println(prefix + "mImportanceExplanation=" + mImportanceExplanation); pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked); pw.println(prefix + "mIntercept=" + mIntercept); pw.println(prefix + "mHidden==" + mHidden); pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey); Loading Loading @@ -564,11 +568,11 @@ public final class NotificationRecord { public final String toString() { return String.format( "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" + ": %s)", "appImportanceLocked=%s: %s)", System.identityHashCode(this), this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(), this.sbn.getTag(), this.mImportance, this.sbn.getKey(), this.sbn.getNotification()); mIsAppImportanceLocked, this.sbn.getNotification()); } public void addAdjustment(Adjustment adjustment) { Loading Loading @@ -600,7 +604,8 @@ public final class NotificationRecord { if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) { // Only allow user sentiment update from assistant if user hasn't already // expressed a preference for this channel if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) { if (!mIsAppImportanceLocked && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) { setUserSentiment(adjustment.getSignals().getInt( Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL)); } Loading @@ -609,6 +614,11 @@ public final class NotificationRecord { } } public void setIsAppImportanceLocked(boolean isAppImportanceLocked) { mIsAppImportanceLocked = isAppImportanceLocked; calculateUserSentiment(); } public void setContactAffinity(float contactAffinity) { mContactAffinity = contactAffinity; if (mImportance < IMPORTANCE_DEFAULT && Loading Loading @@ -870,6 +880,13 @@ public final class NotificationRecord { return mChannel; } /** * @see RankingHelper#getIsAppImportanceLocked(String, int) */ public boolean getIsAppImportanceLocked() { return mIsAppImportanceLocked; } protected void updateNotificationChannel(NotificationChannel channel) { if (channel != null) { mChannel = channel; Loading Loading @@ -927,7 +944,8 @@ public final class NotificationRecord { } private void calculateUserSentiment() { if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0) { if ((getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0 || mIsAppImportanceLocked) { mUserSentiment = USER_SENTIMENT_POSITIVE; } } Loading