Add updatable fields to Ranking

- NotificationChannel
- Badging

Test: runtest systemuinotification & cts

Change-Id: I7fd1f2dc06148927e9a4bd5b760d436e2c5e8a98
diff --git a/api/current.txt b/api/current.txt
index cd66ed6..0fce726 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35788,6 +35788,7 @@
 
   public static class NotificationListenerService.Ranking {
     ctor public NotificationListenerService.Ranking();
+    method public boolean canShowBadge();
     method public java.util.List<java.lang.String> getAdditionalPeople();
     method public android.app.NotificationChannel getChannel();
     method public int getImportance();
@@ -35829,7 +35830,6 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
-    method public android.app.NotificationChannel getNotificationChannel();
     method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
diff --git a/api/system-current.txt b/api/system-current.txt
index a3deab0..e41a845 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -38733,6 +38733,7 @@
 
   public static class NotificationListenerService.Ranking {
     ctor public NotificationListenerService.Ranking();
+    method public boolean canShowBadge();
     method public java.util.List<java.lang.String> getAdditionalPeople();
     method public android.app.NotificationChannel getChannel();
     method public int getImportance();
@@ -38774,7 +38775,6 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
-    method public android.app.NotificationChannel getNotificationChannel();
     method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
diff --git a/api/test-current.txt b/api/test-current.txt
index f07f3b4..a152e49 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -35909,6 +35909,7 @@
 
   public static class NotificationListenerService.Ranking {
     ctor public NotificationListenerService.Ranking();
+    method public boolean canShowBadge();
     method public java.util.List<java.lang.String> getAdditionalPeople();
     method public android.app.NotificationChannel getChannel();
     method public int getImportance();
@@ -35950,7 +35951,6 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
-    method public android.app.NotificationChannel getNotificationChannel();
     method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index f909af0..d674bfe 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -47,6 +47,8 @@
             in Notification notification, inout int[] idReceived, int userId);
     void cancelNotificationWithTag(String pkg, String tag, int id, int userId);
 
+    void setShowBadge(String pkg, int uid, boolean showBadge);
+    boolean canShowBadge(String pkg, int uid);
     void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
     boolean areNotificationsEnabledForPackage(String pkg, int uid);
     boolean areNotificationsEnabled(String pkg);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 56ef791..be5f80a 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -122,6 +122,7 @@
     private static final int DEFAULT_IMPORTANCE =
             NotificationManager.IMPORTANCE_UNSPECIFIED;
     private static final boolean DEFAULT_DELETED = false;
+    private static final boolean DEFAULT_SHOW_BADGE = true;
 
     private final String mId;
     private CharSequence mName;
@@ -133,7 +134,7 @@
     private long[] mVibration;
     private int mUserLockedFields;
     private boolean mVibrationEnabled;
-    private boolean mShowBadge;
+    private boolean mShowBadge = DEFAULT_SHOW_BADGE;
     private boolean mDeleted = DEFAULT_DELETED;
 
     /**
@@ -368,6 +369,8 @@
     /**
      * Returns whether notifications posted to this channel can appear as badges in a Launcher
      * application.
+     *
+     * Note that badging may be disabled for other reasons.
      */
     public boolean canShowBadge() {
         return mShowBadge;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 694837e..d930689 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1166,11 +1166,12 @@
         // System specified group key.
         private String mOverrideGroupKey;
         // Notification assistant channel override.
-        private NotificationChannel mOverrideChannel;
+        private NotificationChannel mChannel;
         // Notification assistant people override.
         private ArrayList<String> mOverridePeople;
         // Notification assistant snooze criteria.
         private ArrayList<SnoozeCriterion> mSnoozeCriteria;
+        private boolean mShowBadge;
 
         public Ranking() {}
 
@@ -1200,7 +1201,7 @@
         }
 
         /**
-         * Returns the user specificed visibility for the package that posted
+         * Returns the user specified visibility for the package that posted
          * this notification, or
          * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if
          * no such preference has been expressed.
@@ -1233,7 +1234,7 @@
          * Returns the importance of the notification, which dictates its
          * modes of presentation, see: {@link NotificationManager#IMPORTANCE_DEFAULT}, etc.
          *
-         * @return the rank of the notification
+         * @return the importance of the notification
          */
         public @NotificationManager.Importance int getImportance() {
             return mImportance;
@@ -1258,12 +1259,11 @@
         }
 
         /**
-         * If the {@link NotificationAssistantService} has overridden the channel this notification
-         * was posted to, then this will not match the channel provided by the posting application
-         * and this should be used to determine the interruptiveness of the notification instead.
+         * Returns the notification channel this notification was posted to, which dictates
+         * notification behavior and presentation.
          */
         public NotificationChannel getChannel() {
-            return mOverrideChannel;
+            return mChannel;
         }
 
         /**
@@ -1283,11 +1283,20 @@
             return mSnoozeCriteria;
         }
 
+        /**
+         * Returns whether this notification can be displayed as a badge.
+         *
+         * @return true if the notification can be displayed as a badge, false otherwise.
+         */
+        public boolean canShowBadge() {
+            return mShowBadge;
+        }
+
         private void populate(String key, int rank, boolean matchesInterruptionFilter,
                 int visibilityOverride, int suppressedVisualEffects, int importance,
                 CharSequence explanation, String overrideGroupKey,
-                NotificationChannel overrideChannel, ArrayList<String> overridePeople,
-                ArrayList<SnoozeCriterion> snoozeCriteria) {
+                NotificationChannel channel, ArrayList<String> overridePeople,
+                ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1297,9 +1306,10 @@
             mImportance = importance;
             mImportanceExplanation = explanation;
             mOverrideGroupKey = overrideGroupKey;
-            mOverrideChannel = overrideChannel;
+            mChannel = channel;
             mOverridePeople = overridePeople;
             mSnoozeCriteria = snoozeCriteria;
+            mShowBadge = showBadge;
         }
 
         /**
@@ -1343,9 +1353,10 @@
         private ArrayMap<String, Integer> mImportance;
         private ArrayMap<String, String> mImportanceExplanation;
         private ArrayMap<String, String> mOverrideGroupKeys;
-        private ArrayMap<String, NotificationChannel> mOverrideChannels;
+        private ArrayMap<String, NotificationChannel> mChannels;
         private ArrayMap<String, ArrayList<String>> mOverridePeople;
         private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
+        private ArrayMap<String, Boolean> mShowBadge;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1373,7 +1384,8 @@
             outRanking.populate(key, rank, !isIntercepted(key),
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
                     getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
-                    getOverrideChannel(key), getOverridePeople(key), getSnoozeCriteria(key));
+                    getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
+                    getShowBadge(key));
             return rank >= 0;
         }
 
@@ -1453,13 +1465,13 @@
             return mOverrideGroupKeys.get(key);
         }
 
-        private NotificationChannel getOverrideChannel(String key) {
+        private NotificationChannel getChannel(String key) {
             synchronized (this) {
-                if (mOverrideChannels == null) {
-                    buildOverrideChannelsLocked();
+                if (mChannels == null) {
+                    buildChannelsLocked();
                 }
             }
-            return mOverrideChannels.get(key);
+            return mChannels.get(key);
         }
 
         private ArrayList<String> getOverridePeople(String key) {
@@ -1480,6 +1492,16 @@
             return mSnoozeCriteria.get(key);
         }
 
+        private boolean getShowBadge(String key) {
+            synchronized (this) {
+                if (mShowBadge == null) {
+                    buildShowBadgeLocked();
+                }
+            }
+            Boolean showBadge = mShowBadge.get(key);
+            return showBadge == null ? false : showBadge.booleanValue();
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1544,11 +1566,11 @@
         }
 
         // Locked by 'this'
-        private void buildOverrideChannelsLocked() {
-            Bundle overrideChannels = mRankingUpdate.getOverrideChannels();
-            mOverrideChannels = new ArrayMap<>(overrideChannels.size());
-            for (String key : overrideChannels.keySet()) {
-                mOverrideChannels.put(key, overrideChannels.getParcelable(key));
+        private void buildChannelsLocked() {
+            Bundle channels = mRankingUpdate.getChannels();
+            mChannels = new ArrayMap<>(channels.size());
+            for (String key : channels.keySet()) {
+                mChannels.put(key, channels.getParcelable(key));
             }
         }
 
@@ -1570,6 +1592,15 @@
             }
         }
 
+        // Locked by 'this'
+        private void buildShowBadgeLocked() {
+            Bundle showBadge = mRankingUpdate.getShowBadge();
+            mShowBadge = new ArrayMap<>(showBadge.size());
+            for (String key : showBadge.keySet()) {
+                mShowBadge.put(key, showBadge.getBoolean(key));
+            }
+        }
+
         // ----------- Parcelable
 
         @Override
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index a2cdeff..326b212 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -31,14 +31,16 @@
     private final int[] mImportance;
     private final Bundle mImportanceExplanation;
     private final Bundle mOverrideGroupKeys;
-    private final Bundle mOverrideChannels;
+    private final Bundle mChannels;
     private final Bundle mOverridePeople;
     private final Bundle mSnoozeCriteria;
+    private final Bundle mShowBadge;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
             int[] importance, Bundle explanation, Bundle overrideGroupKeys,
-            Bundle overrideChannels, Bundle overridePeople, Bundle snoozeCriteria) {
+            Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
+            Bundle showBadge) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
@@ -46,9 +48,10 @@
         mImportance = importance;
         mImportanceExplanation = explanation;
         mOverrideGroupKeys = overrideGroupKeys;
-        mOverrideChannels = overrideChannels;
+        mChannels = channels;
         mOverridePeople = overridePeople;
         mSnoozeCriteria = snoozeCriteria;
+        mShowBadge = showBadge;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -60,9 +63,10 @@
         in.readIntArray(mImportance);
         mImportanceExplanation = in.readBundle();
         mOverrideGroupKeys = in.readBundle();
-        mOverrideChannels = in.readBundle();
+        mChannels = in.readBundle();
         mOverridePeople = in.readBundle();
         mSnoozeCriteria = in.readBundle();
+        mShowBadge = in.readBundle();
     }
 
     @Override
@@ -79,9 +83,10 @@
         out.writeIntArray(mImportance);
         out.writeBundle(mImportanceExplanation);
         out.writeBundle(mOverrideGroupKeys);
-        out.writeBundle(mOverrideChannels);
+        out.writeBundle(mChannels);
         out.writeBundle(mOverridePeople);
         out.writeBundle(mSnoozeCriteria);
+        out.writeBundle(mShowBadge);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -123,8 +128,8 @@
         return mOverrideGroupKeys;
     }
 
-    public Bundle getOverrideChannels() {
-        return mOverrideChannels;
+    public Bundle getChannels() {
+        return mChannels;
     }
 
     public Bundle getOverridePeople() {
@@ -134,4 +139,8 @@
     public Bundle getSnoozeCriteria() {
         return mSnoozeCriteria;
     }
+
+    public Bundle getShowBadge() {
+        return mShowBadge;
+    }
 }
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 6276af3..85baf4e 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -43,21 +43,18 @@
     private final Notification notification;
     private final UserHandle user;
     private final long postTime;
-    private final NotificationChannel channel;
 
     private Context mContext; // used for inflation & icon expansion
 
     /** @hide */
-    public StatusBarNotification(String pkg, String opPkg, NotificationChannel channel, int id,
+    public StatusBarNotification(String pkg, String opPkg, int id,
             String tag, int uid, int initialPid, Notification notification, UserHandle user,
             String overrideGroupKey, long postTime) {
         if (pkg == null) throw new NullPointerException();
         if (notification == null) throw new NullPointerException();
-        if (channel == null) throw new IllegalArgumentException();
 
         this.pkg = pkg;
         this.opPkg = opPkg;
-        this.channel = channel;
         this.id = id;
         this.tag = tag;
         this.uid = uid;
@@ -88,7 +85,6 @@
         this.postTime = postTime;
         this.key = key();
         this.groupKey = groupKey();
-        this.channel = null;
     }
 
     public StatusBarNotification(Parcel in) {
@@ -112,7 +108,6 @@
         }
         this.key = key();
         this.groupKey = groupKey();
-        this.channel = NotificationChannel.CREATOR.createFromParcel(in);
     }
 
     private String key() {
@@ -182,7 +177,6 @@
         } else {
             out.writeInt(0);
         }
-        this.channel.writeToParcel(out, flags);
     }
 
     public int describeContents() {
@@ -209,14 +203,14 @@
     public StatusBarNotification cloneLight() {
         final Notification no = new Notification();
         this.notification.cloneInto(no, false); // light copy
-        return new StatusBarNotification(this.pkg, this.opPkg, this.channel,
+        return new StatusBarNotification(this.pkg, this.opPkg,
                 this.id, this.tag, this.uid, this.initialPid,
                 no, this.user, this.overrideGroupKey, this.postTime);
     }
 
     @Override
     public StatusBarNotification clone() {
-        return new StatusBarNotification(this.pkg, this.opPkg, this.channel,
+        return new StatusBarNotification(this.pkg, this.opPkg,
                 this.id, this.tag, this.uid, this.initialPid,
                 this.notification.clone(), this.user, this.overrideGroupKey, this.postTime);
     }
@@ -336,13 +330,6 @@
     }
 
     /**
-     * Returns the channel this notification was posted to.
-     */
-    public NotificationChannel getNotificationChannel() {
-        return channel;
-    }
-
-    /**
      * @hide
      */
     public Context getPackageContext(Context context) {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7de48d3..c36279c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2090,6 +2090,7 @@
         <item>com.android.server.notification.ImportanceExtractor</item>
         <item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
         <item>com.android.server.notification.VisibilityExtractor</item>
+        <item>com.android.server.notification.BadgeExtractor</item>
     </string-array>
 
     <!-- Flag indicating that this device does not rotate and will always remain in its default
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 6ac5cb8..49d2462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -24,6 +24,7 @@
 import android.app.INotificationManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
@@ -1038,6 +1039,7 @@
     private void bindGuts(final ExpandableNotificationRow row) {
         row.inflateGuts();
         final StatusBarNotification sbn = row.getStatusBarNotification();
+        final NotificationChannel channel = row.getEntry().channel;
         PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
         row.setTag(sbn.getPackageName());
         final NotificationGuts guts = row.getGuts();
@@ -1077,8 +1079,8 @@
                         closeControls(row, guts, v);
                     }
                 };
-        guts.bindNotification(pmUser, iNotificationManager, sbn, onSettingsClick, onDoneClick,
-                mNonBlockablePkgs);
+        guts.bindNotification(pmUser, iNotificationManager, sbn, channel,
+                onSettingsClick, onDoneClick, mNonBlockablePkgs);
     }
 
     private void closeControls(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 458daf1..3a89186 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar;
 
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.Context;
 import android.graphics.drawable.Icon;
@@ -55,6 +56,7 @@
         private static final int COLOR_INVALID = 1;
         public String key;
         public StatusBarNotification notification;
+        public NotificationChannel channel;
         public StatusBarIconView icon;
         public StatusBarIconView expandedIcon;
         public ExpandableNotificationRow row; // the outer expanded view
@@ -429,6 +431,14 @@
          return null;
     }
 
+    public NotificationChannel getChannel(String key) {
+        if (mRankingMap != null) {
+            mRankingMap.getRanking(key, mTmpRanking);
+            return mTmpRanking.getChannel();
+        }
+        return null;
+    }
+
     private void updateRankingAndSort(RankingMap ranking) {
         if (ranking != null) {
             mRankingMap = ranking;
@@ -442,6 +452,7 @@
                         entry.notification.setOverrideGroupKey(overrideGroupKey);
                         mGroupManager.onEntryUpdated(entry, oldSbn);
                     }
+                    entry.channel = getChannel(entry.key);
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index c7adb60..83104e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -72,11 +72,7 @@
     private INotificationManager mINotificationManager;
     private int mStartingUserImportance;
     private StatusBarNotification mStatusBarNotification;
-
-    private ImageView mAutoButton;
-    private TextView mImportanceSummary;
-    private TextView mImportanceTitle;
-    private boolean mAuto;
+    private NotificationChannel mNotificationChannel;
 
     private View mImportanceGroup;
     private View mChannelDisabled;
@@ -170,11 +166,12 @@
     }
 
     void bindNotification(final PackageManager pm, final INotificationManager iNotificationManager,
-            final StatusBarNotification sbn, OnSettingsClickListener onSettingsClick,
+            final StatusBarNotification sbn, final NotificationChannel channel,
+            OnSettingsClickListener onSettingsClick,
             OnClickListener onDoneClick, final Set<String> nonBlockablePkgs) {
         mINotificationManager = iNotificationManager;
+        mNotificationChannel = channel;
         mStatusBarNotification = sbn;
-        final NotificationChannel channel = sbn.getNotificationChannel();
         mStartingUserImportance = channel.getImportance();
 
         final String pkg = sbn.getPackageName();
@@ -288,14 +285,13 @@
         if (selectedImportance == mStartingUserImportance) {
             return;
         }
-        final NotificationChannel channel = mStatusBarNotification.getNotificationChannel();
         MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
                 selectedImportance - mStartingUserImportance);
-        channel.setImportance(selectedImportance);
+        mNotificationChannel.setImportance(selectedImportance);
         try {
             mINotificationManager.updateNotificationChannelForPackage(
                     mStatusBarNotification.getPackageName(), mStatusBarNotification.getUid(),
-                    channel);
+                    mNotificationChannel);
         } catch (RemoteException e) {
             // :(
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 3291d59..9612db0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1388,7 +1388,7 @@
             newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
 
             StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
-                    sbn.getOpPkg(), sbn.getNotificationChannel(),
+                    sbn.getOpPkg(),
                     sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
                     newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java
index c65f7150..cac0806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java
@@ -91,7 +91,6 @@
         // mMockStatusBarNotification with a test channel.
         mNotificationChannel = new NotificationChannel(
                 TEST_CHANNEL, TEST_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
-        when(mMockStatusBarNotification.getNotificationChannel()).thenReturn(mNotificationChannel);
         when(mMockStatusBarNotification.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
     }
 
@@ -100,7 +99,7 @@
     public void testBindNotification_SetsTextApplicationName() throws Exception {
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
         final TextView textView = (TextView) mNotificationGuts.findViewById(R.id.pkgname);
         assertTrue(textView.getText().toString().contains("App Name"));
     }
@@ -109,7 +108,7 @@
     @UiThreadTest
     public void testBindNotification_SetsTextChannelName() throws Exception {
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
         final TextView textView = (TextView) mNotificationGuts.findViewById(R.id.channel_name);
         assertEquals(TEST_CHANNEL_NAME, textView.getText());
     }
@@ -119,8 +118,8 @@
     public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, (View v, int appUid) -> { latch.countDown(); },
-                null, null);
+                mMockStatusBarNotification, mNotificationChannel,
+                (View v, int appUid) -> { latch.countDown(); }, null, null);
 
         final TextView settingsButton =
                 (TextView) mNotificationGuts.findViewById(R.id.more_settings);
@@ -134,7 +133,7 @@
     public void testBindNotification_SetsOnClickListenerForDone() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null,
+                mMockStatusBarNotification, mNotificationChannel, null,
                 (View v) -> { latch.countDown(); },
                 null);
 
@@ -148,7 +147,7 @@
     @UiThreadTest
     public void testHasImportanceChanged_DefaultsToFalse() throws Exception {
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
         assertFalse(mNotificationGuts.hasImportanceChanged());
     }
 
@@ -157,7 +156,7 @@
     public void testHasImportanceChanged_ReturnsTrueAfterButtonChecked() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
         // Find the high button and check it.
         RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
         highButton.setChecked(true);
@@ -169,7 +168,7 @@
     public void testImportanceButtonCheckedBasedOnInitialImportance() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_HIGH);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
         RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
         assertTrue(highButton.isChecked());
@@ -179,7 +178,7 @@
     @UiThreadTest
     public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), anyInt(), any());
     }
@@ -189,7 +188,7 @@
     public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
         RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
         highButton.setChecked(true);
@@ -201,7 +200,7 @@
     @UiThreadTest
     public void testCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception {
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
         mNotificationGuts.closeControls(-1, -1, true);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -213,7 +212,7 @@
     public void testCloseControls_DoesNotUpdateNotificationChannelIfUnspecified() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
         mNotificationGuts.closeControls(-1, -1, true);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -225,7 +224,7 @@
     public void testCloseControls_CallsUpdateNotificationChannelIfChanged() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
         RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
         highButton.setChecked(true);
@@ -240,7 +239,7 @@
     public void testCloseControls_DoesNotUpdateNotificationChannelIfSaveFalse() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
         RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
         highButton.setChecked(true);
@@ -254,7 +253,7 @@
     public void testEnabledSwitchOnByDefault() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
         Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
         assertTrue(enabledSwitch.isChecked());
@@ -265,7 +264,7 @@
     public void testEnabledSwitchVisibleByDefault() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
         Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
         assertEquals(View.VISIBLE, enabledSwitch.getVisibility());
@@ -276,7 +275,8 @@
     public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, Collections.singleton(TEST_PACKAGE_NAME));
+                mMockStatusBarNotification, mNotificationChannel, null, null,
+                Collections.singleton(TEST_PACKAGE_NAME));
 
         Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
         assertEquals(View.INVISIBLE, enabledSwitch.getVisibility());
@@ -287,7 +287,8 @@
     public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, Collections.singleton(TEST_PACKAGE_NAME));
+                mMockStatusBarNotification, mNotificationChannel, null, null,
+                Collections.singleton(TEST_PACKAGE_NAME));
 
         Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
         enabledSwitch.setChecked(false);
@@ -301,7 +302,7 @@
     public void testEnabledSwitchOverridesOtherButtons() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
         Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
         RadioButton lowButton = (RadioButton) mNotificationGuts.findViewById(R.id.low_importance);
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
new file mode 100644
index 0000000..4795fbf
--- /dev/null
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -0,0 +1,59 @@
+/**
+* Copyright (C) 2017 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.android.server.notification;
+
+import android.content.Context;
+import android.util.Slog;
+
+/**
+ * Determines whether a badge should be shown for this notification
+ */
+public class BadgeExtractor implements NotificationSignalExtractor {
+    private static final String TAG = "BadgeExtractor";
+    private static final boolean DBG = false;
+
+    private RankingConfig mConfig;
+
+    public void initialize(Context ctx, NotificationUsageStats usageStats) {
+        if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
+    }
+
+    public RankingReconsideration process(NotificationRecord record) {
+        if (record == null || record.getNotification() == null) {
+            if (DBG) Slog.d(TAG, "skipping empty notification");
+            return null;
+        }
+
+        if (mConfig == null) {
+            if (DBG) Slog.d(TAG, "missing config");
+            return null;
+        }
+        boolean appCanShowBadge =
+                mConfig.canShowBadge(record.sbn.getPackageName(), record.sbn.getUid());
+        if (!appCanShowBadge) {
+            record.setShowBadge(false);
+        } else {
+            record.setShowBadge(record.getChannel().canShowBadge() && appCanShowBadge);
+        }
+
+        return null;
+    }
+
+    @Override
+    public void setConfig(RankingConfig config) {
+        mConfig = config;
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 168884d..96459be 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1243,6 +1243,35 @@
         sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
     }
 
+    private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
+            boolean fromAssistant) {
+        if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
+            // cancel
+            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
+                    UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
+                    null);
+        }
+        if (fromAssistant) {
+            mRankingHelper.updateNotificationChannelFromAssistant(pkg, uid, channel);
+        } else {
+            mRankingHelper.updateNotificationChannel(pkg, uid, channel);
+        }
+
+        synchronized (mNotificationList) {
+            final int N = mNotificationList.size();
+            for (int i = N - 1; i >= 0; --i) {
+                NotificationRecord r = mNotificationList.get(i);
+                if (channel.getId() != null && channel.getId().equals(r.getChannel().getId())) {
+                    r.updateNotificationChannel(mRankingHelper.getNotificationChannel(
+                            r.sbn.getPackageName(), r.getUser().getIdentifier(),
+                            channel.getId(), false));
+                }
+            }
+        }
+        mRankingHandler.requestSort(true);
+        savePolicyFile();
+    }
+
     private ArrayList<ComponentName> getSuppressors() {
         ArrayList<ComponentName> names = new ArrayList<ComponentName>();
         for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
@@ -1521,6 +1550,19 @@
         }
 
         @Override
+        public boolean canShowBadge(String pkg, int uid) {
+            checkCallerIsSystem();
+            return mRankingHelper.canShowBadge(pkg, uid);
+        }
+
+        @Override
+        public void setShowBadge(String pkg, int uid, boolean showBadge) {
+            checkCallerIsSystem();
+            mRankingHelper.setShowBadge(pkg, uid, showBadge);
+            savePolicyFile();
+        }
+
+        @Override
         public void createNotificationChannels(String pkg,
                 ParceledListSlice channelsList) throws RemoteException {
             checkCallerIsSystemOrSameApp(pkg);
@@ -1566,15 +1608,8 @@
         public void updateNotificationChannelForPackage(String pkg, int uid,
                 NotificationChannel channel) {
             enforceSystemOrSystemUI("Caller not system or systemui");
-            if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
-                // cancel
-                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
-                        UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
-                        null);
-            }
-            mRankingHelper.updateNotificationChannel(pkg, uid, channel);
-            mRankingHandler.requestSort(true);
-            savePolicyFile();
+            Preconditions.checkNotNull(channel);
+            updateNotificationChannelInt(pkg, uid, channel, false);
         }
 
         @Override
@@ -1701,7 +1736,6 @@
                 return new StatusBarNotification(
                         sbn.getPackageName(),
                         sbn.getOpPkg(),
-                        sbn.getNotificationChannel(),
                         sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
                         sbn.getNotification().clone(),
                         sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
@@ -2495,15 +2529,9 @@
         public void updateNotificationChannelFromAssistant(INotificationListener token, String pkg,
                 NotificationChannel channel) throws RemoteException {
             ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
-            if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
-                // cancel
-                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
-                        info.userid, REASON_CHANNEL_BANNED, null);
-            }
+            Preconditions.checkNotNull(channel);
             int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
-            mRankingHelper.updateNotificationChannelFromAssistant(pkg, uid, channel);
-            mRankingHandler.requestSort(true);
-            savePolicyFile();
+            updateNotificationChannelInt(pkg, uid, channel, true);
         }
 
         @Override
@@ -2528,7 +2556,7 @@
             final ArrayList<SnoozeCriterion> snoozeCriterionList =
                     adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
             if (!TextUtils.isEmpty(overrideChannelId)) {
-                n.setNotificationChannelOverride(mRankingHelper.getNotificationChannel(
+                n.updateNotificationChannel(mRankingHelper.getNotificationChannel(
                         n.sbn.getPackageName(), n.sbn.getUid(), overrideChannelId,
                         false /* includeDeleted */));
             }
@@ -2610,13 +2638,13 @@
                 final StatusBarNotification summarySbn =
                         new StatusBarNotification(adjustedSbn.getPackageName(),
                                 adjustedSbn.getOpPkg(),
-                                adjustedSbn.getNotificationChannel(),
                                 Integer.MAX_VALUE,
                                 GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
                                 adjustedSbn.getInitialPid(), summaryNotification,
                                 adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
                                 System.currentTimeMillis());
-                summaryRecord = new NotificationRecord(getContext(), summarySbn);
+                summaryRecord = new NotificationRecord(getContext(), summarySbn,
+                        notificationRecord.getChannel());
                 summaries.put(pkg, summarySbn.getKey());
             }
         }
@@ -2882,7 +2910,7 @@
         final NotificationChannel channel =  mRankingHelper.getNotificationChannelWithFallback(pkg,
                 callingUid, notification.getChannel(), false /* includeDeleted */);
         final StatusBarNotification n = new StatusBarNotification(
-                pkg, opPkg, channel, id, tag, callingUid, callingPid, notification,
+                pkg, opPkg, id, tag, callingUid, callingPid, notification,
                 user, null, System.currentTimeMillis());
 
         // Limit the number of notifications that any given package except the android
@@ -2946,7 +2974,7 @@
                 Notification.PRIORITY_MAX);
 
         // setup local book-keeping
-        final NotificationRecord r = new NotificationRecord(getContext(), n);
+        final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
         synchronized (mNotificationLock) {
             mEnqueuedNotifications.add(r);
         }
@@ -3490,14 +3518,18 @@
         boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
         synchronized (mNotificationLock) {
             final int N = mNotificationList.size();
+            // Any field that can change via one of the extractors or by the assistant
+            // needs to be added here.
             ArrayList<String> orderBefore = new ArrayList<String>(N);
             ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
             int[] visibilities = new int[N];
+            boolean[] showBadges = new boolean[N];
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
                 orderBefore.add(r.getKey());
                 groupOverrideBefore.add(r.sbn.getGroupKey());
                 visibilities[i] = r.getPackageVisibilityOverride();
+                showBadges[i] = r.canShowBadge();
                 mRankingHelper.extractSignals(r);
             }
             mRankingHelper.sort(mNotificationList);
@@ -3506,7 +3538,8 @@
                 if (forceUpdate
                         || !orderBefore.get(i).equals(r.getKey())
                         || visibilities[i] != r.getPackageVisibilityOverride()
-                        || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
+                        || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())
+                        || showBadges[i] != r.canShowBadge()) {
                     scheduleSendRankingUpdate();
                     return;
                 }
@@ -4246,9 +4279,10 @@
         Bundle visibilityOverrides = new Bundle();
         Bundle suppressedVisualEffects = new Bundle();
         Bundle explanation = new Bundle();
-        Bundle overrideChannels = new Bundle();
+        Bundle channels = new Bundle();
         Bundle overridePeople = new Bundle();
         Bundle snoozeCriteria = new Bundle();
+        Bundle showBadge = new Bundle();
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
             if (!isVisibleToListener(record.sbn, info)) {
@@ -4270,9 +4304,10 @@
                 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
             }
             overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
-            overrideChannels.putParcelable(key, record.getChannel());
+            channels.putParcelable(key, record.getChannel());
             overridePeople.putStringArrayList(key, record.getPeopleOverride());
             snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
+            showBadge.putBoolean(key, record.canShowBadge());
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -4283,7 +4318,7 @@
         }
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
                 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
-                overrideChannels, overridePeople, snoozeCriteria);
+                channels, overridePeople, snoozeCriteria, showBadge);
     }
 
     private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index e8c3d97..2a5a25f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,7 +25,6 @@
 import android.app.NotificationChannel;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -114,12 +113,14 @@
     private Uri mSound;
     private long[] mVibration;
     private AudioAttributes mAttributes;
-    private NotificationChannel mOverrideChannel;
+    private NotificationChannel mChannel;
     private ArrayList<String> mPeopleOverride;
     private ArrayList<SnoozeCriterion> mSnoozeCriteria;
+    private boolean mShowBadge;
 
     @VisibleForTesting
-    public NotificationRecord(Context context, StatusBarNotification sbn)
+    public NotificationRecord(Context context, StatusBarNotification sbn,
+            NotificationChannel channel)
     {
         this.sbn = sbn;
         mOriginalFlags = sbn.getNotification().flags;
@@ -128,6 +129,7 @@
         mUpdateTimeMs = mCreationTimeMs;
         mContext = context;
         stats = new NotificationUsageStats.SingleNotificationStats();
+        mChannel = channel;
         mPreChannelsNotification = isPreChannelsNotification();
         mSound = calculateSound();
         mVibration = calculateVibration();
@@ -154,7 +156,7 @@
     private Uri calculateSound() {
         final Notification n = sbn.getNotification();
 
-        Uri sound = sbn.getNotificationChannel().getSound();
+        Uri sound = mChannel.getSound();
         if (mPreChannelsNotification && (getChannel().getUserLockedFields()
                 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
 
@@ -386,7 +388,8 @@
         pw.println(prefix + "  mSound= " + mSound);
         pw.println(prefix + "  mVibration= " + mVibration);
         pw.println(prefix + "  mAttributes= " + mAttributes);
-        pw.println(prefix + "  overrideChannel=" + getChannel());
+        pw.println(prefix + "  mShowBadge=" + mShowBadge);
+        pw.println(prefix + "  channel=" + getChannel());
         if (getPeopleOverride() != null) {
             pw.println(prefix + "  overridePeople= " + TextUtils.join(",", getPeopleOverride()));
         }
@@ -640,16 +643,24 @@
     }
 
     public NotificationChannel getChannel() {
-        return mOverrideChannel == null ? sbn.getNotificationChannel() : mOverrideChannel;
+        return mChannel;
     }
 
-    protected void setNotificationChannelOverride(NotificationChannel channel) {
-        mOverrideChannel = channel;
-        if (mOverrideChannel != null) {
+    protected void updateNotificationChannel(NotificationChannel channel) {
+        if (channel != null) {
+            mChannel = channel;
             calculateImportance();
         }
     }
 
+    public void setShowBadge(boolean showBadge) {
+        mShowBadge = showBadge;
+    }
+
+    public boolean canShowBadge() {
+        return mShowBadge;
+    }
+
     public Uri getSound() {
         return mSound;
     }
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index c2cef09..492d5c6 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -22,6 +22,8 @@
 
     void setImportance(String packageName, int uid, int importance);
     int getImportance(String packageName, int uid);
+    void setShowBadge(String packageName, int uid, boolean showBadge);
+    boolean canShowBadge(String packageName, int uid);
 
     void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
             boolean fromTargetApp);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index e44fb7f..1861bcb 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -67,10 +67,12 @@
     private static final String ATT_PRIORITY = "priority";
     private static final String ATT_VISIBILITY = "visibility";
     private static final String ATT_IMPORTANCE = "importance";
+    private static final String ATT_SHOW_BADGE = "show_badge";
 
     private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
     private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
     private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
+    private static final boolean DEFAULT_SHOW_BADGE = true;
 
     private final NotificationSignalExtractor[] mSignalExtractors;
     private final NotificationComparator mPreliminaryComparator;
@@ -169,7 +171,8 @@
                         Record r = getOrCreateRecord(name, uid,
                                 safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
                                 safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY),
-                                safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
+                                safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
+                                safeBool(parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
 
                         // Channels
                         final int innerDepth = parser.getDepth();
@@ -215,11 +218,11 @@
 
     private Record getOrCreateRecord(String pkg, int uid) {
         return getOrCreateRecord(pkg, uid,
-                DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY);
+                DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE);
     }
 
     private Record getOrCreateRecord(String pkg, int uid, int importance, int priority,
-            int visibility) {
+            int visibility, boolean showBadge) {
         final String key = recordKey(pkg, uid);
         Record r = (uid == Record.UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg) : mRecords.get(key);
         if (r == null) {
@@ -229,6 +232,7 @@
             r.importance = importance;
             r.priority = priority;
             r.visibility = visibility;
+            r.showBadge = showBadge;
             createDefaultChannelIfMissing(r);
             if (r.uid == Record.UNKNOWN_UID) {
                 mRestoredWithoutUids.put(pkg, r);
@@ -298,7 +302,7 @@
             }
             final boolean hasNonDefaultSettings = r.importance != DEFAULT_IMPORTANCE
                     || r.priority != DEFAULT_PRIORITY || r.visibility != DEFAULT_VISIBILITY
-                    || r.channels.size() > 0;
+                    || r.showBadge != DEFAULT_SHOW_BADGE || r.channels.size() > 0;
             if (hasNonDefaultSettings) {
                 out.startTag(null, TAG_PACKAGE);
                 out.attribute(null, ATT_NAME, r.pkg);
@@ -311,6 +315,7 @@
                 if (r.visibility != DEFAULT_VISIBILITY) {
                     out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
                 }
+                out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
 
                 if (!forBackup) {
                     out.attribute(null, ATT_UID, Integer.toString(r.uid));
@@ -396,6 +401,12 @@
         return Collections.binarySearch(notificationList, target, mFinalComparator);
     }
 
+    private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
+        final String value = parser.getAttributeValue(null, att);
+        if (TextUtils.isEmpty(value)) return defValue;
+        return Boolean.parseBoolean(value);
+    }
+
     private static int safeInt(XmlPullParser parser, String att, int defValue) {
         final String val = parser.getAttributeValue(null, att);
         return tryParseInt(val, defValue);
@@ -419,6 +430,17 @@
     }
 
     @Override
+    public boolean canShowBadge(String packageName, int uid) {
+        return getOrCreateRecord(packageName, uid).showBadge;
+    }
+
+    @Override
+    public void setShowBadge(String packageName, int uid, boolean showBadge) {
+        getOrCreateRecord(packageName, uid).showBadge = showBadge;
+        updateConfig();
+    }
+
+    @Override
     public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
             boolean fromTargetApp) {
         Preconditions.checkNotNull(pkg);
@@ -454,6 +476,9 @@
         if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
             channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
         }
+        if (!r.showBadge) {
+            channel.setShowBadge(false);
+        }
         r.channels.put(channel.getId(), channel);
         updateConfig();
     }
@@ -672,7 +697,7 @@
             final Record r = records.valueAt(i);
             if (filter == null || filter.matches(r.pkg)) {
                 pw.print(prefix);
-                pw.print("  ");
+                pw.print("  AppSettings: ");
                 pw.print(r.pkg);
                 pw.print(" (");
                 pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
@@ -689,6 +714,8 @@
                     pw.print(" visibility=");
                     pw.print(Notification.visibilityToString(r.visibility));
                 }
+                pw.print(" showBadge=");
+                pw.print(Boolean.toString(r.showBadge));
                 pw.println();
                 for (NotificationChannel channel : r.channels.values()) {
                     pw.print(prefix);
@@ -725,6 +752,9 @@
                     if (r.visibility != DEFAULT_VISIBILITY) {
                         record.put("visibility", Notification.visibilityToString(r.visibility));
                     }
+                    if (r.showBadge != DEFAULT_SHOW_BADGE) {
+                        record.put("showBadge", Boolean.valueOf(r.showBadge));
+                    }
                     for (NotificationChannel channel : r.channels.values()) {
                         record.put("channel", channel.toJson());
                     }
@@ -838,6 +868,7 @@
         int importance = DEFAULT_IMPORTANCE;
         int priority = DEFAULT_PRIORITY;
         int visibility = DEFAULT_VISIBILITY;
+        boolean showBadge = DEFAULT_SHOW_BADGE;
 
         ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
    }
diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
new file mode 100644
index 0000000..b26bac3
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BadgeExtractorTest {
+
+    @Mock RankingConfig mConfig;
+
+    private String mPkg = "com.android.server.notification";
+    private int mId = 1001;
+    private String mTag = null;
+    private int mUid = 1000;
+    private int mPid = 2000;
+    private UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    private NotificationRecord getNotificationRecord(NotificationChannel channel) {
+        final Builder builder = new Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setPriority(Notification.PRIORITY_HIGH)
+                .setDefaults(Notification.DEFAULT_SOUND);
+
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid,
+                mPid, n, mUser, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+        return r;
+    }
+
+    private Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    //
+    // Tests
+    //
+
+    @Test
+    public void testAppYesChannelNo() throws Exception {
+        BadgeExtractor extractor = new BadgeExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
+        NotificationChannel channel =
+                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
+        channel.setShowBadge(false);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertFalse(r.canShowBadge());
+    }
+
+    @Test
+    public void testAppNoChannelYes() throws Exception {
+        BadgeExtractor extractor = new BadgeExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false);
+        NotificationChannel channel =
+                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_HIGH);
+        channel.setShowBadge(true);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertFalse(r.canShowBadge());
+    }
+
+    @Test
+    public void testAppYesChannelYes() throws Exception {
+        BadgeExtractor extractor = new BadgeExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
+        NotificationChannel channel =
+                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
+        channel.setShowBadge(true);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertTrue(r.canShowBadge());
+    }
+
+    @Test
+    public void testAppNoChannelNo() throws Exception {
+        BadgeExtractor extractor = new BadgeExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false);
+        NotificationChannel channel =
+                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
+        channel.setShowBadge(false);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertFalse(r.canShowBadge());
+    }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index ad436724a..468a26b 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -230,9 +230,9 @@
             n.flags |= Notification.FLAG_INSISTENT;
         }
 
-        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, channel, id, mTag, mUid,
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
                 mPid, n, mUser, null, System.currentTimeMillis());
-        NotificationRecord r = new NotificationRecord(getContext(), sbn);
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
         mService.addNotification(r);
         return r;
     }
diff --git a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
index f48d785..936531b 100644
--- a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
@@ -70,9 +70,7 @@
         if (groupKey != null) {
             nb.setGroup(groupKey);
         }
-        NotificationChannel channel =
-                new NotificationChannel("test", "test", NotificationManager.IMPORTANCE_LOW);
-        return new StatusBarNotification(pkg, pkg, channel, id, tag, 0, 0, nb.build(), user, null,
+        return new StatusBarNotification(pkg, pkg, id, tag, 0, 0, nb.build(), user, null,
                 System.currentTimeMillis());
     }
 
diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
index eee9cf1..f8a32bb 100644
--- a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -69,9 +69,9 @@
                 .setDefaults(Notification.DEFAULT_SOUND);
 
         Notification n = builder.build();
-        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, channel, mId, mTag, mUid,
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid,
                 mPid, n, mUser, null, System.currentTimeMillis());
-        NotificationRecord r = new NotificationRecord(getContext(), sbn);
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
         return r;
     }
 
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
index 403b65c..aa08b41 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
@@ -105,8 +105,8 @@
                 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
                 .build();
         mRecordMinCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
-                callPkg, getDefaultChannel(), 1, "minCall", callUid, callUid, n1,
-                new UserHandle(userId), "", 2000));
+                callPkg, 1, "minCall", callUid, callUid, n1,
+                new UserHandle(userId), "", 2000), getDefaultChannel());
         mRecordMinCall.setUserImportance(NotificationManager.IMPORTANCE_MIN);
 
         Notification n2 = new Notification.Builder(mContext)
@@ -114,8 +114,8 @@
                 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
                 .build();
         mRecordHighCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
-                callPkg, getDefaultChannel(), 1, "highcall", callUid, callUid, n2,
-                new UserHandle(userId), "", 1999));
+                callPkg, 1, "highcall", callUid, callUid, n2,
+                new UserHandle(userId), "", 1999), getDefaultChannel());
         mRecordHighCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
 
         Notification n3 = new Notification.Builder(mContext)
@@ -124,43 +124,43 @@
                 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
                 .build();
         mRecordDefaultMedia = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "media", uid2, uid2, n3, new UserHandle(userId),
-                "", 1499));
+                pkg2, 1, "media", uid2, uid2, n3, new UserHandle(userId),
+                "", 1499), getDefaultChannel());
         mRecordDefaultMedia.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
 
         Notification n4 = new Notification.Builder(mContext)
                 .setStyle(new Notification.MessagingStyle("sender!")).build();
         mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId),
-                "", 1599));
+                pkg2, 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId),
+                "", 1599), getDefaultChannel());
         mRecordInlineReply.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
         mRecordInlineReply.setPackagePriority(Notification.PRIORITY_MAX);
 
         Notification n5 = new Notification.Builder(mContext)
                 .setCategory(Notification.CATEGORY_MESSAGE).build();
         mRecordSms = new NotificationRecord(mContext, new StatusBarNotification(smsPkg,
-                smsPkg, getDefaultChannel(), 1, "sms", smsUid, smsUid, n5, new UserHandle(userId),
-                "", 1299));
+                smsPkg, 1, "sms", smsUid, smsUid, n5, new UserHandle(userId),
+                "", 1299), getDefaultChannel());
         mRecordSms.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
 
         Notification n6 = new Notification.Builder(mContext).build();
         mRecordStarredContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "starred", uid2, uid2, n6, new UserHandle(userId),
-                "", 1259));
+                pkg2, 1, "starred", uid2, uid2, n6, new UserHandle(userId),
+                "", 1259), getDefaultChannel());
         mRecordStarredContact.setContactAffinity(ValidateNotificationPeople.STARRED_CONTACT);
         mRecordStarredContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
 
         Notification n7 = new Notification.Builder(mContext).build();
         mRecordContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "contact", uid2, uid2, n7, new UserHandle(userId),
-                "", 1259));
+                pkg2, 1, "contact", uid2, uid2, n7, new UserHandle(userId),
+                "", 1259), getDefaultChannel());
         mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT);
         mRecordContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
 
         Notification n8 = new Notification.Builder(mContext).build();
         mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "urgent", uid2, uid2, n8, new UserHandle(userId),
-                "", 1258));
+                pkg2, 1, "urgent", uid2, uid2, n8, new UserHandle(userId),
+                "", 1258), getDefaultChannel());
         mRecordUrgent.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
 
         Notification n9 = new Notification.Builder(mContext)
@@ -169,15 +169,15 @@
                         |Notification.FLAG_FOREGROUND_SERVICE, true)
                 .build();
         mRecordCheater = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "cheater", uid2, uid2, n9, new UserHandle(userId),
-                "", 9258));
+                pkg2, 1, "cheater", uid2, uid2, n9, new UserHandle(userId),
+                "", 9258), getDefaultChannel());
         mRecordCheater.setUserImportance(NotificationManager.IMPORTANCE_LOW);
 
         Notification n10 = new Notification.Builder(mContext)
                 .setStyle(new Notification.InboxStyle().setSummaryText("message!")).build();
         mRecordEmail = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "email", uid2, uid2, n10, new UserHandle(userId),
-                "", 1599));
+                pkg2, 1, "email", uid2, uid2, n10, new UserHandle(userId),
+                "", 1599), getDefaultChannel());
         mRecordEmail.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
     }
 
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
index b6166f6..f0f4c4d 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -59,6 +59,7 @@
             assertEquals(getChannel(key, i), ranking.getChannel());
             assertEquals(getPeople(key, i), ranking.getAdditionalPeople());
             assertEquals(getSnoozeCriteria(key, i), ranking.getSnoozeCriteria());
+            assertEquals(getShowBadge(i), ranking.canShowBadge());
         }
     }
 
@@ -68,9 +69,10 @@
         Bundle overrideGroupKeys = new Bundle();
         Bundle suppressedVisualEffects = new Bundle();
         Bundle explanation = new Bundle();
-        Bundle overrideChannels = new Bundle();
+        Bundle channels = new Bundle();
         Bundle overridePeople = new Bundle();
         Bundle snoozeCriteria = new Bundle();
+        Bundle showBadge = new Bundle();
         int[] importance = new int[mKeys.length];
 
         for (int i = 0; i < mKeys.length; i++) {
@@ -83,14 +85,15 @@
             suppressedVisualEffects.putInt(key, getSuppressedVisualEffects(i));
             importance[i] = getImportance(i);
             explanation.putString(key, getExplanation(key));
-            overrideChannels.putParcelable(key, getChannel(key, i));
+            channels.putParcelable(key, getChannel(key, i));
             overridePeople.putStringArrayList(key, getPeople(key, i));
             snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i));
+            showBadge.putBoolean(key, getShowBadge(i));
         }
         NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
                 interceptedKeys.toArray(new String[0]), visibilityOverrides,
                 suppressedVisualEffects, importance, explanation, overrideGroupKeys,
-                overrideChannels, overridePeople, snoozeCriteria);
+                channels, overridePeople, snoozeCriteria, showBadge);
         return update;
     }
 
@@ -122,6 +125,10 @@
         return new NotificationChannel(key, key, getImportance(index));
     }
 
+    private boolean getShowBadge(int index) {
+        return index % 3 == 0;
+    }
+
     private ArrayList<String> getPeople(String key, int index) {
         ArrayList<String> people = new ArrayList<>();
         for (int i = 0; i < index; i++) {
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9b74fcc..92d9810 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -132,9 +132,9 @@
                 .setPriority(Notification.PRIORITY_HIGH)
                 .build();
         StatusBarNotification sbn = new StatusBarNotification(mContext.getPackageName(),
-                mContext.getPackageName(), channel, 1, "tag", uid, 0,
+                mContext.getPackageName(), 1, "tag", uid, 0,
                 n, new UserHandle(uid), null, 0);
-        return new NotificationRecord(mContext, sbn);
+        return new NotificationRecord(mContext, sbn, channel);
     }
 
     @Test
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
index fc94271f..15dcc26 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
@@ -136,10 +136,10 @@
 
         Notification n = builder.build();
         if (preO) {
-            return new StatusBarNotification(pkg, pkg, defaultChannel, id1, tag1, uid, uid, n,
+            return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n,
                     mUser, null, uid);
         } else {
-            return new StatusBarNotification(pkg2, pkg2, channel, id2, tag2, uid2, uid2, n,
+            return new StatusBarNotification(pkg2, pkg2, id2, tag2, uid2, uid2, n,
                     mUser, null, uid2);
         }
     }
@@ -155,7 +155,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound());
     }
 
@@ -166,7 +166,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(CUSTOM_SOUND, record.getSound());
     }
 
@@ -178,7 +178,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(CUSTOM_SOUND, record.getSound());
     }
 
@@ -189,7 +189,7 @@
         StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         assertEquals(CUSTOM_SOUND, record.getSound());
     }
 
@@ -200,7 +200,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, true /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertNotNull(record.getVibration());
     }
 
@@ -211,7 +211,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(CUSTOM_VIBRATION, record.getVibration());
     }
 
@@ -223,7 +223,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertTrue(!Objects.equals(CUSTOM_VIBRATION, record.getVibration()));
     }
 
@@ -234,7 +234,7 @@
         StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         assertEquals(CUSTOM_CHANNEL_VIBRATION, record.getVibration());
     }
 
@@ -245,7 +245,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes());
     }
 
@@ -256,7 +256,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes());
     }
 
@@ -264,7 +264,7 @@
     public void testImportance_preUpgrade() throws Exception {
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(NotificationManager.IMPORTANCE_HIGH, record.getImportance());
     }
 
@@ -275,7 +275,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(NotificationManager.IMPORTANCE_LOW, record.getImportance());
     }
 
@@ -283,7 +283,7 @@
     public void testImportance_upgrade() throws Exception {
         StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         assertEquals(NotificationManager.IMPORTANCE_DEFAULT, record.getImportance());
     }
 }
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 59ac427..0320d8a 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -105,8 +105,8 @@
                 .setWhen(1205)
                 .build();
         mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiGroupGSortA, user,
-                null, System.currentTimeMillis()));
+                "package", "package", 1, null, 0, 0, mNotiGroupGSortA, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
 
         mNotiGroupGSortB = new Notification.Builder(getContext())
                 .setContentTitle("B")
@@ -115,24 +115,24 @@
                 .setWhen(1200)
                 .build();
         mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiGroupGSortB, user,
-                null, System.currentTimeMillis()));
+                "package", "package", 1, null, 0, 0, mNotiGroupGSortB, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
 
         mNotiNoGroup = new Notification.Builder(getContext())
                 .setContentTitle("C")
                 .setWhen(1201)
                 .build();
         mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroup, user,
-                null, System.currentTimeMillis()));
+                "package", "package", 1, null, 0, 0, mNotiNoGroup, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
 
         mNotiNoGroup2 = new Notification.Builder(getContext())
                 .setContentTitle("D")
                 .setWhen(1202)
                 .build();
         mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroup2, user,
-                null, System.currentTimeMillis()));
+                "package", "package", 1, null, 0, 0, mNotiNoGroup2, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
 
         mNotiNoGroupSortA = new Notification.Builder(getContext())
                 .setContentTitle("E")
@@ -140,8 +140,8 @@
                 .setSortKey("A")
                 .build();
         mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroupSortA, user,
-                null, System.currentTimeMillis()));
+                "package", "package", 1, null, 0, 0, mNotiNoGroupSortA, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
 
         final ApplicationInfo legacy = new ApplicationInfo();
         legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -254,8 +254,12 @@
         mHelper.createNotificationChannel(pkg, uid, channel1, true);
         mHelper.createNotificationChannel(pkg, uid, channel2, false);
 
+        mHelper.setShowBadge(pkg, uid, true);
+        mHelper.setShowBadge(pkg2, uid2, false);
+
         ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), channel2.getId(),
                 NotificationChannel.DEFAULT_CHANNEL_ID);
+        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{pkg}, new int[]{uid});
 
         XmlPullParser parser = Xml.newPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
@@ -263,6 +267,8 @@
         parser.nextTag();
         mHelper.readXml(parser, false);
 
+        assertFalse(mHelper.canShowBadge(pkg2, uid2));
+        assertTrue(mHelper.canShowBadge(pkg, uid));
         assertEquals(channel1, mHelper.getNotificationChannel(pkg, uid, channel1.getId(), false));
         compareChannels(channel2,
                 mHelper.getNotificationChannel(pkg, uid, channel2.getId(), false));
@@ -758,4 +764,11 @@
         mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid});
         assertEquals(2, mHelper.getNotificationChannels(pkg, uid, false).getList().size());
     }
+
+    @Test
+    public void testRecordDefaults() throws Exception {
+        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(pkg, uid));
+        assertEquals(true, mHelper.canShowBadge(pkg, uid));
+        assertEquals(1, mHelper.getNotificationChannels(pkg, uid, false).getList().size());
+    }
 }
diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
index 460fcdf..b7931d4 100644
--- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
@@ -199,8 +199,8 @@
                 .setWhen(1205)
                 .build();
         return new NotificationRecord(getContext(), new StatusBarNotification(
-                pkg, pkg, getDefaultChannel(), id, tag, 0, 0, n, user, null,
-                System.currentTimeMillis()));
+                pkg, pkg, id, tag, 0, 0, n, user, null,
+                System.currentTimeMillis()), getDefaultChannel());
     }
 
     private NotificationChannel getDefaultChannel() {