Loading core/java/android/app/NotificationChannel.java +25 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,7 @@ public final class NotificationChannel implements Parcelable { private static final String ATT_CONTENT_TYPE = "content_type"; private static final String ATT_SHOW_BADGE = "show_badge"; private static final String ATT_USER_LOCKED = "locked"; private static final String ATT_FG_SERVICE_SHOWN = "fgservice"; private static final String ATT_GROUP = "group"; private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system"; private static final String DELIMITER = ","; Loading Loading @@ -144,6 +145,7 @@ public final class NotificationChannel implements Parcelable { // Bitwise representation of fields that have been changed by the user, preventing the app from // making changes to these fields. private int mUserLockedFields; private boolean mFgServiceShown; private boolean mVibrationEnabled; private boolean mShowBadge = DEFAULT_SHOW_BADGE; private boolean mDeleted = DEFAULT_DELETED; Loading Loading @@ -200,6 +202,7 @@ public final class NotificationChannel implements Parcelable { mLights = in.readByte() != 0; mVibration = in.createLongArray(); mUserLockedFields = in.readInt(); mFgServiceShown = in.readByte() != 0; mVibrationEnabled = in.readByte() != 0; mShowBadge = in.readByte() != 0; mDeleted = in.readByte() != 0; Loading Loading @@ -245,6 +248,7 @@ public final class NotificationChannel implements Parcelable { dest.writeByte(mLights ? (byte) 1 : (byte) 0); dest.writeLongArray(mVibration); dest.writeInt(mUserLockedFields); dest.writeByte(mFgServiceShown ? (byte) 1 : (byte) 0); dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0); dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0); dest.writeByte(mDeleted ? (byte) 1 : (byte) 0); Loading Loading @@ -278,6 +282,13 @@ public final class NotificationChannel implements Parcelable { mUserLockedFields &= ~field; } /** * @hide */ public void setFgServiceShown(boolean shown) { mFgServiceShown = shown; } /** * @hide */ Loading Loading @@ -573,6 +584,13 @@ public final class NotificationChannel implements Parcelable { return mUserLockedFields; } /** * @hide */ public boolean isFgServiceShown() { return mFgServiceShown; } /** * @hide */ Loading Loading @@ -620,6 +638,7 @@ public final class NotificationChannel implements Parcelable { setDeleted(safeBool(parser, ATT_DELETED, false)); setGroup(parser.getAttributeValue(null, ATT_GROUP)); lockFields(safeInt(parser, ATT_USER_LOCKED, 0)); setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false)); setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false)); } Loading Loading @@ -724,6 +743,9 @@ public final class NotificationChannel implements Parcelable { if (getUserLockedFields() != 0) { out.attribute(null, ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); } if (isFgServiceShown()) { out.attribute(null, ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown())); } if (canShowBadge()) { out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); } Loading Loading @@ -772,6 +794,7 @@ public final class NotificationChannel implements Parcelable { record.put(ATT_LIGHT_COLOR, Integer.toString(getLightColor())); record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate())); record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown())); record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern())); record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); record.put(ATT_DELETED, Boolean.toString(isDeleted())); Loading Loading @@ -933,6 +956,7 @@ public final class NotificationChannel implements Parcelable { + ", mLightColor=" + mLightColor + ", mVibration=" + Arrays.toString(mVibration) + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields) + ", mFgServiceShown=" + mFgServiceShown + ", mVibrationEnabled=" + mVibrationEnabled + ", mShowBadge=" + mShowBadge + ", mDeleted=" + mDeleted Loading Loading @@ -963,6 +987,7 @@ public final class NotificationChannel implements Parcelable { } } proto.write(NotificationChannelProto.USER_LOCKED_FIELDS, mUserLockedFields); proto.write(NotificationChannelProto.FG_SERVICE_SHOWN, mFgServiceShown); proto.write(NotificationChannelProto.IS_VIBRATION_ENABLED, mVibrationEnabled); proto.write(NotificationChannelProto.SHOW_BADGE, mShowBadge); proto.write(NotificationChannelProto.IS_DELETED, mDeleted); Loading core/proto/android/app/notification_channel.proto +1 −0 Original line number Diff line number Diff line Loading @@ -53,4 +53,5 @@ message NotificationChannelProto { optional android.media.AudioAttributesProto audio_attributes = 16; // If this is a blockable system notification channel. optional bool is_blockable_system = 17; optional bool fg_service_shown = 18; } services/core/java/com/android/server/notification/NotificationManagerService.java +23 −11 Original line number Diff line number Diff line Loading @@ -4051,19 +4051,31 @@ public class NotificationManagerService extends SystemService { final NotificationRecord r = new NotificationRecord(getContext(), n, channel); r.setIsAppImportanceLocked(mRankingHelper.getIsAppImportanceLocked(pkg, callingUid)); if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0 && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0 && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) { // Increase the importance of foreground service notifications unless the user had an // opinion otherwise if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { final boolean fgServiceShown = channel.isFgServiceShown(); if (((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0 || !fgServiceShown) && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) { // Increase the importance of foreground service notifications unless the user had // an opinion otherwise (and the channel hasn't yet shown a fg service). if (TextUtils.isEmpty(channelId) || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) { r.setImportance(IMPORTANCE_LOW, "Bumped for foreground service"); } else { channel.setImportance(IMPORTANCE_LOW); if (!fgServiceShown) { channel.unlockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); channel.setFgServiceShown(true); } mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false); r.updateNotificationChannel(channel); } } else if (!fgServiceShown && !TextUtils.isEmpty(channelId) && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) { channel.setFgServiceShown(true); r.updateNotificationChannel(channel); } } if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r, Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +24 −1 Original line number Diff line number Diff line Loading @@ -557,11 +557,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(IMPORTANCE_NONE, mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance()); final StatusBarNotification sbn = generateNotificationRecord(channel).sbn; StatusBarNotification sbn = generateNotificationRecord(channel).sbn; sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE; mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", sbn.getId(), sbn.getNotification(), sbn.getUserId()); waitForIdle(); // The first time a foreground service notification is shown, we allow the channel // to be updated to allow it to be seen. assertEquals(1, mBinderService.getActiveNotifications(sbn.getPackageName()).length); assertEquals(IMPORTANCE_LOW, mService.getNotificationRecord(sbn.getKey()).getImportance()); assertEquals(IMPORTANCE_LOW, mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance()); mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId()); waitForIdle(); update = new NotificationChannel("blockedbyuser", "name", IMPORTANCE_NONE); update.setFgServiceShown(true); mBinderService.updateNotificationChannelForPackage(PKG, mUid, update); waitForIdle(); assertEquals(IMPORTANCE_NONE, mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance()); sbn = generateNotificationRecord(channel).sbn; sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE; mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", sbn.getId(), sbn.getNotification(), sbn.getUserId()); waitForIdle(); // The second time it is shown, we keep the user's preference. assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length); assertNull(mService.getNotificationRecord(sbn.getKey())); assertEquals(IMPORTANCE_NONE, Loading Loading
core/java/android/app/NotificationChannel.java +25 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,7 @@ public final class NotificationChannel implements Parcelable { private static final String ATT_CONTENT_TYPE = "content_type"; private static final String ATT_SHOW_BADGE = "show_badge"; private static final String ATT_USER_LOCKED = "locked"; private static final String ATT_FG_SERVICE_SHOWN = "fgservice"; private static final String ATT_GROUP = "group"; private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system"; private static final String DELIMITER = ","; Loading Loading @@ -144,6 +145,7 @@ public final class NotificationChannel implements Parcelable { // Bitwise representation of fields that have been changed by the user, preventing the app from // making changes to these fields. private int mUserLockedFields; private boolean mFgServiceShown; private boolean mVibrationEnabled; private boolean mShowBadge = DEFAULT_SHOW_BADGE; private boolean mDeleted = DEFAULT_DELETED; Loading Loading @@ -200,6 +202,7 @@ public final class NotificationChannel implements Parcelable { mLights = in.readByte() != 0; mVibration = in.createLongArray(); mUserLockedFields = in.readInt(); mFgServiceShown = in.readByte() != 0; mVibrationEnabled = in.readByte() != 0; mShowBadge = in.readByte() != 0; mDeleted = in.readByte() != 0; Loading Loading @@ -245,6 +248,7 @@ public final class NotificationChannel implements Parcelable { dest.writeByte(mLights ? (byte) 1 : (byte) 0); dest.writeLongArray(mVibration); dest.writeInt(mUserLockedFields); dest.writeByte(mFgServiceShown ? (byte) 1 : (byte) 0); dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0); dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0); dest.writeByte(mDeleted ? (byte) 1 : (byte) 0); Loading Loading @@ -278,6 +282,13 @@ public final class NotificationChannel implements Parcelable { mUserLockedFields &= ~field; } /** * @hide */ public void setFgServiceShown(boolean shown) { mFgServiceShown = shown; } /** * @hide */ Loading Loading @@ -573,6 +584,13 @@ public final class NotificationChannel implements Parcelable { return mUserLockedFields; } /** * @hide */ public boolean isFgServiceShown() { return mFgServiceShown; } /** * @hide */ Loading Loading @@ -620,6 +638,7 @@ public final class NotificationChannel implements Parcelable { setDeleted(safeBool(parser, ATT_DELETED, false)); setGroup(parser.getAttributeValue(null, ATT_GROUP)); lockFields(safeInt(parser, ATT_USER_LOCKED, 0)); setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false)); setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false)); } Loading Loading @@ -724,6 +743,9 @@ public final class NotificationChannel implements Parcelable { if (getUserLockedFields() != 0) { out.attribute(null, ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); } if (isFgServiceShown()) { out.attribute(null, ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown())); } if (canShowBadge()) { out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); } Loading Loading @@ -772,6 +794,7 @@ public final class NotificationChannel implements Parcelable { record.put(ATT_LIGHT_COLOR, Integer.toString(getLightColor())); record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate())); record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown())); record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern())); record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); record.put(ATT_DELETED, Boolean.toString(isDeleted())); Loading Loading @@ -933,6 +956,7 @@ public final class NotificationChannel implements Parcelable { + ", mLightColor=" + mLightColor + ", mVibration=" + Arrays.toString(mVibration) + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields) + ", mFgServiceShown=" + mFgServiceShown + ", mVibrationEnabled=" + mVibrationEnabled + ", mShowBadge=" + mShowBadge + ", mDeleted=" + mDeleted Loading Loading @@ -963,6 +987,7 @@ public final class NotificationChannel implements Parcelable { } } proto.write(NotificationChannelProto.USER_LOCKED_FIELDS, mUserLockedFields); proto.write(NotificationChannelProto.FG_SERVICE_SHOWN, mFgServiceShown); proto.write(NotificationChannelProto.IS_VIBRATION_ENABLED, mVibrationEnabled); proto.write(NotificationChannelProto.SHOW_BADGE, mShowBadge); proto.write(NotificationChannelProto.IS_DELETED, mDeleted); Loading
core/proto/android/app/notification_channel.proto +1 −0 Original line number Diff line number Diff line Loading @@ -53,4 +53,5 @@ message NotificationChannelProto { optional android.media.AudioAttributesProto audio_attributes = 16; // If this is a blockable system notification channel. optional bool is_blockable_system = 17; optional bool fg_service_shown = 18; }
services/core/java/com/android/server/notification/NotificationManagerService.java +23 −11 Original line number Diff line number Diff line Loading @@ -4051,19 +4051,31 @@ public class NotificationManagerService extends SystemService { final NotificationRecord r = new NotificationRecord(getContext(), n, channel); r.setIsAppImportanceLocked(mRankingHelper.getIsAppImportanceLocked(pkg, callingUid)); if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0 && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0 && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) { // Increase the importance of foreground service notifications unless the user had an // opinion otherwise if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { final boolean fgServiceShown = channel.isFgServiceShown(); if (((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0 || !fgServiceShown) && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) { // Increase the importance of foreground service notifications unless the user had // an opinion otherwise (and the channel hasn't yet shown a fg service). if (TextUtils.isEmpty(channelId) || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) { r.setImportance(IMPORTANCE_LOW, "Bumped for foreground service"); } else { channel.setImportance(IMPORTANCE_LOW); if (!fgServiceShown) { channel.unlockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); channel.setFgServiceShown(true); } mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false); r.updateNotificationChannel(channel); } } else if (!fgServiceShown && !TextUtils.isEmpty(channelId) && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) { channel.setFgServiceShown(true); r.updateNotificationChannel(channel); } } if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r, Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +24 −1 Original line number Diff line number Diff line Loading @@ -557,11 +557,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(IMPORTANCE_NONE, mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance()); final StatusBarNotification sbn = generateNotificationRecord(channel).sbn; StatusBarNotification sbn = generateNotificationRecord(channel).sbn; sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE; mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", sbn.getId(), sbn.getNotification(), sbn.getUserId()); waitForIdle(); // The first time a foreground service notification is shown, we allow the channel // to be updated to allow it to be seen. assertEquals(1, mBinderService.getActiveNotifications(sbn.getPackageName()).length); assertEquals(IMPORTANCE_LOW, mService.getNotificationRecord(sbn.getKey()).getImportance()); assertEquals(IMPORTANCE_LOW, mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance()); mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId()); waitForIdle(); update = new NotificationChannel("blockedbyuser", "name", IMPORTANCE_NONE); update.setFgServiceShown(true); mBinderService.updateNotificationChannelForPackage(PKG, mUid, update); waitForIdle(); assertEquals(IMPORTANCE_NONE, mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance()); sbn = generateNotificationRecord(channel).sbn; sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE; mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", sbn.getId(), sbn.getNotification(), sbn.getUserId()); waitForIdle(); // The second time it is shown, we keep the user's preference. assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length); assertNull(mService.getNotificationRecord(sbn.getKey())); assertEquals(IMPORTANCE_NONE, Loading