Merge "use importance instead of score"
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index a7545f2..d7fcd7d 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -863,6 +863,8 @@
         private boolean mMatchesInterruptionFilter;
         private int mVisibilityOverride;
         private int mSuppressedVisualEffects;
+        private int mImportance;
+        private CharSequence mImportanceExplanation;
 
         public Ranking() {}
 
@@ -928,7 +930,7 @@
          * @return the rank of the notification
          */
         public int getImportance() {
-            return IMPORTANCE_DEFAULT;  // TODO implement;
+            return mImportance;
         }
 
         /**
@@ -939,18 +941,21 @@
          * @return the explanation for the importance, or null if it is the natural importance
          */
         public CharSequence getImportanceExplanation() {
-            return null;  // TODO implement
+            return mImportanceExplanation;
         }
 
         private void populate(String key, int rank, boolean isAmbient,
                 boolean matchesInterruptionFilter, int visibilityOverride,
-                int suppressedVisualEffects) {
+                int suppressedVisualEffects, int importance,
+                CharSequence explanation) {
             mKey = key;
             mRank = rank;
             mIsAmbient = isAmbient;
             mMatchesInterruptionFilter = matchesInterruptionFilter;
             mVisibilityOverride = visibilityOverride;
             mSuppressedVisualEffects = suppressedVisualEffects;
+            mImportance = importance;
+            mImportanceExplanation = explanation;
         }
 
         /**
@@ -990,6 +995,8 @@
         private ArraySet<Object> mIntercepted;
         private ArrayMap<String, Integer> mVisibilityOverrides;
         private ArrayMap<String, Integer> mSuppressedVisualEffects;
+        private ArrayMap<String, Integer> mImportance;
+        private ArrayMap<String, String> mImportanceExplanation;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1015,7 +1022,8 @@
         public boolean getRanking(String key, Ranking outRanking) {
             int rank = getRank(key);
             outRanking.populate(key, rank, isAmbient(key), !isIntercepted(key),
-                    getVisibilityOverride(key), getSuppressedVisualEffects(key));
+                    getVisibilityOverride(key), getSuppressedVisualEffects(key),
+                    getImportance(key), getImportanceExplanation(key));
             return rank >= 0;
         }
 
@@ -1073,6 +1081,28 @@
             return suppressed.intValue();
         }
 
+        private int getImportance(String key) {
+            synchronized (this) {
+                if (mImportance == null) {
+                    buildImportanceLocked();
+                }
+            }
+            Integer importance = mImportance.get(key);
+            if (importance == null) {
+                return Ranking.IMPORTANCE_DEFAULT;
+            }
+            return importance.intValue();
+        }
+
+        private String getImportanceExplanation(String key) {
+            synchronized (this) {
+                if (mImportanceExplanation == null) {
+                    buildImportanceExplanationLocked();
+                }
+            }
+            return mImportanceExplanation.get(key);
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1107,6 +1137,25 @@
                 mSuppressedVisualEffects.put(key, suppressedBundle.getInt(key));
             }
         }
+        // Locked by 'this'
+        private void buildImportanceLocked() {
+            String[] orderedKeys = mRankingUpdate.getOrderedKeys();
+            int[] importance = mRankingUpdate.getImportance();
+            mImportance = new ArrayMap<>(orderedKeys.length);
+            for (int i = 0; i < orderedKeys.length; i++) {
+                String key = orderedKeys[i];
+                mImportance.put(key, importance[i]);
+            }
+        }
+
+        // Locked by 'this'
+        private void buildImportanceExplanationLocked() {
+            Bundle explanationBundle = mRankingUpdate.getImportanceExplanation();
+            mImportanceExplanation = new ArrayMap<>(explanationBundle.size());
+            for (String key: explanationBundle.keySet()) {
+                mImportanceExplanation.put(key, explanationBundle.getString(key));
+            }
+        }
 
         // ----------- Parcelable
 
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 1282fb1..0d42ffb 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -29,14 +29,19 @@
     private final int mFirstAmbientIndex;
     private final Bundle mVisibilityOverrides;
     private final Bundle mSuppressedVisualEffects;
+    private final int[] mImportance;
+    private final Bundle mImportanceExplanation;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
-            Bundle visibilityOverrides, int firstAmbientIndex, Bundle suppressedVisualEffects) {
+            Bundle visibilityOverrides, int firstAmbientIndex, Bundle suppressedVisualEffects,
+            int[] importance, Bundle explanation) {
         mKeys = keys;
         mFirstAmbientIndex = firstAmbientIndex;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
         mSuppressedVisualEffects = suppressedVisualEffects;
+        mImportance = importance;
+        mImportanceExplanation = explanation;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -45,6 +50,9 @@
         mInterceptedKeys = in.readStringArray();
         mVisibilityOverrides = in.readBundle();
         mSuppressedVisualEffects = in.readBundle();
+        mImportance = new int[mKeys.length];
+        in.readIntArray(mImportance);
+        mImportanceExplanation = in.readBundle();
     }
 
     @Override
@@ -59,6 +67,8 @@
         out.writeStringArray(mInterceptedKeys);
         out.writeBundle(mVisibilityOverrides);
         out.writeBundle(mSuppressedVisualEffects);
+        out.writeIntArray(mImportance);
+        out.writeBundle(mImportanceExplanation);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -91,4 +101,12 @@
     public Bundle getSuppressedVisualEffects() {
         return mSuppressedVisualEffects;
     }
+
+    public int[] getImportance() {
+        return mImportance;
+    }
+
+    public Bundle getImportanceExplanation() {
+        return mImportanceExplanation;
+    }
 }
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 2cab914..198e43d 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -42,7 +42,6 @@
     private final UserHandle user;
     private final long postTime;
 
-    private final int score;
     private Context mContext; // used for inflation & icon expansion
 
     /** @hide */
@@ -64,7 +63,6 @@
         this.tag = tag;
         this.uid = uid;
         this.initialPid = initialPid;
-        this.score = score;
         this.notification = notification;
         this.user = user;
         this.postTime = postTime;
@@ -83,7 +81,6 @@
         }
         this.uid = in.readInt();
         this.initialPid = in.readInt();
-        this.score = in.readInt();
         this.notification = new Notification(in);
         this.user = UserHandle.readFromParcel(in);
         this.postTime = in.readLong();
@@ -120,7 +117,6 @@
         }
         out.writeInt(this.uid);
         out.writeInt(this.initialPid);
-        out.writeInt(this.score);
         this.notification.writeToParcel(out, flags);
         user.writeToParcel(out, flags);
 
@@ -153,14 +149,14 @@
         this.notification.cloneInto(no, false); // light copy
         return new StatusBarNotification(this.pkg, this.opPkg,
                 this.id, this.tag, this.uid, this.initialPid,
-                this.score, no, this.user, this.postTime);
+                0, no, this.user, this.postTime);
     }
 
     @Override
     public StatusBarNotification clone() {
         return new StatusBarNotification(this.pkg, this.opPkg,
                 this.id, this.tag, this.uid, this.initialPid,
-                this.score, this.notification.clone(), this.user, this.postTime);
+                0, this.notification.clone(), this.user, this.postTime);
     }
 
     @Override
@@ -168,7 +164,7 @@
         return String.format(
                 "StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
                 this.pkg, this.user, this.id, this.tag,
-                this.score, this.key, this.notification);
+                0, this.key, this.notification);
     }
 
     /** Convenience method to check the notification's flags for
@@ -247,11 +243,6 @@
         return postTime;
     }
 
-    /** @hide */
-    public int getScore() {
-        return score;
-    }
-
     /**
      * A unique instance key for this notification record.
      */
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e5415df..ca249b9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4102,4 +4102,7 @@
     </plurals>
 
     <string name="default_notification_topic_label">Miscellaneous</string>
+
+    <string name="importance_from_topic">You set the importance of these notifications.</string>
+    <string name="importance_from_person">This is important because of the people involved.</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dd81f89..7470331 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2379,4 +2379,6 @@
   <java-symbol type="dimen" name="notification_content_margin_end" />
   <java-symbol type="dimen" name="notification_content_picture_margin" />
   <java-symbol type="dimen" name="notification_content_margin_top" />
+  <java-symbol type="string" name="importance_from_topic" />
+  <java-symbol type="string" name="importance_from_person" />
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index cc6a29a..8b94e54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar;
 
+import static  android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.TimeInterpolator;
@@ -2102,11 +2104,11 @@
         mNotificationData.updateRanking(ranking);
 
         boolean applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());
-        boolean shouldInterrupt = shouldInterrupt(entry, notification);
+        boolean shouldPeek = shouldPeek(entry, notification);
         boolean alertAgain = alertAgain(entry, n);
         if (DEBUG) {
             Log.d(TAG, "applyInPlace=" + applyInPlace
-                    + " shouldInterrupt=" + shouldInterrupt
+                    + " shouldPeek=" + shouldPeek
                     + " alertAgain=" + alertAgain);
         }
 
@@ -2153,7 +2155,7 @@
             entry.icon.set(ic);
             inflateViews(entry, mStackScroller);
         }
-        updateHeadsUp(key, entry, shouldInterrupt, alertAgain);
+        updateHeadsUp(key, entry, shouldPeek, alertAgain);
         updateNotifications();
 
         // Update the veto button accordingly (and as a result, whether this row is
@@ -2169,7 +2171,7 @@
         setAreThereNotifications();
     }
 
-    protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
+    protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
             boolean alertAgain);
 
     private void updateNotificationViews(Entry entry, StatusBarNotification sbn) {
@@ -2212,49 +2214,54 @@
                 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
     }
 
-    protected boolean shouldInterrupt(Entry entry) {
-        return shouldInterrupt(entry, entry.notification);
+    protected boolean shouldPeek(Entry entry) {
+        return shouldPeek(entry, entry.notification);
     }
 
-    protected boolean shouldInterrupt(Entry entry, StatusBarNotification sbn) {
+    protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
         if (mNotificationData.shouldFilterOut(sbn)) {
-            if (DEBUG) {
-                Log.d(TAG, "Skipping HUN check for " + sbn.getKey() + " since it's filtered out.");
-            }
+            if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
             return false;
         }
 
         if (isSnoozedPackage(sbn)) {
+            if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
             return false;
         }
 
-        Notification notification = sbn.getNotification();
-        // some predicates to make the boolean logic legible
-        boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
-                || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
-                || notification.sound != null
-                || notification.vibrate != null;
-        boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
-        boolean isFullscreen = notification.fullScreenIntent != null;
-        boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
-        boolean accessibilityForcesLaunch = isFullscreen
-                && mAccessibilityManager.isTouchExplorationEnabled();
-        boolean justLaunchedFullScreenIntent = entry.hasJustLaunchedFullScreenIntent();
-        boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
-                && !accessibilityForcesLaunch
-                && !justLaunchedFullScreenIntent
-                && mPowerManager.isScreenOn()
+        if (entry.hasJustLaunchedFullScreenIntent()) {
+            if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
+            return false;
+        }
+
+        if (sbn.getNotification().fullScreenIntent != null
+                && mAccessibilityManager.isTouchExplorationEnabled()) {
+            if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
+            return false;
+        }
+
+
+        if (mNotificationData.shouldSuppressPeek(sbn.getKey())) {
+            if (DEBUG) Log.d(TAG, "No peeking: suppressed by manager: " + sbn.getKey());
+            return false;
+        }
+
+        if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_MAX) {
+            if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
+            return false;
+        }
+
+        boolean inUse = mPowerManager.isScreenOn()
                 && (!mStatusBarKeyguardViewManager.isShowing()
                         || mStatusBarKeyguardViewManager.isOccluded())
-                && !mStatusBarKeyguardViewManager.isInputRestricted()
-                && !mNotificationData.shouldSuppressPeek(sbn.getKey());
+                && !mStatusBarKeyguardViewManager.isInputRestricted();
         try {
-            interrupt = interrupt && !mDreamManager.isDreaming();
+            inUse = inUse && !mDreamManager.isDreaming();
         } catch (RemoteException e) {
             Log.d(TAG, "failed to query dream manager", e);
         }
-        if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
-        return interrupt;
+        if (DEBUG) Log.d(TAG, "peek if device in use: " + inUse);
+        return inUse;
     }
 
     protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 89edae3..fa41215 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -188,22 +188,33 @@
         public int compare(Entry a, Entry b) {
             final StatusBarNotification na = a.notification;
             final StatusBarNotification nb = b.notification;
-            final int aPriority = na.getNotification().priority;
-            final int bPriority = nb.getNotification().priority;
+            int aImportance = Ranking.IMPORTANCE_DEFAULT;
+            int bImportance = Ranking.IMPORTANCE_DEFAULT;
+            int aRank = 0;
+            int bRank = 0;
+
+            if (mRankingMap != null) {
+                // RankingMap as received from NoMan
+                mRankingMap.getRanking(a.key, mRankingA);
+                mRankingMap.getRanking(b.key, mRankingB);
+                aImportance = mRankingA.getImportance();
+                bImportance = mRankingB.getImportance();
+                aRank = mRankingA.getRank();
+                bRank = mRankingB.getRank();
+            }
 
             String mediaNotification = mEnvironment.getCurrentMediaNotificationKey();
 
             // PRIORITY_MIN media streams are allowed to drift to the bottom
             final boolean aMedia = a.key.equals(mediaNotification)
-                    && aPriority > Notification.PRIORITY_MIN;
+                    && aImportance > Ranking.IMPORTANCE_LOW;
             final boolean bMedia = b.key.equals(mediaNotification)
-                    && bPriority > Notification.PRIORITY_MIN;
+                    && bImportance > Ranking.IMPORTANCE_LOW;
 
-            boolean aSystemMax = aPriority >= Notification.PRIORITY_MAX &&
+            boolean aSystemMax = aImportance >= Ranking.IMPORTANCE_MAX &&
                     isSystemNotification(na);
-            boolean bSystemMax = bPriority >= Notification.PRIORITY_MAX &&
+            boolean bSystemMax = bImportance >= Ranking.IMPORTANCE_MAX &&
                     isSystemNotification(nb);
-            int d = nb.getScore() - na.getScore();
 
             boolean isHeadsUp = a.row.isHeadsUp();
             if (isHeadsUp != b.row.isHeadsUp()) {
@@ -217,13 +228,8 @@
             } else if (aSystemMax != bSystemMax) {
                 // Upsort PRIORITY_MAX system notifications
                 return aSystemMax ? -1 : 1;
-            } else if (mRankingMap != null) {
-                // RankingMap as received from NoMan
-                mRankingMap.getRanking(a.key, mRankingA);
-                mRankingMap.getRanking(b.key, mRankingB);
-                return mRankingA.getRank() - mRankingB.getRank();
-            } if (d != 0) {
-                return d;
+            } else if (aRank != bRank) {
+                return aRank - bRank;
             } else {
                 return (int) (nb.getNotification().when - na.getNotification().when);
             }
@@ -295,6 +301,14 @@
         return false;
     }
 
+    public int getImportance(String key) {
+        if (mRankingMap != null) {
+            mRankingMap.getRanking(key, mTmpRanking);
+            return mTmpRanking.getImportance();
+        }
+        return Ranking.IMPORTANCE_UNSPECIFIED;
+    }
+
     private void updateRankingAndSort(RankingMap ranking) {
         if (ranking != null) {
             mRankingMap = ranking;
@@ -390,12 +404,13 @@
     }
 
     private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) {
+        mRankingMap.getRanking(e.key, mTmpRanking);
         pw.print(indent);
         pw.println("  [" + i + "] key=" + e.key + " icon=" + e.icon);
         StatusBarNotification n = e.notification;
         pw.print(indent);
-        pw.println("      pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" +
-                n.getScore());
+        pw.println("      pkg=" + n.getPackageName() + " id=" + n.getId() + " importance=" +
+                mTmpRanking.getImportance());
         pw.print(indent);
         pw.println("      notification=" + n.getNotification());
         pw.print(indent);
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 181e6aa..b854c80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1266,7 +1266,7 @@
         if (shadeEntry == null) {
             return;
         }
-        boolean isHeadsUped = mUseHeadsUp && shouldInterrupt(shadeEntry);
+        boolean isHeadsUped = mUseHeadsUp && shouldPeek(shadeEntry);
         if (isHeadsUped) {
             mHeadsUpManager.showNotification(shadeEntry);
             // Mark as seen immediately
@@ -2111,17 +2111,17 @@
 
     }
 
-    protected void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
+    protected void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
             boolean alertAgain) {
         final boolean wasHeadsUp = isHeadsUp(key);
         if (wasHeadsUp) {
-            if (!shouldInterrupt) {
+            if (!shouldPeek) {
                 // We don't want this to be interrupting anymore, lets remove it
                 mHeadsUpManager.removeNotification(key);
             } else {
                 mHeadsUpManager.updateNotification(entry, alertAgain);
             }
-        } else if (shouldInterrupt && alertAgain) {
+        } else if (shouldPeek && alertAgain) {
             // This notification was updated to be a heads-up, show it!
             mHeadsUpManager.showNotification(entry);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 39a2986..856a774 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -175,7 +175,7 @@
     }
 
     @Override
-    protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldInterrupt,
+    protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldPeek,
             boolean alertAgain) {
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index ec81fd2..32db000 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -32,11 +32,11 @@
             return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
         }
 
-        final int leftScore = left.sbn.getScore();
-        final int rightScore = right.sbn.getScore();
-        if (leftScore != rightScore) {
+        final int leftImportance = left.getImportance();
+        final int rightImportance = right.getImportance();
+        if (leftImportance != rightImportance) {
             // by priority, high to low
-            return -1 * Integer.compare(leftScore, rightScore);
+            return -1 * Integer.compare(leftImportance, rightImportance);
         }
 
         final float leftPeople = left.getContactAffinity();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c83012c..dd5baec 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -34,6 +34,7 @@
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_PEEK;
 import static android.service.notification.NotificationListenerService.TRIM_FULL;
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
+import static  android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -181,16 +182,6 @@
     static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
 
     static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
-    static final boolean SCORE_ONGOING_HIGHER = false;
-
-    static final int JUNK_SCORE = -1000;
-    static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
-    static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
-
-    // Notifications with scores below this will not interrupt the user, either via LED or
-    // sound or vibration
-    static final int SCORE_INTERRUPTION_THRESHOLD =
-            Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
 
     static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
     static final boolean ENABLE_BLOCKED_TOASTS = true;
@@ -2158,31 +2149,15 @@
 
                 synchronized (mNotificationList) {
 
-                    // === Scoring ===
-
-                    // 0. Sanitize inputs
+                    // Sanitize inputs
                     notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
                             Notification.PRIORITY_MAX);
-                    // Migrate notification flags to scores
-                    if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
-                        if (notification.priority < Notification.PRIORITY_MAX) {
-                            notification.priority = Notification.PRIORITY_MAX;
-                        }
-                    } else if (SCORE_ONGOING_HIGHER &&
-                            0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
-                        if (notification.priority < Notification.PRIORITY_HIGH) {
-                            notification.priority = Notification.PRIORITY_HIGH;
-                        }
-                    }
 
-                    // 1. initial score: buckets of 10, around the app [-20..20]
-                    final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
-
-                    // 2. extract ranking signals from the notification data
+                    // setup local book-keeping
                     final StatusBarNotification n = new StatusBarNotification(
-                            pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
+                            pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
                             user);
-                    NotificationRecord r = new NotificationRecord(n, score);
+                    NotificationRecord r = new NotificationRecord(getContext(), n);
                     NotificationRecord old = mNotificationsByKey.get(n.getKey());
                     if (old != null) {
                         // Retain ranking information from previous record
@@ -2217,23 +2192,16 @@
                     mRankingHelper.extractSignals(r);
                     savePolicyFile();
 
-                    // 3. Apply local rules
-
                     // blocked apps
                     if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
                         if (!isSystemNotification) {
-                            r.score = JUNK_SCORE;
                             Slog.e(TAG, "Suppressing notification from package " + pkg
                                     + " by user request.");
                             mUsageStats.registerBlocked(r);
+                            return;
                         }
                     }
 
-                    if (r.score < SCORE_DISPLAY_THRESHOLD) {
-                        // Notification will be blocked because the score is too low.
-                        return;
-                    }
-
                     int index = indexOfNotificationLocked(n.getKey());
                     if (index < 0) {
                         mNotificationList.add(r);
@@ -2385,7 +2353,7 @@
         final Notification notification = record.sbn.getNotification();
 
         // Should this notification make noise, vibe, or use the LED?
-        final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
+        final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_HIGH;
         final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
         if (DBG || record.isIntercepted())
             Slog.v(TAG,
@@ -3251,24 +3219,29 @@
         final int N = mNotificationList.size();
         ArrayList<String> keys = new ArrayList<String>(N);
         ArrayList<String> interceptedKeys = new ArrayList<String>(N);
+        ArrayList<Integer> importance = new ArrayList<>(N);
         Bundle visibilityOverrides = new Bundle();
         Bundle suppressedVisualEffects = new Bundle();
+        Bundle explanation = new Bundle();
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
             if (!isVisibleToListener(record.sbn, info)) {
                 continue;
             }
-            keys.add(record.sbn.getKey());
+            final String key = record.sbn.getKey();
+            keys.add(key);
+            importance.add(record.getImportance());
+            if (record.getImportanceExplanation() != null) {
+                explanation.putCharSequence(key, record.getImportanceExplanation());
+            }
             if (record.isIntercepted()) {
-                interceptedKeys.add(record.sbn.getKey());
+                interceptedKeys.add(key);
 
             }
-            suppressedVisualEffects.putInt(
-                    record.sbn.getKey(), record.getSuppressedVisualEffects());
+            suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
             if (record.getPackageVisibilityOverride()
                     != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
-                visibilityOverrides.putInt(record.sbn.getKey(),
-                        record.getPackageVisibilityOverride());
+                visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
             }
             // Find first min-prio notification for speedbump placement.
             if (speedBumpIndex == -1 &&
@@ -3283,10 +3256,15 @@
                 speedBumpIndex = keys.size() - 1;
             }
         }
-        String[] keysAr = keys.toArray(new String[keys.size()]);
+        final int M = keys.size();
+        String[] keysAr = keys.toArray(new String[M]);
         String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
+        int[] importanceAr = new int[M];
+        for (int i = 0; i < M; i++) {
+            importanceAr[i] = importance.get(i);
+        }
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
-                speedBumpIndex, suppressedVisualEffects);
+                speedBumpIndex, suppressedVisualEffects, importanceAr, explanation);
     }
 
     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 3b7384e..463c0b0 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -15,6 +15,11 @@
  */
 package com.android.server.notification;
 
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_LOW;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
+
 import android.app.Notification;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -49,10 +54,10 @@
 public final class NotificationRecord {
     final StatusBarNotification sbn;
     final int mOriginalFlags;
+    private final Context mContext;
 
     NotificationUsageStats.SingleNotificationStats stats;
     boolean isCanceled;
-    int score;
     /** Whether the notification was seen by the user via one of the notification listeners. */
     boolean mIsSeen;
 
@@ -84,18 +89,71 @@
     private String mGlobalSortKey;
     private int mPackageVisibility;
     private int mTopicImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+    private int mImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+    private CharSequence mImportanceExplanation = null;
 
     private int mSuppressedVisualEffects = 0;
+    private String mTopicExplanation;
+    private String mPeopleExplanation;
 
     @VisibleForTesting
-    public NotificationRecord(StatusBarNotification sbn, int score)
+    public NotificationRecord(Context context, StatusBarNotification sbn)
     {
         this.sbn = sbn;
-        this.score = score;
         mOriginalFlags = sbn.getNotification().flags;
         mRankingTimeMs = calculateRankingTimeMs(0L);
         mCreationTimeMs = sbn.getPostTime();
         mUpdateTimeMs = mCreationTimeMs;
+        mContext = context;
+        mImportance = defaultImportance();
+    }
+
+    private int defaultImportance() {
+        final Notification n = sbn.getNotification();
+        int importance = IMPORTANCE_DEFAULT;
+
+        // Migrate notification flags to scores
+        if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) {
+            n.priority = Notification.PRIORITY_MAX;
+        }
+
+        switch (n.priority) {
+            case Notification.PRIORITY_MIN:
+            case Notification.PRIORITY_LOW:
+                importance = IMPORTANCE_LOW;
+                break;
+            case Notification.PRIORITY_DEFAULT:
+                importance = IMPORTANCE_DEFAULT;
+                break;
+            case Notification.PRIORITY_HIGH:
+                importance = IMPORTANCE_HIGH;
+                break;
+            case Notification.PRIORITY_MAX:
+                importance = IMPORTANCE_MAX;
+                break;
+        }
+
+        boolean isNoisy = (n.defaults & Notification.DEFAULT_SOUND) != 0
+                || (n.defaults & Notification.DEFAULT_VIBRATE) != 0
+                || n.sound != null
+                || n.vibrate != null;
+        if (!isNoisy && importance > IMPORTANCE_DEFAULT) {
+            importance = IMPORTANCE_DEFAULT;
+        }
+        // maybe only do this for target API < N?
+        if (isNoisy) {
+            if (importance == IMPORTANCE_HIGH) {
+                importance = IMPORTANCE_MAX;
+            } else {
+                importance = IMPORTANCE_HIGH;
+            }
+        }
+
+        if (n.fullScreenIntent != null) {
+            importance = IMPORTANCE_MAX;
+        }
+
+        return importance;
     }
 
     // copy any notes that the ranking system may have made before the update
@@ -109,6 +167,8 @@
         mCreationTimeMs = previous.mCreationTimeMs;
         mVisibleSinceMs = previous.mVisibleSinceMs;
         mTopicImportance = previous.mTopicImportance;
+        mImportance = previous.mImportance;
+        mImportanceExplanation = previous.mImportanceExplanation;
         // Don't copy mGlobalSortKey, recompute it.
     }
 
@@ -129,7 +189,7 @@
         pw.println(prefix + this);
         pw.println(prefix + "  uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
         pw.println(prefix + "  icon=" + iconStr);
-        pw.println(prefix + "  pri=" + notification.priority + " score=" + sbn.getScore());
+        pw.println(prefix + "  pri=" + notification.priority);
         pw.println(prefix + "  key=" + sbn.getKey());
         pw.println(prefix + "  seen=" + mIsSeen);
         pw.println(prefix + "  groupKey=" + getGroupKey());
@@ -158,6 +218,7 @@
                         action.actionIntent.toString()
                         ));
             }
+            }
             pw.println(prefix + "  }");
         }
         if (notification.extras != null && notification.extras.size() > 0) {
@@ -200,6 +261,9 @@
         pw.println(prefix + "  mPackageVisibility=" + mPackageVisibility);
         pw.println(prefix + "  mTopicImportance="
                 + NotificationListenerService.Ranking.importanceToString(mTopicImportance));
+        pw.println(prefix + "  mImportance="
+                + NotificationListenerService.Ranking.importanceToString(mImportance));
+        pw.println(prefix + "  mImportanceExplanation=" + mImportanceExplanation);
         pw.println(prefix + "  mIntercept=" + mIntercept);
         pw.println(prefix + "  mGlobalSortKey=" + mGlobalSortKey);
         pw.println(prefix + "  mRankingTimeMs=" + mRankingTimeMs);
@@ -234,15 +298,19 @@
     @Override
     public final String toString() {
         return String.format(
-                "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
+                "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s: %s)",
                 System.identityHashCode(this),
                 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
-                this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
+                this.sbn.getTag(), this.mImportance, this.sbn.getKey(),
                 this.sbn.getNotification());
     }
 
     public void setContactAffinity(float contactAffinity) {
         mContactAffinity = contactAffinity;
+        if (mImportance < IMPORTANCE_DEFAULT &&
+                mContactAffinity > ValidateNotificationPeople.VALID_CONTACT) {
+            setImportance(IMPORTANCE_DEFAULT, getPeopleExplanation());
+        }
     }
 
     public float getContactAffinity() {
@@ -276,6 +344,30 @@
     public void setTopicImportance(int importance) {
         if (importance != NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED) {
             mTopicImportance = importance;
+            applyTopicImportance();
+        }
+    }
+
+    private String getTopicExplanation() {
+        if (mTopicExplanation == null) {
+            mTopicExplanation =
+                    mContext.getString(com.android.internal.R.string.importance_from_topic);
+        }
+        return mTopicExplanation;
+    }
+
+    private String getPeopleExplanation() {
+        if (mPeopleExplanation == null) {
+            mPeopleExplanation =
+                    mContext.getString(com.android.internal.R.string.importance_from_person);
+        }
+        return mPeopleExplanation;
+    }
+
+    private void applyTopicImportance() {
+        if (mTopicImportance != NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED) {
+            mImportance = mTopicImportance;
+            mImportanceExplanation = getTopicExplanation();
         }
     }
 
@@ -283,6 +375,22 @@
         return mTopicImportance;
     }
 
+    public void setImportance(int importance, CharSequence explanation) {
+        if (importance != NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED) {
+            mImportance = importance;
+            mImportanceExplanation = explanation;
+        }
+        applyTopicImportance();
+    }
+
+    public int getImportance() {
+        return mImportance;
+    }
+
+    public CharSequence getImportanceExplanation() {
+        return mImportanceExplanation;
+    }
+
     public boolean setIntercepted(boolean intercept) {
         mIntercept = intercept;
         return mIntercept;
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
index d0065cd..df7b412 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -64,8 +64,8 @@
                 .setWhen(1205)
                 .setTopic(new Notification.Topic("A", "a"))
                 .build();
-        mRecordGroupGSortA = new NotificationRecord(new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user), 0);
+        mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification(
+                "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user));
 
         mNotiGroupGSortB = new Notification.Builder(getContext())
                 .setContentTitle("B")
@@ -74,24 +74,24 @@
                 .setWhen(1200)
                 .setTopic(new Notification.Topic("A", "a"))
                 .build();
-        mRecordGroupGSortB = new NotificationRecord(new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user), 0);
+        mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification(
+                "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user));
 
         mNotiNoGroup = new Notification.Builder(getContext())
                 .setContentTitle("C")
                 .setWhen(1201)
                 .setTopic(new Notification.Topic("C", "c"))
                 .build();
-        mRecordNoGroup = new NotificationRecord(new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, 0, mNotiNoGroup, user), 0);
+        mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification(
+                "package", "package", 1, null, 0, 0, 0, mNotiNoGroup, user));
 
         mNotiNoGroup2 = new Notification.Builder(getContext())
                 .setContentTitle("D")
                 .setWhen(1202)
                 .setTopic(new Notification.Topic("D", "d"))
                 .build();
-        mRecordNoGroup2 = new NotificationRecord(new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, 0, mNotiNoGroup2, user), 0);
+        mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification(
+                "package", "package", 1, null, 0, 0, 0, mNotiNoGroup2, user));
 
         mNotiNoGroupSortA = new Notification.Builder(getContext())
                 .setContentTitle("E")
@@ -99,8 +99,8 @@
                 .setSortKey("A")
                 .setTopic(new Notification.Topic("E", "e"))
                 .build();
-        mRecordNoGroupSortA = new NotificationRecord(new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user), 0);
+        mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
+                "package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user));
     }
 
     @SmallTest