OSDN Git Service

Add badging support for channels.
authorJulia Reynolds <juliacr@google.com>
Thu, 15 Dec 2016 16:34:26 +0000 (11:34 -0500)
committerJulia Reynolds <juliacr@google.com>
Wed, 21 Dec 2016 21:12:23 +0000 (21:12 +0000)
In this iteration badges are a user opt in feature.
Known issue: all listeners will receive 'badge only' notifications.

Test: runtest systemui-notification

Change-Id: Ic7450bf4de5351cfdc72bd96ec946fe6e035035c

16 files changed:
api/current.txt
api/system-current.txt
api/test-current.txt
core/java/android/app/INotificationManager.aidl
core/java/android/app/NotificationChannel.java
core/java/android/app/NotificationManager.java
packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
services/core/java/com/android/server/notification/ImportanceExtractor.java
services/core/java/com/android/server/notification/NotificationManagerService.java
services/core/java/com/android/server/notification/PriorityExtractor.java
services/core/java/com/android/server/notification/RankingConfig.java
services/core/java/com/android/server/notification/RankingHelper.java
services/core/java/com/android/server/notification/VisibilityExtractor.java
services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
services/tests/notification/src/com/android/server/notification/RankingHelperTest.java

index 395d7aa..bf0c9f6 100644 (file)
@@ -5381,6 +5381,7 @@ package android.app {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
     ctor protected NotificationChannel(android.os.Parcel);
     method public boolean canBypassDnd();
+    method public boolean canShowBadge();
     method public int describeContents();
     method public void enableVibration(boolean);
     method public java.lang.String getId();
@@ -5389,10 +5390,13 @@ package android.app {
     method public java.lang.CharSequence getName();
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
+    method public boolean isAllowed();
+    method public void setAllowed(boolean);
     method public void setBypassDnd(boolean);
     method public void setImportance(int);
     method public void setLights(boolean);
     method public void setLockscreenVisibility(int);
+    method public void setShowBadge(boolean);
     method public void setSound(android.net.Uri);
     method public void setVibrationPattern(long[]);
     method public boolean shouldShowLights();
index f38b02e..6ecbb02 100644 (file)
@@ -5538,6 +5538,7 @@ package android.app {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
     ctor protected NotificationChannel(android.os.Parcel);
     method public boolean canBypassDnd();
+    method public boolean canShowBadge();
     method public int describeContents();
     method public void enableVibration(boolean);
     method public java.lang.String getId();
@@ -5547,12 +5548,15 @@ package android.app {
     method public android.net.Uri getSound();
     method public int getUserLockedFields();
     method public long[] getVibrationPattern();
+    method public boolean isAllowed();
     method public void lockFields(int);
     method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
+    method public void setAllowed(boolean);
     method public void setBypassDnd(boolean);
     method public void setImportance(int);
     method public void setLights(boolean);
     method public void setLockscreenVisibility(int);
+    method public void setShowBadge(boolean);
     method public void setSound(android.net.Uri);
     method public void setVibrationPattern(long[]);
     method public boolean shouldShowLights();
@@ -5562,9 +5566,12 @@ package android.app {
     method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
     field public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR;
     field public static final java.lang.String DEFAULT_CHANNEL_ID = "miscellaneous";
+    field public static final int[] LOCKABLE_FIELDS;
+    field public static final int USER_LOCKED_ALLOWED = 64; // 0x40
     field public static final int USER_LOCKED_IMPORTANCE = 4; // 0x4
     field public static final int USER_LOCKED_LIGHTS = 8; // 0x8
     field public static final int USER_LOCKED_PRIORITY = 1; // 0x1
+    field public static final int USER_LOCKED_SHOW_BADGE = 128; // 0x80
     field public static final int USER_LOCKED_SOUND = 32; // 0x20
     field public static final int USER_LOCKED_VIBRATION = 16; // 0x10
     field public static final int USER_LOCKED_VISIBILITY = 2; // 0x2
index e43d1b5..9eca670 100644 (file)
@@ -5391,6 +5391,7 @@ package android.app {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
     ctor protected NotificationChannel(android.os.Parcel);
     method public boolean canBypassDnd();
+    method public boolean canShowBadge();
     method public int describeContents();
     method public void enableVibration(boolean);
     method public java.lang.String getId();
@@ -5399,10 +5400,13 @@ package android.app {
     method public java.lang.CharSequence getName();
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
+    method public boolean isAllowed();
+    method public void setAllowed(boolean);
     method public void setBypassDnd(boolean);
     method public void setImportance(int);
     method public void setLights(boolean);
     method public void setLockscreenVisibility(int);
+    method public void setShowBadge(boolean);
     method public void setSound(android.net.Uri);
     method public void setVibrationPattern(long[]);
     method public boolean shouldShowLights();
index 2c4e7a0..211aa07 100644 (file)
@@ -50,13 +50,6 @@ interface INotificationManager
     void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
     boolean areNotificationsEnabledForPackage(String pkg, int uid);
     boolean areNotificationsEnabled(String pkg);
-
-    void setVisibilityOverride(String pkg, int uid, int visibility);
-    int getVisibilityOverride(String pkg, int uid);
-    void setPriority(String pkg, int uid, int priority);
-    int getPriority(String pkg, int uid);
-    void setImportance(String pkg, int uid, int importance);
-    int getImportance(String pkg, int uid);
     int getPackageImportance(String pkg);
 
     void createNotificationChannel(String pkg, in NotificationChannel channel,
index 79a01c2..52d9b67 100644 (file)
@@ -53,7 +53,9 @@ public final class NotificationChannel implements Parcelable {
     private static final String ATT_SOUND = "sound";
     //TODO: add audio attributes support
     private static final String ATT_AUDIO_ATTRIBUTES = "audio_attributes";
+    private static final String ATT_SHOW_BADGE = "show_badge";
     private static final String ATT_USER_LOCKED = "locked";
+    private static final String ATT_ALLOWED = "allowed";
     private static final String DELIMITER = ",";
 
     /**
@@ -87,10 +89,39 @@ public final class NotificationChannel implements Parcelable {
     @SystemApi
     public static final int USER_LOCKED_SOUND = 0x00000020;
 
+    /**
+     * @hide
+     */
+    @SystemApi
+    public static final int USER_LOCKED_ALLOWED = 0x00000040;
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public static final int USER_LOCKED_SHOW_BADGE = 0x00000080;
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public static final int[] LOCKABLE_FIELDS = new int[] {
+            USER_LOCKED_PRIORITY,
+            USER_LOCKED_VISIBILITY,
+            USER_LOCKED_IMPORTANCE,
+            USER_LOCKED_LIGHTS,
+            USER_LOCKED_VIBRATION,
+            USER_LOCKED_SOUND,
+            USER_LOCKED_ALLOWED,
+            USER_LOCKED_SHOW_BADGE
+    };
+
+
     private static final int DEFAULT_VISIBILITY =
             NotificationManager.VISIBILITY_NO_OVERRIDE;
     private static final int DEFAULT_IMPORTANCE =
             NotificationManager.IMPORTANCE_UNSPECIFIED;
+    private static final boolean DEFAULT_ALLOWED = true;
 
     private final String mId;
     private CharSequence mName;
@@ -102,6 +133,8 @@ public final class NotificationChannel implements Parcelable {
     private long[] mVibration;
     private int mUserLockedFields;
     private boolean mVibrationEnabled;
+    private boolean mShowBadge;
+    private boolean mAllowed = DEFAULT_ALLOWED;
 
     /**
      * Creates a notification channel.
@@ -137,6 +170,8 @@ public final class NotificationChannel implements Parcelable {
         mVibration = in.createLongArray();
         mUserLockedFields = in.readInt();
         mVibrationEnabled = in.readByte() != 0;
+        mShowBadge = in.readByte() != 0;
+        mAllowed = in.readByte() != 0;
     }
 
     @Override
@@ -161,6 +196,8 @@ public final class NotificationChannel implements Parcelable {
         dest.writeLongArray(mVibration);
         dest.writeInt(mUserLockedFields);
         dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0);
+        dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0);
+        dest.writeByte(mAllowed ? (byte) 1 : (byte) 0);
     }
 
     /**
@@ -174,30 +211,30 @@ public final class NotificationChannel implements Parcelable {
     // Modifiable by a notification ranker.
 
     /**
-     * Only modifiable by the system and notification ranker.
-     *
-     * Sets whether or not this notification can interrupt the user in
+     * Sets whether or not notifications posted to this channel can interrupt the user in
      * {@link android.app.NotificationManager.Policy#INTERRUPTION_FILTER_PRIORITY} mode.
+     *
+     * Only modifiable by the system and notification ranker.
      */
     public void setBypassDnd(boolean bypassDnd) {
         this.mBypassDnd = bypassDnd;
     }
 
     /**
-     * Only modifiable by the system and notification ranker.
+     * Sets whether notifications posted to this channel appear on the lockscreen or not, and if so,
+     * whether they appear in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}.
      *
-     * Sets whether this notification appears on the lockscreen or not, and if so, whether it
-     * appears in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}.
+     * Only modifiable by the system and notification ranker.
      */
     public void setLockscreenVisibility(int lockscreenVisibility) {
         this.mLockscreenVisibility = lockscreenVisibility;
     }
 
     /**
-     * Only modifiable by the system and notification ranker.
-     *
      * Sets the level of interruption of this notification channel.
      *
+     * Only modifiable by the system and notification ranker.
+     *
      * @param importance the amount the user should be interrupted by notifications from this
      *                   channel. See e.g.
      *                   {@link android.app.NotificationManager#IMPORTANCE_DEFAULT}.
@@ -206,6 +243,30 @@ public final class NotificationChannel implements Parcelable {
         this.mImportance = importance;
     }
 
+    /**
+     * Sets whether notifications posted to this channel can appear as application icon badges
+     * in a Launcher.
+     *
+     * Only modifiable by the system and notification ranker.
+     *
+     * @param showBadge true if badges should be allowed to be shown.
+     */
+    public void setShowBadge(boolean showBadge) {
+        this.mShowBadge = showBadge;
+    }
+
+    /**
+     * Sets whether notifications are allowed to be posted to this channel.
+     *
+     * Only modifiable by the system and notification ranker.
+     *
+     * @param allowed true if notifications are not allowed from this channel.
+     */
+    public void setAllowed(boolean allowed) {
+        this.mAllowed = allowed;
+    }
+
+
     // Modifiable by apps on channel creation.
 
     /**
@@ -311,6 +372,21 @@ public final class NotificationChannel implements Parcelable {
     }
 
     /**
+     * Returns whether notifications posted to this channel can appear as badges in a Launcher
+     * application.
+     */
+    public boolean canShowBadge() {
+        return mShowBadge;
+    }
+
+    /**
+     * Returns whether notifications are allowed to post to this channel.
+     */
+    public boolean isAllowed() {
+        return mAllowed;
+    }
+
+    /**
      * @hide
      */
     @SystemApi
@@ -331,6 +407,8 @@ public final class NotificationChannel implements Parcelable {
         setLights(safeBool(parser, ATT_LIGHTS, false));
         enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
         setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
+        setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false));
+        setAllowed(safeBool(parser, ATT_ALLOWED, true));
         lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
     }
 
@@ -369,6 +447,12 @@ public final class NotificationChannel implements Parcelable {
         if (getUserLockedFields() != 0) {
             out.attribute(null, ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
         }
+        if (canShowBadge()) {
+            out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
+        }
+        if (!isAllowed()) {
+            out.attribute(null, ATT_ALLOWED, Boolean.toString(isAllowed()));
+        }
 
         out.endTag(null, TAG_CHANNEL);
     }
@@ -398,6 +482,8 @@ public final class NotificationChannel implements Parcelable {
         record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate()));
         record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
         record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern()));
+        record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
+        record.put(ATT_ALLOWED, Boolean.toString(isAllowed()));
 
         return record;
     }
@@ -481,6 +567,8 @@ public final class NotificationChannel implements Parcelable {
         if (mLights != that.mLights) return false;
         if (mUserLockedFields != that.mUserLockedFields) return false;
         if (mVibrationEnabled != that.mVibrationEnabled) return false;
+        if (mShowBadge != that.mShowBadge) return false;
+        if (mAllowed != that.mAllowed) return false;
         if (mId != null ? !mId.equals(that.mId) : that.mId != null) return false;
         if (mName != null ? !mName.equals(that.mName) : that.mName != null) return false;
         if (mSound != null ? !mSound.equals(that.mSound) : that.mSound != null) return false;
@@ -500,10 +588,11 @@ public final class NotificationChannel implements Parcelable {
         result = 31 * result + Arrays.hashCode(mVibration);
         result = 31 * result + mUserLockedFields;
         result = 31 * result + (mVibrationEnabled ? 1 : 0);
+        result = 31 * result + (mShowBadge ? 1 : 0);
+        result = 31 * result + (mAllowed ? 1 : 0);
         return result;
     }
 
-
     @Override
     public String toString() {
         return "NotificationChannel{" +
@@ -517,6 +606,8 @@ public final class NotificationChannel implements Parcelable {
                 ", mVibration=" + Arrays.toString(mVibration) +
                 ", mUserLockedFields=" + mUserLockedFields +
                 ", mVibrationEnabled=" + mVibrationEnabled +
+                ", mShowBadge=" + mShowBadge +
+                ", mAllowed=" + mAllowed +
                 '}';
     }
 }
index 7693d76..a9ff482 100644 (file)
@@ -202,7 +202,7 @@ public class NotificationManager
     public static final int IMPORTANCE_UNSPECIFIED = -1000;
 
     /**
-     * A notification with no importance: shows nowhere, is blocked.
+     * A notification with no importance: does not show in the shade.
      */
     public static final int IMPORTANCE_NONE = 0;
 
index ed1179a..58b8284 100644 (file)
@@ -182,10 +182,6 @@ public class NotificationGuts extends LinearLayout implements TunerService.Tunab
         mINotificationManager = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
         mStartingUserImportance = NotificationManager.IMPORTANCE_UNSPECIFIED;
-        try {
-            mStartingUserImportance =
-                    mINotificationManager.getImportance(sbn.getPackageName(), sbn.getUid());
-        } catch (RemoteException e) {}
         mNotificationImportance = importance;
         boolean nonBlockable = false;
         try {
@@ -220,11 +216,6 @@ public class NotificationGuts extends LinearLayout implements TunerService.Tunab
         int progress = getSelectedImportance();
         MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
                 progress - mStartingUserImportance);
-        try {
-            mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(), progress);
-        } catch (RemoteException e) {
-            // :(
-        }
     }
 
     private int getSelectedImportance() {
index 3bdc22c..46ec92b 100644 (file)
@@ -15,7 +15,6 @@
 */
 package com.android.server.notification;
 
-import android.app.NotificationManager;
 import android.content.Context;
 import android.util.Slog;
 
@@ -42,17 +41,8 @@ public class ImportanceExtractor implements NotificationSignalExtractor {
             if (DBG) Slog.d(TAG, "missing config");
             return null;
         }
-        int importance = NotificationManager.IMPORTANCE_UNSPECIFIED;
-        int appImportance = mConfig.getImportance(
-                record.sbn.getPackageName(), record.sbn.getUid());
-        int channelImportance = record.getChannel().getImportance();
-        if (appImportance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
-            record.setUserImportance(channelImportance);
-        } else if (channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
-            record.setUserImportance(appImportance);
-        } else {
-            record.setUserImportance(Math.min(appImportance, channelImportance));
-        }
+        record.setUserImportance(record.getChannel().getImportance());
+
         return null;
     }
 
index a6fb458..a3181e4 100644 (file)
@@ -1504,56 +1504,17 @@ public class NotificationManagerService extends SystemService {
         }
 
         @Override
-        public void setPriority(String pkg, int uid, int priority) {
-            checkCallerIsSystem();
-            mRankingHelper.setPriority(pkg, uid, priority);
-            savePolicyFile();
-        }
-
-        @Override
-        public int getPriority(String pkg, int uid) {
-            checkCallerIsSystem();
-            return mRankingHelper.getPriority(pkg, uid);
-        }
-
-        @Override
-        public void setVisibilityOverride(String pkg, int uid, int visibility) {
-            checkCallerIsSystem();
-            mRankingHelper.setVisibilityOverride(pkg, uid, visibility);
-            savePolicyFile();
-        }
-
-        @Override
-        public int getVisibilityOverride(String pkg, int uid) {
-            checkCallerIsSystem();
-            return mRankingHelper.getVisibilityOverride(pkg, uid);
-        }
-
-        @Override
-        public void setImportance(String pkg, int uid, int importance) {
-            enforceSystemOrSystemUI("Caller not system or systemui");
-            setNotificationsEnabledForPackageImpl(pkg, uid, importance != IMPORTANCE_NONE);
-            mRankingHelper.setImportance(pkg, uid, importance);
-            savePolicyFile();
-        }
-
-        @Override
         public int getPackageImportance(String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
             return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
         }
 
         @Override
-        public int getImportance(String pkg, int uid) {
-            enforceSystemOrSystemUI("Caller not system or systemui");
-            return mRankingHelper.getImportance(pkg, uid);
-        }
-
-        @Override
         public void createNotificationChannel(String pkg, NotificationChannel channel,
                 IOnNotificationChannelCreatedListener listener) throws RemoteException {
             checkCallerIsSystemOrSameApp(pkg);
-            mRankingHelper.createNotificationChannel(pkg, Binder.getCallingUid(), channel);
+            mRankingHelper.createNotificationChannel(pkg, Binder.getCallingUid(), channel,
+                    true /* fromTargetApp */);
             savePolicyFile();
             listener.onNotificationChannelCreated(channel);
         }
@@ -1587,12 +1548,13 @@ public class NotificationManagerService extends SystemService {
         public void updateNotificationChannelForPackage(String pkg, int uid,
                 NotificationChannel channel) {
             checkCallerIsSystem();
-            if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
-                // cancel
+            if (!channel.isAllowed()) {
                 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
-                        UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
+                        UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
+                        null);
             }
             mRankingHelper.updateNotificationChannel(pkg, uid, channel);
+            mRankingHandler.requestSort(true);
             savePolicyFile();
         }
 
@@ -2412,7 +2374,7 @@ public class NotificationManagerService extends SystemService {
                 NotificationChannel channel) throws RemoteException {
             ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
             int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
-            mRankingHelper.createNotificationChannel(pkg, uid, channel);
+            mRankingHelper.createNotificationChannel(pkg, uid, channel, false /* fromTargetApp */);
             savePolicyFile();
         }
 
@@ -2435,13 +2397,14 @@ public class NotificationManagerService extends SystemService {
         public void updateNotificationChannelFromAssistant(INotificationListener token, String pkg,
                 NotificationChannel channel) throws RemoteException {
             ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
-            if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
+            if (!channel.isAllowed()) {
                 // cancel
                 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
                         info.userid, REASON_CHANNEL_BANNED, null);
             }
             int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
             mRankingHelper.updateNotificationChannelFromAssistant(pkg, uid, channel);
+            mRankingHandler.requestSort(true);
             savePolicyFile();
         }
 
@@ -2876,7 +2839,7 @@ public class NotificationManagerService extends SystemService {
         idOut[0] = id;
     }
 
-    private class EnqueueNotificationRunnable implements Runnable {
+    protected class EnqueueNotificationRunnable implements Runnable {
         private final NotificationRecord r;
         private final int userId;
 
@@ -2935,23 +2898,9 @@ public class NotificationManagerService extends SystemService {
 
                 mRankingHelper.extractSignals(r);
 
-                final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
-
                 // blocked apps
-                if (r.getImportance() == NotificationManager.IMPORTANCE_NONE
-                        || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE
-                        || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {
-                    if (!isSystemNotification) {
-                        if (isPackageSuspended) {
-                            Slog.e(TAG, "Suppressing notification from package due to package "
-                                    + "suspended by administrator.");
-                            mUsageStats.registerSuspendedByAdmin(r);
-                        } else {
-                            Slog.e(TAG, "Suppressing notification from package by user request.");
-                            mUsageStats.registerBlocked(r);
-                        }
-                        return;
-                    }
+                if (isBlocked(r, mUsageStats)) {
+                    return;
                 }
 
                 // tell the assistant service about the notification
@@ -3018,6 +2967,28 @@ public class NotificationManagerService extends SystemService {
                 buzzBeepBlinkLocked(r);
             }
         }
+
+        protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
+            final String pkg = r.sbn.getPackageName();
+            final int callingUid = r.sbn.getUid();
+
+            final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
+            if (isPackageSuspended) {
+                Slog.e(TAG, "Suppressing notification from package due to package "
+                        + "suspended by administrator.");
+                usageStats.registerSuspendedByAdmin(r);
+                return isPackageSuspended;
+            }
+
+            final boolean isBlocked = r.getImportance() == NotificationManager.IMPORTANCE_NONE
+                    || !r.getChannel().isAllowed()
+                    || !noteNotificationOp(pkg, callingUid);
+            if (isBlocked) {
+                Slog.e(TAG, "Suppressing notification from package by user request.");
+                    usageStats.registerBlocked(r);
+            }
+            return isBlocked;
+        }
     }
 
     /**
index 666cf00..5d5d39d 100644 (file)
@@ -43,11 +43,8 @@ public class PriorityExtractor implements NotificationSignalExtractor {
             return null;
         }
 
-        int priority = mConfig.getPriority(record.sbn.getPackageName(), record.sbn.getUid());
-        if (priority == Notification.PRIORITY_DEFAULT && record.getChannel().canBypassDnd()){
-            priority = Notification.PRIORITY_MAX;
-        }
-        record.setPackagePriority(priority);
+        record.setPackagePriority(record.getChannel().canBypassDnd()
+                ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT);
 
         return null;
     }
index 882e84c..5c1e99c 100644 (file)
@@ -20,19 +20,11 @@ import android.content.pm.ParceledListSlice;
 
 public interface RankingConfig {
 
-    int getPriority(String packageName, int uid);
-
-    void setPriority(String packageName, int uid, int priority);
-
-    int getVisibilityOverride(String packageName, int uid);
-
-    void setVisibilityOverride(String packageName, int uid, int visibility);
-
     void setImportance(String packageName, int uid, int importance);
-
     int getImportance(String packageName, int uid);
 
-    void createNotificationChannel(String pkg, int uid, NotificationChannel channel);
+    void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
+            boolean fromTargetApp);
     void updateNotificationChannel(String pkg, int uid, NotificationChannel channel);
     void updateNotificationChannelFromAssistant(String pkg, int uid, NotificationChannel channel);
     NotificationChannel getNotificationChannel(String pkg, int uid, String channelId);
index 98d4c69..6474613 100644 (file)
@@ -411,40 +411,6 @@ public class RankingHelper implements RankingConfig {
     }
 
     /**
-     * Gets priority.
-     */
-    @Override
-    public int getPriority(String packageName, int uid) {
-        return getOrCreateRecord(packageName, uid).priority;
-    }
-
-    /**
-     * Sets priority.
-     */
-    @Override
-    public void setPriority(String packageName, int uid, int priority) {
-        getOrCreateRecord(packageName, uid).priority = priority;
-        updateConfig();
-    }
-
-    /**
-     * Gets visual override.
-     */
-    @Override
-    public int getVisibilityOverride(String packageName, int uid) {
-        return getOrCreateRecord(packageName, uid).visibility;
-    }
-
-    /**
-     * Sets visibility override.
-     */
-    @Override
-    public void setVisibilityOverride(String pkgName, int uid, int visibility) {
-        getOrCreateRecord(pkgName, uid).visibility = visibility;
-        updateConfig();
-    }
-
-    /**
      * Gets importance.
      */
     @Override
@@ -453,7 +419,8 @@ public class RankingHelper implements RankingConfig {
     }
 
     @Override
-    public void createNotificationChannel(String pkg, int uid, NotificationChannel channel) {
+    public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
+            boolean fromTargetApp) {
         Preconditions.checkNotNull(pkg);
         Preconditions.checkNotNull(channel);
         Preconditions.checkNotNull(channel.getId());
@@ -473,6 +440,14 @@ public class RankingHelper implements RankingConfig {
                 || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
             throw new IllegalArgumentException("Invalid importance level");
         }
+        // Reset fields that apps aren't allowed to set.
+        if (fromTargetApp) {
+            channel.setShowBadge(false);
+            channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
+            channel.setLockscreenVisibility(r.visibility);
+        }
+        channel.setAllowed(true);
+        clearLockedFields(channel);
         if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
             channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
         }
@@ -480,6 +455,14 @@ public class RankingHelper implements RankingConfig {
         updateConfig();
     }
 
+    private void clearLockedFields(NotificationChannel channel) {
+        int clearMask = 0;
+        for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
+            clearMask |= NotificationChannel.LOCKABLE_FIELDS[i];
+        }
+        channel.lockFields(~clearMask);
+    }
+
     @Override
     public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel) {
         Preconditions.checkNotNull(updatedChannel);
@@ -534,6 +517,12 @@ public class RankingHelper implements RankingConfig {
                 channel.setLockscreenVisibility(updatedChannel.getLockscreenVisibility());
             }
         }
+        if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_ALLOWED) == 0) {
+            channel.setAllowed(updatedChannel.isAllowed());
+        }
+        if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_SHOW_BADGE) == 0) {
+            channel.setShowBadge(updatedChannel.canShowBadge());
+        }
 
         r.channels.put(channel.getId(), channel);
         updateConfig();
index 9d0e506..37dbe3a 100644 (file)
@@ -43,12 +43,7 @@ public class VisibilityExtractor implements NotificationSignalExtractor {
             return null;
         }
 
-        int visibility =
-                mConfig.getVisibilityOverride(record.sbn.getPackageName(), record.sbn.getUid());
-        if (visibility == NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
-            visibility = record.getChannel().getLockscreenVisibility();
-        }
-        record.setPackageVisibilityOverride(visibility);
+        record.setPackageVisibilityOverride(record.getChannel().getLockscreenVisibility());
 
         return null;
     }
index 6bc9675..eee9cf1 100644 (file)
@@ -97,11 +97,11 @@ public class ImportanceExtractorTest {
 
         extractor.process(r);
 
-        assertEquals(r.getUserImportance(), NotificationManager.IMPORTANCE_MIN);
+        assertEquals(r.getUserImportance(), NotificationManager.IMPORTANCE_UNSPECIFIED);
     }
 
     @Test
-    public void testAppPreferenceChannelPermissive() throws Exception {
+    public void testAppPreferenceChannelPreference() throws Exception {
         ImportanceExtractor extractor = new ImportanceExtractor();
         extractor.setConfig(mConfig);
 
@@ -114,58 +114,6 @@ public class ImportanceExtractorTest {
 
         extractor.process(r);
 
-        assertEquals(r.getUserImportance(), NotificationManager.IMPORTANCE_MIN);
-    }
-
-    @Test
-    public void testAppPreferenceChannelStrict() throws Exception {
-        ImportanceExtractor extractor = new ImportanceExtractor();
-        extractor.setConfig(mConfig);
-
-        when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
-          NotificationManager.IMPORTANCE_HIGH);
-        NotificationChannel channel =
-                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_MIN);
-
-        NotificationRecord r = getNotificationRecord(channel);
-
-        extractor.process(r);
-
-        assertEquals(r.getUserImportance(), NotificationManager.IMPORTANCE_MIN);
-    }
-
-    @Test
-    public void testNoAppPreferenceChannelPreference() throws Exception {
-        ImportanceExtractor extractor = new ImportanceExtractor();
-        extractor.setConfig(mConfig);
-
-        when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
-          NotificationManager.IMPORTANCE_UNSPECIFIED);
-        NotificationChannel channel =
-                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_MIN);
-
-        NotificationRecord r = getNotificationRecord(channel);
-
-        extractor.process(r);
-
-        assertEquals(r.getUserImportance(), NotificationManager.IMPORTANCE_MIN);
-    }
-
-    @Test
-    public void testNoPreferences() throws Exception {
-        ImportanceExtractor extractor = new ImportanceExtractor();
-        extractor.setConfig(mConfig);
-
-        when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
-          NotificationManager.IMPORTANCE_UNSPECIFIED);
-        NotificationChannel channel =
-                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
-
-        NotificationRecord r = getNotificationRecord(channel);
-
-        extractor.process(r);
-
-        assertEquals(r.getUserImportance(), 
-             NotificationManager.IMPORTANCE_UNSPECIFIED);
+        assertEquals(r.getUserImportance(), NotificationManager.IMPORTANCE_HIGH);
     }
 }
index 80c4cce..e1c0166 100644 (file)
 
 package com.android.server.notification;
 
+import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.INotificationManager;
 import android.app.IOnNotificationChannelCreatedListener;
+import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.Context;
@@ -31,6 +38,8 @@ import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.os.Binder;
 import android.os.Handler;
+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;
@@ -42,8 +51,11 @@ import org.junit.runner.RunWith;
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class NotificationManagerServiceTest {
+    private final String pkg = "com.android.server.notification";
+    private final int uid = 0;
     private NotificationManagerService mNotificationManagerService;
     private INotificationManager mBinderService;
+    private IPackageManager mPackageManager = mock(IPackageManager.class);
 
     @Before
     public void setUp() throws Exception {
@@ -51,12 +63,11 @@ public class NotificationManagerServiceTest {
         mNotificationManagerService = new NotificationManagerService(context);
 
         // MockPackageManager - default returns ApplicationInfo with matching calling UID
-        final IPackageManager mockPackageManager = mock(IPackageManager.class);
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.uid = Binder.getCallingUid();
-        when(mockPackageManager.getApplicationInfo(any(), anyInt(), anyInt()))
+        when(mPackageManager.getApplicationInfo(any(), anyInt(), anyInt()))
                 .thenReturn(applicationInfo);
-        mNotificationManagerService.setPackageManager(mockPackageManager);
+        mNotificationManagerService.setPackageManager(mPackageManager);
         mNotificationManagerService.setHandler(new Handler(context.getMainLooper()));
 
         // Tests call directly into the Binder.
@@ -92,4 +103,63 @@ public class NotificationManagerServiceTest {
             // pass
         }
     }
+
+    @Test
+    public void testBlockedNotifications_suspended() throws Exception {
+        NotificationUsageStats usageStats = mock(NotificationUsageStats.class);
+        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(true);
+
+        NotificationChannel channel = new NotificationChannel("id", "name",
+                NotificationManager.IMPORTANCE_HIGH);
+        NotificationRecord r = generateNotificationRecord(channel);
+        NotificationManagerService.EnqueueNotificationRunnable enqueue =
+                mNotificationManagerService.new EnqueueNotificationRunnable(UserHandle.USER_SYSTEM,
+                        r);
+        assertTrue(enqueue.isBlocked(r, usageStats));
+        verify(usageStats, times(1)).registerSuspendedByAdmin(eq(r));
+    }
+
+    @Test
+    public void testBlockedNotifications_blockedChannel() throws Exception {
+        NotificationUsageStats usageStats = mock(NotificationUsageStats.class);
+        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+
+        NotificationChannel channel = new NotificationChannel("id", "name",
+                NotificationManager.IMPORTANCE_HIGH);
+        channel.setAllowed(false);
+        NotificationRecord r = generateNotificationRecord(channel);
+        NotificationManagerService.EnqueueNotificationRunnable enqueue =
+                mNotificationManagerService.new EnqueueNotificationRunnable(UserHandle.USER_SYSTEM,
+                        r);
+        assertTrue(enqueue.isBlocked(r, usageStats));
+        verify(usageStats, times(1)).registerBlocked(eq(r));
+    }
+
+    @Test
+    public void testBlockedNotifications_blockedApp() throws Exception {
+        NotificationUsageStats usageStats = mock(NotificationUsageStats.class);
+        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+
+        NotificationChannel channel = new NotificationChannel("id", "name",
+                NotificationManager.IMPORTANCE_HIGH);
+        NotificationRecord r = generateNotificationRecord(channel);
+        r.setUserImportance(NotificationManager.IMPORTANCE_NONE);
+        NotificationManagerService.EnqueueNotificationRunnable enqueue =
+                mNotificationManagerService.new EnqueueNotificationRunnable(UserHandle.USER_SYSTEM,
+                        r);
+        assertTrue(enqueue.isBlocked(r, usageStats));
+        verify(usageStats, times(1)).registerBlocked(eq(r));
+    }
+
+    private NotificationRecord generateNotificationRecord(NotificationChannel channel) {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        Notification n = new Notification.Builder(context)
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setPriority(Notification.PRIORITY_HIGH)
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, channel, 1, "tag", uid, uid,
+                n, UserHandle.SYSTEM, null, uid);
+        return new NotificationRecord(context, sbn);
+    }
 }
index e6afe76..5696a72 100644 (file)
@@ -246,8 +246,8 @@ public class RankingHelperTest {
         channel2.enableVibration(true);
         channel2.setVibrationPattern(new long[] {100, 67, 145, 156});
 
-        mHelper.createNotificationChannel(pkg, uid, channel1);
-        mHelper.createNotificationChannel(pkg, uid, channel2);
+        mHelper.createNotificationChannel(pkg, uid, channel1, false);
+        mHelper.createNotificationChannel(pkg, uid, channel2, false);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), channel2.getId(),
                 NotificationChannel.DEFAULT_CHANNEL_ID);
@@ -273,7 +273,7 @@ public class RankingHelperTest {
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_DEFAULT);
 
-        mHelper.createNotificationChannel(pkg, uid, channel1);
+        mHelper.createNotificationChannel(pkg, uid, channel1, true);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(),
                 NotificationChannel.DEFAULT_CHANNEL_ID);
@@ -296,7 +296,7 @@ public class RankingHelperTest {
     public void testChannelXml_defaultChannelUpdatedApp_userSettings() throws Exception {
          NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MIN);
-        mHelper.createNotificationChannel(pkg, uid, channel1);
+        mHelper.createNotificationChannel(pkg, uid, channel1, true);
 
         final NotificationChannel defaultChannel =
                 mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
@@ -355,7 +355,7 @@ public class RankingHelperTest {
 
         try {
             mHelper.createNotificationChannel(pkg, uid,
-                    new NotificationChannel(pkg, "", NotificationManager.IMPORTANCE_LOW));
+                    new NotificationChannel(pkg, "", NotificationManager.IMPORTANCE_LOW), true);
             fail("Channel creation should fail");
         } catch (IllegalArgumentException e) {
             // pass
@@ -369,7 +369,7 @@ public class RankingHelperTest {
             new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
         channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
 
-        mHelper.createNotificationChannel(pkg, uid, channel);
+        mHelper.createNotificationChannel(pkg, uid, channel, false);
 
         // same id, try to update
         final NotificationChannel channel2 =
@@ -389,7 +389,7 @@ public class RankingHelperTest {
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
         channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
 
-        mHelper.createNotificationChannel(pkg, uid, channel);
+        mHelper.createNotificationChannel(pkg, uid, channel, false);
 
         // same id, try to update
         final NotificationChannel channel2 =
@@ -410,7 +410,7 @@ public class RankingHelperTest {
         channel.setLights(false);
         channel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
 
-        mHelper.createNotificationChannel(pkg, uid, channel);
+        mHelper.createNotificationChannel(pkg, uid, channel, false);
 
         // same id, try to update
         final NotificationChannel channel2 =
@@ -432,7 +432,7 @@ public class RankingHelperTest {
         channel.setLights(false);
         channel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
 
-        mHelper.createNotificationChannel(pkg, uid, channel);
+        mHelper.createNotificationChannel(pkg, uid, channel, false);
 
         // same id, try to update
         final NotificationChannel channel2 =
@@ -453,7 +453,7 @@ public class RankingHelperTest {
         channel.setBypassDnd(true);
         channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
 
-        mHelper.createNotificationChannel(pkg, uid, channel);
+        mHelper.createNotificationChannel(pkg, uid, channel, false);
 
         // same id, try to update all fields
         final NotificationChannel channel2 =
@@ -474,7 +474,7 @@ public class RankingHelperTest {
         channel.setSound(new Uri.Builder().scheme("test").build());
         channel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
 
-        mHelper.createNotificationChannel(pkg, uid, channel);
+        mHelper.createNotificationChannel(pkg, uid, channel, false);
 
         // same id, try to update all fields
         final NotificationChannel channel2 =
@@ -488,6 +488,44 @@ public class RankingHelperTest {
     }
 
     @Test
+    public void testUpdate_userLockedAllowed() throws Exception {
+        final NotificationChannel channel =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+        channel.setAllowed(true);
+        channel.lockFields(NotificationChannel.USER_LOCKED_ALLOWED);
+
+        mHelper.createNotificationChannel(pkg, uid, channel, false);
+
+        final NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+        channel2.setAllowed(false);
+
+        mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
+
+        // no fields should be changed
+        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+    }
+
+    @Test
+    public void testUpdate_userLockedBadge() throws Exception {
+        final NotificationChannel channel =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+        channel.setShowBadge(true);
+        channel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
+
+        mHelper.createNotificationChannel(pkg, uid, channel, false);
+
+        final NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+        channel2.setShowBadge(false);
+
+        mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
+
+        // no fields should be changed
+        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+    }
+
+    @Test
     public void testUpdate() throws Exception {
         // no fields locked by user
         final NotificationChannel channel =
@@ -497,7 +535,7 @@ public class RankingHelperTest {
         channel.setBypassDnd(true);
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
 
-        mHelper.createNotificationChannel(pkg, uid, channel);
+        mHelper.createNotificationChannel(pkg, uid, channel, false);
 
         // same id, try to update all fields
         final NotificationChannel channel2 =
@@ -519,4 +557,60 @@ public class RankingHelperTest {
                 mHelper.getNotificationChannelWithFallback(pkg, uid, "garbage");
         assertEquals(NotificationChannel.DEFAULT_CHANNEL_ID, channel.getId());
     }
+
+    @Test
+    public void testCreateChannel_CannotChangeHiddenFields() throws Exception {
+        final NotificationChannel channel =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+        channel.setSound(new Uri.Builder().scheme("test").build());
+        channel.setLights(true);
+        channel.setBypassDnd(true);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        channel.setShowBadge(true);
+        channel.setAllowed(false);
+        int lockMask = 0;
+        for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
+            lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
+        }
+        channel.lockFields(lockMask);
+
+        mHelper.createNotificationChannel(pkg, uid, channel, true);
+
+        NotificationChannel savedChannel =
+                mHelper.getNotificationChannel(pkg, uid, channel.getId());
+
+        assertEquals(channel.getName(), savedChannel.getName());
+        assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
+        assertFalse(savedChannel.canBypassDnd());
+        assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
+        assertFalse(savedChannel.canShowBadge());
+    }
+
+    @Test
+    public void testCreateChannel_CannotChangeHiddenFieldsAssistant() throws Exception {
+        final NotificationChannel channel =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+        channel.setSound(new Uri.Builder().scheme("test").build());
+        channel.setLights(true);
+        channel.setBypassDnd(true);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        channel.setShowBadge(true);
+        channel.setAllowed(false);
+        int lockMask = 0;
+        for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
+            lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
+        }
+        channel.lockFields(lockMask);
+
+        mHelper.createNotificationChannel(pkg, uid, channel, true);
+
+        NotificationChannel savedChannel =
+                mHelper.getNotificationChannel(pkg, uid, channel.getId());
+
+        assertEquals(channel.getName(), savedChannel.getName());
+        assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
+        assertFalse(savedChannel.canBypassDnd());
+        assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
+        assertFalse(savedChannel.canShowBadge());
+    }
 }