Home | History | Annotate | Download | only in notification
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.notification;
     18 
     19 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
     20 import static android.service.notification.NotificationListenerService.TRIM_FULL;
     21 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
     22 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
     23 import static org.xmlpull.v1.XmlPullParser.END_TAG;
     24 import static org.xmlpull.v1.XmlPullParser.START_TAG;
     25 
     26 import android.app.ActivityManager;
     27 import android.app.ActivityManagerNative;
     28 import android.app.AppGlobals;
     29 import android.app.AppOpsManager;
     30 import android.app.IActivityManager;
     31 import android.app.INotificationManager;
     32 import android.app.ITransientNotification;
     33 import android.app.Notification;
     34 import android.app.NotificationManager;
     35 import android.app.PendingIntent;
     36 import android.app.StatusBarManager;
     37 import android.content.BroadcastReceiver;
     38 import android.content.ComponentName;
     39 import android.content.ContentResolver;
     40 import android.content.Context;
     41 import android.content.Intent;
     42 import android.content.IntentFilter;
     43 import android.content.pm.ApplicationInfo;
     44 import android.content.pm.PackageInfo;
     45 import android.content.pm.PackageManager;
     46 import android.content.pm.PackageManager.NameNotFoundException;
     47 import android.content.pm.ParceledListSlice;
     48 import android.content.res.Resources;
     49 import android.database.ContentObserver;
     50 import android.media.AudioAttributes;
     51 import android.media.AudioManager;
     52 import android.media.AudioSystem;
     53 import android.media.IRingtonePlayer;
     54 import android.net.Uri;
     55 import android.os.Binder;
     56 import android.os.Bundle;
     57 import android.os.Environment;
     58 import android.os.Handler;
     59 import android.os.HandlerThread;
     60 import android.os.IBinder;
     61 import android.os.IInterface;
     62 import android.os.Looper;
     63 import android.os.Message;
     64 import android.os.Process;
     65 import android.os.RemoteException;
     66 import android.os.UserHandle;
     67 import android.os.Vibrator;
     68 import android.provider.Settings;
     69 import android.service.notification.Condition;
     70 import android.service.notification.IConditionListener;
     71 import android.service.notification.IConditionProvider;
     72 import android.service.notification.INotificationListener;
     73 import android.service.notification.IStatusBarNotificationHolder;
     74 import android.service.notification.NotificationListenerService;
     75 import android.service.notification.NotificationRankingUpdate;
     76 import android.service.notification.StatusBarNotification;
     77 import android.service.notification.ZenModeConfig;
     78 import android.telephony.PhoneStateListener;
     79 import android.telephony.TelephonyManager;
     80 import android.text.TextUtils;
     81 import android.util.ArrayMap;
     82 import android.util.ArraySet;
     83 import android.util.AtomicFile;
     84 import android.util.Log;
     85 import android.util.Slog;
     86 import android.util.Xml;
     87 import android.view.accessibility.AccessibilityEvent;
     88 import android.view.accessibility.AccessibilityManager;
     89 import android.widget.Toast;
     90 
     91 import com.android.internal.R;
     92 import com.android.internal.util.FastXmlSerializer;
     93 import com.android.server.EventLogTags;
     94 import com.android.server.SystemService;
     95 import com.android.server.lights.Light;
     96 import com.android.server.lights.LightsManager;
     97 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
     98 import com.android.server.notification.ManagedServices.UserProfiles;
     99 import com.android.server.statusbar.StatusBarManagerInternal;
    100 
    101 import libcore.io.IoUtils;
    102 
    103 import org.xmlpull.v1.XmlPullParser;
    104 import org.xmlpull.v1.XmlPullParserException;
    105 import org.xmlpull.v1.XmlSerializer;
    106 
    107 import java.io.File;
    108 import java.io.FileDescriptor;
    109 import java.io.FileInputStream;
    110 import java.io.FileNotFoundException;
    111 import java.io.FileOutputStream;
    112 import java.io.IOException;
    113 import java.io.PrintWriter;
    114 import java.util.ArrayDeque;
    115 import java.util.ArrayList;
    116 import java.util.HashSet;
    117 import java.util.Iterator;
    118 import java.util.NoSuchElementException;
    119 import java.util.Objects;
    120 
    121 /** {@hide} */
    122 public class NotificationManagerService extends SystemService {
    123     static final String TAG = "NotificationService";
    124     static final boolean DBG = false;
    125 
    126     static final int MAX_PACKAGE_NOTIFICATIONS = 50;
    127 
    128     // message codes
    129     static final int MESSAGE_TIMEOUT = 2;
    130     static final int MESSAGE_SAVE_POLICY_FILE = 3;
    131     static final int MESSAGE_RECONSIDER_RANKING = 4;
    132     static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
    133     static final int MESSAGE_SEND_RANKING_UPDATE = 6;
    134     static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
    135     static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
    136 
    137     static final int LONG_DELAY = 3500; // 3.5 seconds
    138     static final int SHORT_DELAY = 2000; // 2 seconds
    139 
    140     static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
    141     static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
    142 
    143     static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
    144     static final boolean SCORE_ONGOING_HIGHER = false;
    145 
    146     static final int JUNK_SCORE = -1000;
    147     static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
    148     static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
    149 
    150     // Notifications with scores below this will not interrupt the user, either via LED or
    151     // sound or vibration
    152     static final int SCORE_INTERRUPTION_THRESHOLD =
    153             Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
    154 
    155     static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
    156     static final boolean ENABLE_BLOCKED_TOASTS = true;
    157 
    158     // When #matchesCallFilter is called from the ringer, wait at most
    159     // 3s to resolve the contacts. This timeout is required since
    160     // ContactsProvider might take a long time to start up.
    161     //
    162     // Return STARRED_CONTACT when the timeout is hit in order to avoid
    163     // missed calls in ZEN mode "Important".
    164     static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
    165     static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
    166             ValidateNotificationPeople.STARRED_CONTACT;
    167 
    168     private IActivityManager mAm;
    169     AudioManager mAudioManager;
    170     StatusBarManagerInternal mStatusBar;
    171     Vibrator mVibrator;
    172 
    173     final IBinder mForegroundToken = new Binder();
    174     private WorkerHandler mHandler;
    175     private final HandlerThread mRankingThread = new HandlerThread("ranker",
    176             Process.THREAD_PRIORITY_BACKGROUND);
    177 
    178     private Light mNotificationLight;
    179     Light mAttentionLight;
    180     private int mDefaultNotificationColor;
    181     private int mDefaultNotificationLedOn;
    182 
    183     private int mDefaultNotificationLedOff;
    184     private long[] mDefaultVibrationPattern;
    185 
    186     private long[] mFallbackVibrationPattern;
    187     private boolean mUseAttentionLight;
    188     boolean mSystemReady;
    189 
    190     private boolean mDisableNotificationEffects;
    191     private int mCallState;
    192     NotificationRecord mSoundNotification;
    193     NotificationRecord mVibrateNotification;
    194 
    195     private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
    196     private ComponentName mEffectsSuppressor;
    197     private int mListenerHints;  // right now, all hints are global
    198     private int mInterruptionFilter;  // current ZEN mode as communicated to listeners
    199 
    200     // for enabling and disabling notification pulse behavior
    201     private boolean mScreenOn = true;
    202     private boolean mInCall = false;
    203     private boolean mNotificationPulseEnabled;
    204 
    205     // used as a mutex for access to all active notifications & listeners
    206     final ArrayList<NotificationRecord> mNotificationList =
    207             new ArrayList<NotificationRecord>();
    208     final ArrayMap<String, NotificationRecord> mNotificationsByKey =
    209             new ArrayMap<String, NotificationRecord>();
    210     final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
    211 
    212     ArrayList<String> mLights = new ArrayList<String>();
    213     NotificationRecord mLedNotification;
    214 
    215     private AppOpsManager mAppOps;
    216 
    217     private Archive mArchive;
    218 
    219     // Notification control database. For now just contains disabled packages.
    220     private AtomicFile mPolicyFile;
    221     private HashSet<String> mBlockedPackages = new HashSet<String>();
    222 
    223     private static final int DB_VERSION = 1;
    224 
    225     private static final String TAG_BODY = "notification-policy";
    226     private static final String ATTR_VERSION = "version";
    227 
    228     private static final String TAG_BLOCKED_PKGS = "blocked-packages";
    229     private static final String TAG_PACKAGE = "package";
    230     private static final String ATTR_NAME = "name";
    231 
    232     private RankingHelper mRankingHelper;
    233 
    234     private final UserProfiles mUserProfiles = new UserProfiles();
    235     private NotificationListeners mListeners;
    236     private ConditionProviders mConditionProviders;
    237     private NotificationUsageStats mUsageStats;
    238 
    239     private static final int MY_UID = Process.myUid();
    240     private static final int MY_PID = Process.myPid();
    241     private static final int REASON_DELEGATE_CLICK = 1;
    242     private static final int REASON_DELEGATE_CANCEL = 2;
    243     private static final int REASON_DELEGATE_CANCEL_ALL = 3;
    244     private static final int REASON_DELEGATE_ERROR = 4;
    245     private static final int REASON_PACKAGE_CHANGED = 5;
    246     private static final int REASON_USER_STOPPED = 6;
    247     private static final int REASON_PACKAGE_BANNED = 7;
    248     private static final int REASON_NOMAN_CANCEL = 8;
    249     private static final int REASON_NOMAN_CANCEL_ALL = 9;
    250     private static final int REASON_LISTENER_CANCEL = 10;
    251     private static final int REASON_LISTENER_CANCEL_ALL = 11;
    252     private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
    253 
    254     private static class Archive {
    255         final int mBufferSize;
    256         final ArrayDeque<StatusBarNotification> mBuffer;
    257 
    258         public Archive(int size) {
    259             mBufferSize = size;
    260             mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
    261         }
    262 
    263         public String toString() {
    264             final StringBuilder sb = new StringBuilder();
    265             final int N = mBuffer.size();
    266             sb.append("Archive (");
    267             sb.append(N);
    268             sb.append(" notification");
    269             sb.append((N==1)?")":"s)");
    270             return sb.toString();
    271         }
    272 
    273         public void record(StatusBarNotification nr) {
    274             if (mBuffer.size() == mBufferSize) {
    275                 mBuffer.removeFirst();
    276             }
    277 
    278             // We don't want to store the heavy bits of the notification in the archive,
    279             // but other clients in the system process might be using the object, so we
    280             // store a (lightened) copy.
    281             mBuffer.addLast(nr.cloneLight());
    282         }
    283 
    284         public void clear() {
    285             mBuffer.clear();
    286         }
    287 
    288         public Iterator<StatusBarNotification> descendingIterator() {
    289             return mBuffer.descendingIterator();
    290         }
    291         public Iterator<StatusBarNotification> ascendingIterator() {
    292             return mBuffer.iterator();
    293         }
    294         public Iterator<StatusBarNotification> filter(
    295                 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
    296             return new Iterator<StatusBarNotification>() {
    297                 StatusBarNotification mNext = findNext();
    298 
    299                 private StatusBarNotification findNext() {
    300                     while (iter.hasNext()) {
    301                         StatusBarNotification nr = iter.next();
    302                         if ((pkg == null || nr.getPackageName() == pkg)
    303                                 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
    304                             return nr;
    305                         }
    306                     }
    307                     return null;
    308                 }
    309 
    310                 @Override
    311                 public boolean hasNext() {
    312                     return mNext == null;
    313                 }
    314 
    315                 @Override
    316                 public StatusBarNotification next() {
    317                     StatusBarNotification next = mNext;
    318                     if (next == null) {
    319                         throw new NoSuchElementException();
    320                     }
    321                     mNext = findNext();
    322                     return next;
    323                 }
    324 
    325                 @Override
    326                 public void remove() {
    327                     iter.remove();
    328                 }
    329             };
    330         }
    331 
    332         public StatusBarNotification[] getArray(int count) {
    333             if (count == 0) count = mBufferSize;
    334             final StatusBarNotification[] a
    335                     = new StatusBarNotification[Math.min(count, mBuffer.size())];
    336             Iterator<StatusBarNotification> iter = descendingIterator();
    337             int i=0;
    338             while (iter.hasNext() && i < count) {
    339                 a[i++] = iter.next();
    340             }
    341             return a;
    342         }
    343 
    344         public StatusBarNotification[] getArray(int count, String pkg, int userId) {
    345             if (count == 0) count = mBufferSize;
    346             final StatusBarNotification[] a
    347                     = new StatusBarNotification[Math.min(count, mBuffer.size())];
    348             Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
    349             int i=0;
    350             while (iter.hasNext() && i < count) {
    351                 a[i++] = iter.next();
    352             }
    353             return a;
    354         }
    355 
    356     }
    357 
    358     private void loadPolicyFile() {
    359         synchronized(mPolicyFile) {
    360             mBlockedPackages.clear();
    361 
    362             FileInputStream infile = null;
    363             try {
    364                 infile = mPolicyFile.openRead();
    365                 final XmlPullParser parser = Xml.newPullParser();
    366                 parser.setInput(infile, null);
    367 
    368                 int type;
    369                 String tag;
    370                 int version = DB_VERSION;
    371                 while ((type = parser.next()) != END_DOCUMENT) {
    372                     tag = parser.getName();
    373                     if (type == START_TAG) {
    374                         if (TAG_BODY.equals(tag)) {
    375                             version = Integer.parseInt(
    376                                     parser.getAttributeValue(null, ATTR_VERSION));
    377                         } else if (TAG_BLOCKED_PKGS.equals(tag)) {
    378                             while ((type = parser.next()) != END_DOCUMENT) {
    379                                 tag = parser.getName();
    380                                 if (TAG_PACKAGE.equals(tag)) {
    381                                     mBlockedPackages.add(
    382                                             parser.getAttributeValue(null, ATTR_NAME));
    383                                 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
    384                                     break;
    385                                 }
    386                             }
    387                         }
    388                     }
    389                     mZenModeHelper.readXml(parser);
    390                     mRankingHelper.readXml(parser);
    391                 }
    392             } catch (FileNotFoundException e) {
    393                 // No data yet
    394             } catch (IOException e) {
    395                 Log.wtf(TAG, "Unable to read notification policy", e);
    396             } catch (NumberFormatException e) {
    397                 Log.wtf(TAG, "Unable to parse notification policy", e);
    398             } catch (XmlPullParserException e) {
    399                 Log.wtf(TAG, "Unable to parse notification policy", e);
    400             } finally {
    401                 IoUtils.closeQuietly(infile);
    402             }
    403         }
    404     }
    405 
    406     public void savePolicyFile() {
    407         mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
    408         mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
    409     }
    410 
    411     private void handleSavePolicyFile() {
    412         Slog.d(TAG, "handleSavePolicyFile");
    413         synchronized (mPolicyFile) {
    414             final FileOutputStream stream;
    415             try {
    416                 stream = mPolicyFile.startWrite();
    417             } catch (IOException e) {
    418                 Slog.w(TAG, "Failed to save policy file", e);
    419                 return;
    420             }
    421 
    422             try {
    423                 final XmlSerializer out = new FastXmlSerializer();
    424                 out.setOutput(stream, "utf-8");
    425                 out.startDocument(null, true);
    426                 out.startTag(null, TAG_BODY);
    427                 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
    428                 mZenModeHelper.writeXml(out);
    429                 mRankingHelper.writeXml(out);
    430                 out.endTag(null, TAG_BODY);
    431                 out.endDocument();
    432                 mPolicyFile.finishWrite(stream);
    433             } catch (IOException e) {
    434                 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
    435                 mPolicyFile.failWrite(stream);
    436             }
    437         }
    438     }
    439 
    440     /** Use this when you actually want to post a notification or toast.
    441      *
    442      * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
    443      */
    444     private boolean noteNotificationOp(String pkg, int uid) {
    445         if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
    446                 != AppOpsManager.MODE_ALLOWED) {
    447             Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
    448             return false;
    449         }
    450         return true;
    451     }
    452 
    453     private static final class ToastRecord
    454     {
    455         final int pid;
    456         final String pkg;
    457         final ITransientNotification callback;
    458         int duration;
    459 
    460         ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
    461         {
    462             this.pid = pid;
    463             this.pkg = pkg;
    464             this.callback = callback;
    465             this.duration = duration;
    466         }
    467 
    468         void update(int duration) {
    469             this.duration = duration;
    470         }
    471 
    472         void dump(PrintWriter pw, String prefix, DumpFilter filter) {
    473             if (filter != null && !filter.matches(pkg)) return;
    474             pw.println(prefix + this);
    475         }
    476 
    477         @Override
    478         public final String toString()
    479         {
    480             return "ToastRecord{"
    481                 + Integer.toHexString(System.identityHashCode(this))
    482                 + " pkg=" + pkg
    483                 + " callback=" + callback
    484                 + " duration=" + duration;
    485         }
    486     }
    487 
    488     private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
    489 
    490         @Override
    491         public void onSetDisabled(int status) {
    492             synchronized (mNotificationList) {
    493                 mDisableNotificationEffects =
    494                         (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
    495                 if (disableNotificationEffects(null) != null) {
    496                     // cancel whatever's going on
    497                     long identity = Binder.clearCallingIdentity();
    498                     try {
    499                         final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
    500                         if (player != null) {
    501                             player.stopAsync();
    502                         }
    503                     } catch (RemoteException e) {
    504                     } finally {
    505                         Binder.restoreCallingIdentity(identity);
    506                     }
    507 
    508                     identity = Binder.clearCallingIdentity();
    509                     try {
    510                         mVibrator.cancel();
    511                     } finally {
    512                         Binder.restoreCallingIdentity(identity);
    513                     }
    514                 }
    515             }
    516         }
    517 
    518         @Override
    519         public void onClearAll(int callingUid, int callingPid, int userId) {
    520             synchronized (mNotificationList) {
    521                 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
    522                         /*includeCurrentProfiles*/ true);
    523             }
    524         }
    525 
    526         @Override
    527         public void onNotificationClick(int callingUid, int callingPid, String key) {
    528             synchronized (mNotificationList) {
    529                 EventLogTags.writeNotificationClicked(key);
    530                 NotificationRecord r = mNotificationsByKey.get(key);
    531                 if (r == null) {
    532                     Log.w(TAG, "No notification with key: " + key);
    533                     return;
    534                 }
    535                 StatusBarNotification sbn = r.sbn;
    536                 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
    537                         sbn.getId(), Notification.FLAG_AUTO_CANCEL,
    538                         Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
    539                         REASON_DELEGATE_CLICK, null);
    540             }
    541         }
    542 
    543         @Override
    544         public void onNotificationClear(int callingUid, int callingPid,
    545                 String pkg, String tag, int id, int userId) {
    546             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
    547                     Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
    548                     true, userId, REASON_DELEGATE_CANCEL, null);
    549         }
    550 
    551         @Override
    552         public void onPanelRevealed() {
    553             EventLogTags.writeNotificationPanelRevealed();
    554             synchronized (mNotificationList) {
    555                 // sound
    556                 mSoundNotification = null;
    557 
    558                 long identity = Binder.clearCallingIdentity();
    559                 try {
    560                     final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
    561                     if (player != null) {
    562                         player.stopAsync();
    563                     }
    564                 } catch (RemoteException e) {
    565                 } finally {
    566                     Binder.restoreCallingIdentity(identity);
    567                 }
    568 
    569                 // vibrate
    570                 mVibrateNotification = null;
    571                 identity = Binder.clearCallingIdentity();
    572                 try {
    573                     mVibrator.cancel();
    574                 } finally {
    575                     Binder.restoreCallingIdentity(identity);
    576                 }
    577 
    578                 // light
    579                 mLights.clear();
    580                 mLedNotification = null;
    581                 updateLightsLocked();
    582             }
    583         }
    584 
    585         @Override
    586         public void onPanelHidden() {
    587             EventLogTags.writeNotificationPanelHidden();
    588         }
    589 
    590         @Override
    591         public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
    592                 int uid, int initialPid, String message, int userId) {
    593             Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
    594                     + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
    595             cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
    596                     REASON_DELEGATE_ERROR, null);
    597             long ident = Binder.clearCallingIdentity();
    598             try {
    599                 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
    600                         "Bad notification posted from package " + pkg
    601                         + ": " + message);
    602             } catch (RemoteException e) {
    603             }
    604             Binder.restoreCallingIdentity(ident);
    605         }
    606 
    607         @Override
    608         public void onNotificationVisibilityChanged(
    609                 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
    610             // Using ';' as separator since eventlogs uses ',' to separate
    611             // args.
    612             EventLogTags.writeNotificationVisibilityChanged(
    613                     TextUtils.join(";", newlyVisibleKeys),
    614                     TextUtils.join(";", noLongerVisibleKeys));
    615             synchronized (mNotificationList) {
    616                 for (String key : newlyVisibleKeys) {
    617                     NotificationRecord r = mNotificationsByKey.get(key);
    618                     if (r == null) continue;
    619                     r.stats.onVisibilityChanged(true);
    620                 }
    621                 // Note that we might receive this event after notifications
    622                 // have already left the system, e.g. after dismissing from the
    623                 // shade. Hence not finding notifications in
    624                 // mNotificationsByKey is not an exceptional condition.
    625                 for (String key : noLongerVisibleKeys) {
    626                     NotificationRecord r = mNotificationsByKey.get(key);
    627                     if (r == null) continue;
    628                     r.stats.onVisibilityChanged(false);
    629                 }
    630             }
    631         }
    632 
    633         @Override
    634         public void onNotificationExpansionChanged(String key,
    635                 boolean userAction, boolean expanded) {
    636             EventLogTags.writeNotificationExpansion(key, userAction ? 1 : 0, expanded ? 1 : 0);
    637             synchronized (mNotificationList) {
    638                 NotificationRecord r = mNotificationsByKey.get(key);
    639                 if (r != null) {
    640                     r.stats.onExpansionChanged(userAction, expanded);
    641                 }
    642             }
    643         }
    644     };
    645 
    646     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
    647         @Override
    648         public void onReceive(Context context, Intent intent) {
    649             String action = intent.getAction();
    650 
    651             boolean queryRestart = false;
    652             boolean queryRemove = false;
    653             boolean packageChanged = false;
    654             boolean cancelNotifications = true;
    655 
    656             if (action.equals(Intent.ACTION_PACKAGE_ADDED)
    657                     || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
    658                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
    659                     || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
    660                     || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
    661                     || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
    662                 String pkgList[] = null;
    663                 boolean queryReplace = queryRemove &&
    664                         intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
    665                 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
    666                 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
    667                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
    668                 } else if (queryRestart) {
    669                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
    670                 } else {
    671                     Uri uri = intent.getData();
    672                     if (uri == null) {
    673                         return;
    674                     }
    675                     String pkgName = uri.getSchemeSpecificPart();
    676                     if (pkgName == null) {
    677                         return;
    678                     }
    679                     if (packageChanged) {
    680                         // We cancel notifications for packages which have just been disabled
    681                         try {
    682                             final int enabled = getContext().getPackageManager()
    683                                     .getApplicationEnabledSetting(pkgName);
    684                             if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
    685                                     || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
    686                                 cancelNotifications = false;
    687                             }
    688                         } catch (IllegalArgumentException e) {
    689                             // Package doesn't exist; probably racing with uninstall.
    690                             // cancelNotifications is already true, so nothing to do here.
    691                             if (DBG) {
    692                                 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
    693                             }
    694                         }
    695                     }
    696                     pkgList = new String[]{pkgName};
    697                 }
    698 
    699                 if (pkgList != null && (pkgList.length > 0)) {
    700                     for (String pkgName : pkgList) {
    701                         if (cancelNotifications) {
    702                             cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
    703                                     UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null);
    704                         }
    705                     }
    706                 }
    707                 mListeners.onPackagesChanged(queryReplace, pkgList);
    708                 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
    709             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
    710                 // Keep track of screen on/off state, but do not turn off the notification light
    711                 // until user passes through the lock screen or views the notification.
    712                 mScreenOn = true;
    713             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
    714                 mScreenOn = false;
    715             } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
    716                 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
    717                         .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
    718                 updateNotificationPulse();
    719             } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
    720                 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
    721                 if (userHandle >= 0) {
    722                     cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
    723                             REASON_USER_STOPPED, null);
    724                 }
    725             } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
    726                 // turn off LED when user passes through lock screen
    727                 mNotificationLight.turnOff();
    728                 mStatusBar.notificationLightOff();
    729             } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
    730                 // reload per-user settings
    731                 mSettingsObserver.update(null);
    732                 mUserProfiles.updateCache(context);
    733                 // Refresh managed services
    734                 mConditionProviders.onUserSwitched();
    735                 mListeners.onUserSwitched();
    736             } else if (action.equals(Intent.ACTION_USER_ADDED)) {
    737                 mUserProfiles.updateCache(context);
    738             }
    739         }
    740     };
    741 
    742     class SettingsObserver extends ContentObserver {
    743         private final Uri NOTIFICATION_LIGHT_PULSE_URI
    744                 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
    745 
    746         SettingsObserver(Handler handler) {
    747             super(handler);
    748         }
    749 
    750         void observe() {
    751             ContentResolver resolver = getContext().getContentResolver();
    752             resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
    753                     false, this, UserHandle.USER_ALL);
    754             update(null);
    755         }
    756 
    757         @Override public void onChange(boolean selfChange, Uri uri) {
    758             update(uri);
    759         }
    760 
    761         public void update(Uri uri) {
    762             ContentResolver resolver = getContext().getContentResolver();
    763             if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
    764                 boolean pulseEnabled = Settings.System.getInt(resolver,
    765                             Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
    766                 if (mNotificationPulseEnabled != pulseEnabled) {
    767                     mNotificationPulseEnabled = pulseEnabled;
    768                     updateNotificationPulse();
    769                 }
    770             }
    771         }
    772     }
    773 
    774     private SettingsObserver mSettingsObserver;
    775     private ZenModeHelper mZenModeHelper;
    776 
    777     private final Runnable mBuzzBeepBlinked = new Runnable() {
    778         @Override
    779         public void run() {
    780             mStatusBar.buzzBeepBlinked();
    781         }
    782     };
    783 
    784     static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
    785         int[] ar = r.getIntArray(resid);
    786         if (ar == null) {
    787             return def;
    788         }
    789         final int len = ar.length > maxlen ? maxlen : ar.length;
    790         long[] out = new long[len];
    791         for (int i=0; i<len; i++) {
    792             out[i] = ar[i];
    793         }
    794         return out;
    795     }
    796 
    797     public NotificationManagerService(Context context) {
    798         super(context);
    799     }
    800 
    801     @Override
    802     public void onStart() {
    803         Resources resources = getContext().getResources();
    804 
    805         mAm = ActivityManagerNative.getDefault();
    806         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
    807         mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
    808 
    809         mHandler = new WorkerHandler();
    810         mRankingThread.start();
    811         String[] extractorNames;
    812         try {
    813             extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
    814         } catch (Resources.NotFoundException e) {
    815             extractorNames = new String[0];
    816         }
    817         mRankingHelper = new RankingHelper(getContext(),
    818                 new RankingWorkerHandler(mRankingThread.getLooper()),
    819                 extractorNames);
    820         mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
    821         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
    822             @Override
    823             public void onConfigChanged() {
    824                 savePolicyFile();
    825             }
    826 
    827             @Override
    828             void onZenModeChanged() {
    829                 synchronized(mNotificationList) {
    830                     updateInterruptionFilterLocked();
    831                 }
    832             }
    833         });
    834         final File systemDir = new File(Environment.getDataDirectory(), "system");
    835         mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
    836         mUsageStats = new NotificationUsageStats(getContext());
    837 
    838         importOldBlockDb();
    839 
    840         mListeners = new NotificationListeners();
    841         mConditionProviders = new ConditionProviders(getContext(),
    842                 mHandler, mUserProfiles, mZenModeHelper);
    843         mStatusBar = getLocalService(StatusBarManagerInternal.class);
    844         mStatusBar.setNotificationDelegate(mNotificationDelegate);
    845 
    846         final LightsManager lights = getLocalService(LightsManager.class);
    847         mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
    848         mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
    849 
    850         mDefaultNotificationColor = resources.getColor(
    851                 R.color.config_defaultNotificationColor);
    852         mDefaultNotificationLedOn = resources.getInteger(
    853                 R.integer.config_defaultNotificationLedOn);
    854         mDefaultNotificationLedOff = resources.getInteger(
    855                 R.integer.config_defaultNotificationLedOff);
    856 
    857         mDefaultVibrationPattern = getLongArray(resources,
    858                 R.array.config_defaultNotificationVibePattern,
    859                 VIBRATE_PATTERN_MAXLEN,
    860                 DEFAULT_VIBRATE_PATTERN);
    861 
    862         mFallbackVibrationPattern = getLongArray(resources,
    863                 R.array.config_notificationFallbackVibePattern,
    864                 VIBRATE_PATTERN_MAXLEN,
    865                 DEFAULT_VIBRATE_PATTERN);
    866 
    867         mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
    868 
    869         // Don't start allowing notifications until the setup wizard has run once.
    870         // After that, including subsequent boots, init with notifications turned on.
    871         // This works on the first boot because the setup wizard will toggle this
    872         // flag at least once and we'll go back to 0 after that.
    873         if (0 == Settings.Global.getInt(getContext().getContentResolver(),
    874                     Settings.Global.DEVICE_PROVISIONED, 0)) {
    875             mDisableNotificationEffects = true;
    876         }
    877         mZenModeHelper.updateZenMode();
    878 
    879         mUserProfiles.updateCache(getContext());
    880         listenForCallState();
    881 
    882         // register for various Intents
    883         IntentFilter filter = new IntentFilter();
    884         filter.addAction(Intent.ACTION_SCREEN_ON);
    885         filter.addAction(Intent.ACTION_SCREEN_OFF);
    886         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
    887         filter.addAction(Intent.ACTION_USER_PRESENT);
    888         filter.addAction(Intent.ACTION_USER_STOPPED);
    889         filter.addAction(Intent.ACTION_USER_SWITCHED);
    890         filter.addAction(Intent.ACTION_USER_ADDED);
    891         getContext().registerReceiver(mIntentReceiver, filter);
    892         IntentFilter pkgFilter = new IntentFilter();
    893         pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    894         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    895         pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    896         pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
    897         pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
    898         pkgFilter.addDataScheme("package");
    899         getContext().registerReceiver(mIntentReceiver, pkgFilter);
    900         IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
    901         getContext().registerReceiver(mIntentReceiver, sdFilter);
    902 
    903         mSettingsObserver = new SettingsObserver(mHandler);
    904 
    905         mArchive = new Archive(resources.getInteger(
    906                 R.integer.config_notificationServiceArchiveSize));
    907 
    908         publishBinderService(Context.NOTIFICATION_SERVICE, mService);
    909         publishLocalService(NotificationManagerInternal.class, mInternalService);
    910     }
    911 
    912     /**
    913      * Read the old XML-based app block database and import those blockages into the AppOps system.
    914      */
    915     private void importOldBlockDb() {
    916         loadPolicyFile();
    917 
    918         PackageManager pm = getContext().getPackageManager();
    919         for (String pkg : mBlockedPackages) {
    920             PackageInfo info = null;
    921             try {
    922                 info = pm.getPackageInfo(pkg, 0);
    923                 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
    924             } catch (NameNotFoundException e) {
    925                 // forget you
    926             }
    927         }
    928         mBlockedPackages.clear();
    929     }
    930 
    931     @Override
    932     public void onBootPhase(int phase) {
    933         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
    934             // no beeping until we're basically done booting
    935             mSystemReady = true;
    936 
    937             // Grab our optional AudioService
    938             mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
    939             mZenModeHelper.setAudioManager(mAudioManager);
    940         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
    941             // This observer will force an update when observe is called, causing us to
    942             // bind to listener services.
    943             mSettingsObserver.observe();
    944             mListeners.onBootPhaseAppsCanStart();
    945             mConditionProviders.onBootPhaseAppsCanStart();
    946         }
    947     }
    948 
    949     void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
    950         Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
    951 
    952         mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
    953                 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
    954 
    955         // Now, cancel any outstanding notifications that are part of a just-disabled app
    956         if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
    957             cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
    958                     REASON_PACKAGE_BANNED, null);
    959         }
    960     }
    961 
    962     private void updateListenerHintsLocked() {
    963         final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
    964         if (hints == mListenerHints) return;
    965         mListenerHints = hints;
    966         scheduleListenerHintsChanged(hints);
    967     }
    968 
    969     private void updateEffectsSuppressorLocked() {
    970         final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
    971                 ? mListenersDisablingEffects.valueAt(0).component : null;
    972         if (Objects.equals(suppressor, mEffectsSuppressor)) return;
    973         mEffectsSuppressor = suppressor;
    974         getContext().sendBroadcast(new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)
    975                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
    976     }
    977 
    978     private void updateInterruptionFilterLocked() {
    979         int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
    980         if (interruptionFilter == mInterruptionFilter) return;
    981         mInterruptionFilter = interruptionFilter;
    982         scheduleInterruptionFilterChanged(interruptionFilter);
    983     }
    984 
    985     private final IBinder mService = new INotificationManager.Stub() {
    986         // Toasts
    987         // ============================================================================
    988 
    989         @Override
    990         public void enqueueToast(String pkg, ITransientNotification callback, int duration)
    991         {
    992             if (DBG) {
    993                 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
    994                         + " duration=" + duration);
    995             }
    996 
    997             if (pkg == null || callback == null) {
    998                 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
    999                 return ;
   1000             }
   1001 
   1002             final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
   1003 
   1004             if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
   1005                 if (!isSystemToast) {
   1006                     Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
   1007                     return;
   1008                 }
   1009             }
   1010 
   1011             synchronized (mToastQueue) {
   1012                 int callingPid = Binder.getCallingPid();
   1013                 long callingId = Binder.clearCallingIdentity();
   1014                 try {
   1015                     ToastRecord record;
   1016                     int index = indexOfToastLocked(pkg, callback);
   1017                     // If it's already in the queue, we update it in place, we don't
   1018                     // move it to the end of the queue.
   1019                     if (index >= 0) {
   1020                         record = mToastQueue.get(index);
   1021                         record.update(duration);
   1022                     } else {
   1023                         // Limit the number of toasts that any given package except the android
   1024                         // package can enqueue.  Prevents DOS attacks and deals with leaks.
   1025                         if (!isSystemToast) {
   1026                             int count = 0;
   1027                             final int N = mToastQueue.size();
   1028                             for (int i=0; i<N; i++) {
   1029                                  final ToastRecord r = mToastQueue.get(i);
   1030                                  if (r.pkg.equals(pkg)) {
   1031                                      count++;
   1032                                      if (count >= MAX_PACKAGE_NOTIFICATIONS) {
   1033                                          Slog.e(TAG, "Package has already posted " + count
   1034                                                 + " toasts. Not showing more. Package=" + pkg);
   1035                                          return;
   1036                                      }
   1037                                  }
   1038                             }
   1039                         }
   1040 
   1041                         record = new ToastRecord(callingPid, pkg, callback, duration);
   1042                         mToastQueue.add(record);
   1043                         index = mToastQueue.size() - 1;
   1044                         keepProcessAliveLocked(callingPid);
   1045                     }
   1046                     // If it's at index 0, it's the current toast.  It doesn't matter if it's
   1047                     // new or just been updated.  Call back and tell it to show itself.
   1048                     // If the callback fails, this will remove it from the list, so don't
   1049                     // assume that it's valid after this.
   1050                     if (index == 0) {
   1051                         showNextToastLocked();
   1052                     }
   1053                 } finally {
   1054                     Binder.restoreCallingIdentity(callingId);
   1055                 }
   1056             }
   1057         }
   1058 
   1059         @Override
   1060         public void cancelToast(String pkg, ITransientNotification callback) {
   1061             Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
   1062 
   1063             if (pkg == null || callback == null) {
   1064                 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
   1065                 return ;
   1066             }
   1067 
   1068             synchronized (mToastQueue) {
   1069                 long callingId = Binder.clearCallingIdentity();
   1070                 try {
   1071                     int index = indexOfToastLocked(pkg, callback);
   1072                     if (index >= 0) {
   1073                         cancelToastLocked(index);
   1074                     } else {
   1075                         Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
   1076                                 + " callback=" + callback);
   1077                     }
   1078                 } finally {
   1079                     Binder.restoreCallingIdentity(callingId);
   1080                 }
   1081             }
   1082         }
   1083 
   1084         @Override
   1085         public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
   1086                 Notification notification, int[] idOut, int userId) throws RemoteException {
   1087             enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
   1088                     Binder.getCallingPid(), tag, id, notification, idOut, userId);
   1089         }
   1090 
   1091         @Override
   1092         public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
   1093             checkCallerIsSystemOrSameApp(pkg);
   1094             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
   1095                     Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
   1096             // Don't allow client applications to cancel foreground service notis.
   1097             cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
   1098                     Binder.getCallingUid() == Process.SYSTEM_UID
   1099                     ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
   1100                     null);
   1101         }
   1102 
   1103         @Override
   1104         public void cancelAllNotifications(String pkg, int userId) {
   1105             checkCallerIsSystemOrSameApp(pkg);
   1106 
   1107             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
   1108                     Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
   1109 
   1110             // Calling from user space, don't allow the canceling of actively
   1111             // running foreground services.
   1112             cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
   1113                     pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
   1114                     REASON_NOMAN_CANCEL_ALL, null);
   1115         }
   1116 
   1117         @Override
   1118         public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
   1119             checkCallerIsSystem();
   1120 
   1121             setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
   1122         }
   1123 
   1124         /**
   1125          * Use this when you just want to know if notifications are OK for this package.
   1126          */
   1127         @Override
   1128         public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
   1129             checkCallerIsSystem();
   1130             return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
   1131                     == AppOpsManager.MODE_ALLOWED);
   1132         }
   1133 
   1134         @Override
   1135         public void setPackagePriority(String pkg, int uid, int priority) {
   1136             checkCallerIsSystem();
   1137             mRankingHelper.setPackagePriority(pkg, uid, priority);
   1138             savePolicyFile();
   1139         }
   1140 
   1141         @Override
   1142         public int getPackagePriority(String pkg, int uid) {
   1143             checkCallerIsSystem();
   1144             return mRankingHelper.getPackagePriority(pkg, uid);
   1145         }
   1146 
   1147         @Override
   1148         public void setPackageVisibilityOverride(String pkg, int uid, int visibility) {
   1149             checkCallerIsSystem();
   1150             mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility);
   1151             savePolicyFile();
   1152         }
   1153 
   1154         @Override
   1155         public int getPackageVisibilityOverride(String pkg, int uid) {
   1156             checkCallerIsSystem();
   1157             return mRankingHelper.getPackageVisibilityOverride(pkg, uid);
   1158         }
   1159 
   1160         /**
   1161          * System-only API for getting a list of current (i.e. not cleared) notifications.
   1162          *
   1163          * Requires ACCESS_NOTIFICATIONS which is signature|system.
   1164          * @returns A list of all the notifications, in natural order.
   1165          */
   1166         @Override
   1167         public StatusBarNotification[] getActiveNotifications(String callingPkg) {
   1168             // enforce() will ensure the calling uid has the correct permission
   1169             getContext().enforceCallingOrSelfPermission(
   1170                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
   1171                     "NotificationManagerService.getActiveNotifications");
   1172 
   1173             StatusBarNotification[] tmp = null;
   1174             int uid = Binder.getCallingUid();
   1175 
   1176             // noteOp will check to make sure the callingPkg matches the uid
   1177             if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
   1178                     == AppOpsManager.MODE_ALLOWED) {
   1179                 synchronized (mNotificationList) {
   1180                     tmp = new StatusBarNotification[mNotificationList.size()];
   1181                     final int N = mNotificationList.size();
   1182                     for (int i=0; i<N; i++) {
   1183                         tmp[i] = mNotificationList.get(i).sbn;
   1184                     }
   1185                 }
   1186             }
   1187             return tmp;
   1188         }
   1189 
   1190         /**
   1191          * System-only API for getting a list of recent (cleared, no longer shown) notifications.
   1192          *
   1193          * Requires ACCESS_NOTIFICATIONS which is signature|system.
   1194          */
   1195         @Override
   1196         public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
   1197             // enforce() will ensure the calling uid has the correct permission
   1198             getContext().enforceCallingOrSelfPermission(
   1199                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
   1200                     "NotificationManagerService.getHistoricalNotifications");
   1201 
   1202             StatusBarNotification[] tmp = null;
   1203             int uid = Binder.getCallingUid();
   1204 
   1205             // noteOp will check to make sure the callingPkg matches the uid
   1206             if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
   1207                     == AppOpsManager.MODE_ALLOWED) {
   1208                 synchronized (mArchive) {
   1209                     tmp = mArchive.getArray(count);
   1210                 }
   1211             }
   1212             return tmp;
   1213         }
   1214 
   1215         /**
   1216          * Register a listener binder directly with the notification manager.
   1217          *
   1218          * Only works with system callers. Apps should extend
   1219          * {@link android.service.notification.NotificationListenerService}.
   1220          */
   1221         @Override
   1222         public void registerListener(final INotificationListener listener,
   1223                 final ComponentName component, final int userid) {
   1224             enforceSystemOrSystemUI("INotificationManager.registerListener");
   1225             mListeners.registerService(listener, component, userid);
   1226         }
   1227 
   1228         /**
   1229          * Remove a listener binder directly
   1230          */
   1231         @Override
   1232         public void unregisterListener(INotificationListener listener, int userid) {
   1233             mListeners.unregisterService(listener, userid);
   1234         }
   1235 
   1236         /**
   1237          * Allow an INotificationListener to simulate a "clear all" operation.
   1238          *
   1239          * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
   1240          *
   1241          * @param token The binder for the listener, to check that the caller is allowed
   1242          */
   1243         @Override
   1244         public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
   1245             final int callingUid = Binder.getCallingUid();
   1246             final int callingPid = Binder.getCallingPid();
   1247             long identity = Binder.clearCallingIdentity();
   1248             try {
   1249                 synchronized (mNotificationList) {
   1250                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
   1251                     if (keys != null) {
   1252                         final int N = keys.length;
   1253                         for (int i = 0; i < N; i++) {
   1254                             NotificationRecord r = mNotificationsByKey.get(keys[i]);
   1255                             if (r == null) continue;
   1256                             final int userId = r.sbn.getUserId();
   1257                             if (userId != info.userid && userId != UserHandle.USER_ALL &&
   1258                                     !mUserProfiles.isCurrentProfile(userId)) {
   1259                                 throw new SecurityException("Disallowed call from listener: "
   1260                                         + info.service);
   1261                             }
   1262                             cancelNotificationFromListenerLocked(info, callingUid, callingPid,
   1263                                     r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
   1264                                     userId);
   1265                         }
   1266                     } else {
   1267                         cancelAllLocked(callingUid, callingPid, info.userid,
   1268                                 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
   1269                     }
   1270                 }
   1271             } finally {
   1272                 Binder.restoreCallingIdentity(identity);
   1273             }
   1274         }
   1275 
   1276         private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
   1277                 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
   1278             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
   1279                     Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
   1280                     true,
   1281                     userId, REASON_LISTENER_CANCEL, info);
   1282         }
   1283 
   1284         /**
   1285          * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
   1286          *
   1287          * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
   1288          *
   1289          * @param token The binder for the listener, to check that the caller is allowed
   1290          */
   1291         @Override
   1292         public void cancelNotificationFromListener(INotificationListener token, String pkg,
   1293                 String tag, int id) {
   1294             final int callingUid = Binder.getCallingUid();
   1295             final int callingPid = Binder.getCallingPid();
   1296             long identity = Binder.clearCallingIdentity();
   1297             try {
   1298                 synchronized (mNotificationList) {
   1299                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
   1300                     if (info.supportsProfiles()) {
   1301                         Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
   1302                                 + "from " + info.component
   1303                                 + " use cancelNotification(key) instead.");
   1304                     } else {
   1305                         cancelNotificationFromListenerLocked(info, callingUid, callingPid,
   1306                                 pkg, tag, id, info.userid);
   1307                     }
   1308                 }
   1309             } finally {
   1310                 Binder.restoreCallingIdentity(identity);
   1311             }
   1312         }
   1313 
   1314         /**
   1315          * Allow an INotificationListener to request the list of outstanding notifications seen by
   1316          * the current user. Useful when starting up, after which point the listener callbacks
   1317          * should be used.
   1318          *
   1319          * @param token The binder for the listener, to check that the caller is allowed
   1320          * @param keys An array of notification keys to fetch, or null to fetch everything
   1321          * @returns The return value will contain the notifications specified in keys, in that
   1322          *      order, or if keys is null, all the notifications, in natural order.
   1323          */
   1324         @Override
   1325         public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
   1326                 INotificationListener token, String[] keys, int trim) {
   1327             synchronized (mNotificationList) {
   1328                 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
   1329                 final boolean getKeys = keys != null;
   1330                 final int N = getKeys ? keys.length : mNotificationList.size();
   1331                 final ArrayList<StatusBarNotification> list
   1332                         = new ArrayList<StatusBarNotification>(N);
   1333                 for (int i=0; i<N; i++) {
   1334                     final NotificationRecord r = getKeys
   1335                             ? mNotificationsByKey.get(keys[i])
   1336                             : mNotificationList.get(i);
   1337                     if (r == null) continue;
   1338                     StatusBarNotification sbn = r.sbn;
   1339                     if (!isVisibleToListener(sbn, info)) continue;
   1340                     StatusBarNotification sbnToSend =
   1341                             (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
   1342                     list.add(sbnToSend);
   1343                 }
   1344                 return new ParceledListSlice<StatusBarNotification>(list);
   1345             }
   1346         }
   1347 
   1348         @Override
   1349         public void requestHintsFromListener(INotificationListener token, int hints) {
   1350             final long identity = Binder.clearCallingIdentity();
   1351             try {
   1352                 synchronized (mNotificationList) {
   1353                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
   1354                     final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
   1355                     if (disableEffects) {
   1356                         mListenersDisablingEffects.add(info);
   1357                     } else {
   1358                         mListenersDisablingEffects.remove(info);
   1359                     }
   1360                     updateListenerHintsLocked();
   1361                     updateEffectsSuppressorLocked();
   1362                 }
   1363             } finally {
   1364                 Binder.restoreCallingIdentity(identity);
   1365             }
   1366         }
   1367 
   1368         @Override
   1369         public int getHintsFromListener(INotificationListener token) {
   1370             synchronized (mNotificationList) {
   1371                 return mListenerHints;
   1372             }
   1373         }
   1374 
   1375         @Override
   1376         public void requestInterruptionFilterFromListener(INotificationListener token,
   1377                 int interruptionFilter) throws RemoteException {
   1378             final long identity = Binder.clearCallingIdentity();
   1379             try {
   1380                 synchronized (mNotificationList) {
   1381                     mListeners.checkServiceTokenLocked(token);
   1382                     mZenModeHelper.requestFromListener(interruptionFilter);
   1383                     updateInterruptionFilterLocked();
   1384                 }
   1385             } finally {
   1386                 Binder.restoreCallingIdentity(identity);
   1387             }
   1388         }
   1389 
   1390         @Override
   1391         public int getInterruptionFilterFromListener(INotificationListener token)
   1392                 throws RemoteException {
   1393             synchronized (mNotificationLight) {
   1394                 return mInterruptionFilter;
   1395             }
   1396         }
   1397 
   1398         @Override
   1399         public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
   1400                 throws RemoteException {
   1401             synchronized (mNotificationList) {
   1402                 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
   1403                 if (info == null) return;
   1404                 mListeners.setOnNotificationPostedTrimLocked(info, trim);
   1405             }
   1406         }
   1407 
   1408         @Override
   1409         public ZenModeConfig getZenModeConfig() {
   1410             enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
   1411             return mZenModeHelper.getConfig();
   1412         }
   1413 
   1414         @Override
   1415         public boolean setZenModeConfig(ZenModeConfig config) {
   1416             checkCallerIsSystem();
   1417             return mZenModeHelper.setConfig(config);
   1418         }
   1419 
   1420         @Override
   1421         public void notifyConditions(String pkg, IConditionProvider provider,
   1422                 Condition[] conditions) {
   1423             final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
   1424             checkCallerIsSystemOrSameApp(pkg);
   1425             final long identity = Binder.clearCallingIdentity();
   1426             try {
   1427                 mConditionProviders.notifyConditions(pkg, info, conditions);
   1428             } finally {
   1429                 Binder.restoreCallingIdentity(identity);
   1430             }
   1431         }
   1432 
   1433         @Override
   1434         public void requestZenModeConditions(IConditionListener callback, int relevance) {
   1435             enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
   1436             mConditionProviders.requestZenModeConditions(callback, relevance);
   1437         }
   1438 
   1439         @Override
   1440         public void setZenModeCondition(Condition condition) {
   1441             enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
   1442             final long identity = Binder.clearCallingIdentity();
   1443             try {
   1444                 mConditionProviders.setZenModeCondition(condition, "binderCall");
   1445             } finally {
   1446                 Binder.restoreCallingIdentity(identity);
   1447             }
   1448         }
   1449 
   1450         @Override
   1451         public void setAutomaticZenModeConditions(Uri[] conditionIds) {
   1452             enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions");
   1453             mConditionProviders.setAutomaticZenModeConditions(conditionIds);
   1454         }
   1455 
   1456         @Override
   1457         public Condition[] getAutomaticZenModeConditions() {
   1458             enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions");
   1459             return mConditionProviders.getAutomaticZenModeConditions();
   1460         }
   1461 
   1462         private void enforceSystemOrSystemUI(String message) {
   1463             if (isCallerSystem()) return;
   1464             getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
   1465                     message);
   1466         }
   1467 
   1468         @Override
   1469         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1470             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
   1471                     != PackageManager.PERMISSION_GRANTED) {
   1472                 pw.println("Permission Denial: can't dump NotificationManager from pid="
   1473                         + Binder.getCallingPid()
   1474                         + ", uid=" + Binder.getCallingUid());
   1475                 return;
   1476             }
   1477 
   1478             dumpImpl(pw, DumpFilter.parseFromArguments(args));
   1479         }
   1480 
   1481         @Override
   1482         public ComponentName getEffectsSuppressor() {
   1483             enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor");
   1484             return mEffectsSuppressor;
   1485         }
   1486 
   1487         @Override
   1488         public boolean matchesCallFilter(Bundle extras) {
   1489             enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
   1490             return mZenModeHelper.matchesCallFilter(
   1491                     UserHandle.getCallingUserHandle(),
   1492                     extras,
   1493                     mRankingHelper.findExtractor(ValidateNotificationPeople.class),
   1494                     MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
   1495                     MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
   1496         }
   1497     };
   1498 
   1499     private String[] getActiveNotificationKeys(INotificationListener token) {
   1500         final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
   1501         final ArrayList<String> keys = new ArrayList<String>();
   1502         if (info.isEnabledForCurrentProfiles()) {
   1503             synchronized (mNotificationList) {
   1504                 final int N = mNotificationList.size();
   1505                 for (int i = 0; i < N; i++) {
   1506                     final StatusBarNotification sbn = mNotificationList.get(i).sbn;
   1507                     if (info.enabledAndUserMatches(sbn.getUserId())) {
   1508                         keys.add(sbn.getKey());
   1509                     }
   1510                 }
   1511             }
   1512         }
   1513         return keys.toArray(new String[keys.size()]);
   1514     }
   1515 
   1516     private String disableNotificationEffects(NotificationRecord record) {
   1517         if (mDisableNotificationEffects) {
   1518             return "booleanState";
   1519         }
   1520         if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
   1521             return "listenerHints";
   1522         }
   1523         if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
   1524             return "callState";
   1525         }
   1526         return null;
   1527     }
   1528 
   1529     void dumpImpl(PrintWriter pw, DumpFilter filter) {
   1530         pw.print("Current Notification Manager state");
   1531         if (filter != null) {
   1532             pw.print(" (filtered to "); pw.print(filter); pw.print(")");
   1533         }
   1534         pw.println(':');
   1535         int N;
   1536         final boolean zenOnly = filter != null && filter.zen;
   1537 
   1538         if (!zenOnly) {
   1539             synchronized (mToastQueue) {
   1540                 N = mToastQueue.size();
   1541                 if (N > 0) {
   1542                     pw.println("  Toast Queue:");
   1543                     for (int i=0; i<N; i++) {
   1544                         mToastQueue.get(i).dump(pw, "    ", filter);
   1545                     }
   1546                     pw.println("  ");
   1547                 }
   1548             }
   1549         }
   1550 
   1551         synchronized (mNotificationList) {
   1552             if (!zenOnly) {
   1553                 N = mNotificationList.size();
   1554                 if (N > 0) {
   1555                     pw.println("  Notification List:");
   1556                     for (int i=0; i<N; i++) {
   1557                         final NotificationRecord nr = mNotificationList.get(i);
   1558                         if (filter != null && !filter.matches(nr.sbn)) continue;
   1559                         nr.dump(pw, "    ", getContext());
   1560                     }
   1561                     pw.println("  ");
   1562                 }
   1563 
   1564                 if (filter == null) {
   1565                     N = mLights.size();
   1566                     if (N > 0) {
   1567                         pw.println("  Lights List:");
   1568                         for (int i=0; i<N; i++) {
   1569                             pw.println("    " + mLights.get(i));
   1570                         }
   1571                         pw.println("  ");
   1572                     }
   1573                     pw.println("  mUseAttentionLight=" + mUseAttentionLight);
   1574                     pw.println("  mNotificationPulseEnabled=" + mNotificationPulseEnabled);
   1575                     pw.println("  mSoundNotification=" + mSoundNotification);
   1576                     pw.println("  mVibrateNotification=" + mVibrateNotification);
   1577                     pw.println("  mDisableNotificationEffects=" + mDisableNotificationEffects);
   1578                     pw.println("  mCallState=" + callStateToString(mCallState));
   1579                     pw.println("  mSystemReady=" + mSystemReady);
   1580                 }
   1581                 pw.println("  mArchive=" + mArchive.toString());
   1582                 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
   1583                 int i=0;
   1584                 while (iter.hasNext()) {
   1585                     final StatusBarNotification sbn = iter.next();
   1586                     if (filter != null && !filter.matches(sbn)) continue;
   1587                     pw.println("    " + sbn);
   1588                     if (++i >= 5) {
   1589                         if (iter.hasNext()) pw.println("    ...");
   1590                         break;
   1591                     }
   1592                 }
   1593             }
   1594 
   1595             if (!zenOnly) {
   1596                 pw.println("\n  Usage Stats:");
   1597                 mUsageStats.dump(pw, "    ", filter);
   1598             }
   1599 
   1600             if (filter == null || zenOnly) {
   1601                 pw.println("\n  Zen Mode:");
   1602                 mZenModeHelper.dump(pw, "    ");
   1603 
   1604                 pw.println("\n  Zen Log:");
   1605                 ZenLog.dump(pw, "    ");
   1606             }
   1607 
   1608             if (!zenOnly) {
   1609                 pw.println("\n  Ranking Config:");
   1610                 mRankingHelper.dump(pw, "    ", filter);
   1611 
   1612                 pw.println("\n  Notification listeners:");
   1613                 mListeners.dump(pw, filter);
   1614                 pw.print("    mListenerHints: "); pw.println(mListenerHints);
   1615                 pw.print("    mListenersDisablingEffects: (");
   1616                 N = mListenersDisablingEffects.size();
   1617                 for (int i = 0; i < N; i++) {
   1618                     final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
   1619                     if (i > 0) pw.print(',');
   1620                     pw.print(listener.component);
   1621                 }
   1622                 pw.println(')');
   1623             }
   1624 
   1625             pw.println("\n  Condition providers:");
   1626             mConditionProviders.dump(pw, filter);
   1627         }
   1628     }
   1629 
   1630     /**
   1631      * The private API only accessible to the system process.
   1632      */
   1633     private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
   1634         @Override
   1635         public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
   1636                 String tag, int id, Notification notification, int[] idReceived, int userId) {
   1637             enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
   1638                     idReceived, userId);
   1639         }
   1640 
   1641         @Override
   1642         public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
   1643                 int userId) {
   1644             checkCallerIsSystem();
   1645             synchronized (mNotificationList) {
   1646                 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
   1647                 if (i < 0) {
   1648                     Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
   1649                             + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
   1650                     return;
   1651                 }
   1652                 NotificationRecord r = mNotificationList.get(i);
   1653                 StatusBarNotification sbn = r.sbn;
   1654                 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
   1655                 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
   1656                 // we have to revert to the flags we received initially *and* force remove
   1657                 // FLAG_FOREGROUND_SERVICE.
   1658                 sbn.getNotification().flags =
   1659                         (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
   1660                 mRankingHelper.sort(mNotificationList);
   1661                 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
   1662             }
   1663         }
   1664     };
   1665 
   1666     void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
   1667             final int callingPid, final String tag, final int id, final Notification notification,
   1668             int[] idOut, int incomingUserId) {
   1669         if (DBG) {
   1670             Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
   1671                     + " notification=" + notification);
   1672         }
   1673         checkCallerIsSystemOrSameApp(pkg);
   1674         final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
   1675         final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
   1676 
   1677         final int userId = ActivityManager.handleIncomingUser(callingPid,
   1678                 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
   1679         final UserHandle user = new UserHandle(userId);
   1680 
   1681         // Limit the number of notifications that any given package except the android
   1682         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
   1683         if (!isSystemNotification && !isNotificationFromListener) {
   1684             synchronized (mNotificationList) {
   1685                 int count = 0;
   1686                 final int N = mNotificationList.size();
   1687                 for (int i=0; i<N; i++) {
   1688                     final NotificationRecord r = mNotificationList.get(i);
   1689                     if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
   1690                         count++;
   1691                         if (count >= MAX_PACKAGE_NOTIFICATIONS) {
   1692                             Slog.e(TAG, "Package has already posted " + count
   1693                                     + " notifications.  Not showing more.  package=" + pkg);
   1694                             return;
   1695                         }
   1696                     }
   1697                 }
   1698             }
   1699         }
   1700 
   1701         // This conditional is a dirty hack to limit the logging done on
   1702         //     behalf of the download manager without affecting other apps.
   1703         if (!pkg.equals("com.android.providers.downloads")
   1704                 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
   1705             EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
   1706                     pkg, id, tag, userId, notification.toString());
   1707         }
   1708 
   1709         if (pkg == null || notification == null) {
   1710             throw new IllegalArgumentException("null not allowed: pkg=" + pkg
   1711                     + " id=" + id + " notification=" + notification);
   1712         }
   1713         if (notification.icon != 0) {
   1714             if (!notification.isValid()) {
   1715                 throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
   1716                         + " id=" + id + " notification=" + notification);
   1717             }
   1718         }
   1719 
   1720         mHandler.post(new Runnable() {
   1721             @Override
   1722             public void run() {
   1723 
   1724                 synchronized (mNotificationList) {
   1725 
   1726                     // === Scoring ===
   1727 
   1728                     // 0. Sanitize inputs
   1729                     notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
   1730                             Notification.PRIORITY_MAX);
   1731                     // Migrate notification flags to scores
   1732                     if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
   1733                         if (notification.priority < Notification.PRIORITY_MAX) {
   1734                             notification.priority = Notification.PRIORITY_MAX;
   1735                         }
   1736                     } else if (SCORE_ONGOING_HIGHER &&
   1737                             0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
   1738                         if (notification.priority < Notification.PRIORITY_HIGH) {
   1739                             notification.priority = Notification.PRIORITY_HIGH;
   1740                         }
   1741                     }
   1742 
   1743                     // 1. initial score: buckets of 10, around the app [-20..20]
   1744                     final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
   1745 
   1746                     // 2. extract ranking signals from the notification data
   1747                     final StatusBarNotification n = new StatusBarNotification(
   1748                             pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
   1749                             user);
   1750                     NotificationRecord r = new NotificationRecord(n, score);
   1751                     NotificationRecord old = mNotificationsByKey.get(n.getKey());
   1752                     if (old != null) {
   1753                         // Retain ranking information from previous record
   1754                         r.copyRankingInformation(old);
   1755                     }
   1756                     mRankingHelper.extractSignals(r);
   1757 
   1758                     // 3. Apply local rules
   1759 
   1760                     // blocked apps
   1761                     if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
   1762                         if (!isSystemNotification) {
   1763                             r.score = JUNK_SCORE;
   1764                             Slog.e(TAG, "Suppressing notification from package " + pkg
   1765                                     + " by user request.");
   1766                         }
   1767                     }
   1768 
   1769                     if (r.score < SCORE_DISPLAY_THRESHOLD) {
   1770                         // Notification will be blocked because the score is too low.
   1771                         return;
   1772                     }
   1773 
   1774                     // Clear out group children of the old notification if the update causes the
   1775                     // group summary to go away. This happens when the old notification was a
   1776                     // summary and the new one isn't, or when the old notification was a summary
   1777                     // and its group key changed.
   1778                     if (old != null && old.getNotification().isGroupSummary() &&
   1779                             (!notification.isGroupSummary() ||
   1780                                     !old.getGroupKey().equals(r.getGroupKey()))) {
   1781                         cancelGroupChildrenLocked(old, callingUid, callingPid, null);
   1782                     }
   1783 
   1784                     int index = indexOfNotificationLocked(n.getKey());
   1785                     if (index < 0) {
   1786                         mNotificationList.add(r);
   1787                         mUsageStats.registerPostedByApp(r);
   1788                     } else {
   1789                         old = mNotificationList.get(index);
   1790                         mNotificationList.set(index, r);
   1791                         mUsageStats.registerUpdatedByApp(r, old);
   1792                         // Make sure we don't lose the foreground service state.
   1793                         notification.flags |=
   1794                                 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
   1795                         r.isUpdate = true;
   1796                     }
   1797 
   1798                     mNotificationsByKey.put(n.getKey(), r);
   1799 
   1800                     // Ensure if this is a foreground service that the proper additional
   1801                     // flags are set.
   1802                     if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
   1803                         notification.flags |= Notification.FLAG_ONGOING_EVENT
   1804                                 | Notification.FLAG_NO_CLEAR;
   1805                     }
   1806 
   1807                     applyZenModeLocked(r);
   1808                     mRankingHelper.sort(mNotificationList);
   1809 
   1810                     if (notification.icon != 0) {
   1811                         StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
   1812                         mListeners.notifyPostedLocked(n, oldSbn);
   1813                     } else {
   1814                         Slog.e(TAG, "Not posting notification with icon==0: " + notification);
   1815                         if (old != null && !old.isCanceled) {
   1816                             mListeners.notifyRemovedLocked(n);
   1817                         }
   1818                         // ATTENTION: in a future release we will bail out here
   1819                         // so that we do not play sounds, show lights, etc. for invalid
   1820                         // notifications
   1821                         Slog.e(TAG, "WARNING: In a future release this will crash the app: "
   1822                                 + n.getPackageName());
   1823                     }
   1824 
   1825                     buzzBeepBlinkLocked(r);
   1826                 }
   1827             }
   1828         });
   1829 
   1830         idOut[0] = id;
   1831     }
   1832 
   1833     private void buzzBeepBlinkLocked(NotificationRecord record) {
   1834         boolean buzzBeepBlinked = false;
   1835         final Notification notification = record.sbn.getNotification();
   1836 
   1837         // Should this notification make noise, vibe, or use the LED?
   1838         final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) &&
   1839                 !record.isIntercepted();
   1840         if (DBG || record.isIntercepted())
   1841             Slog.v(TAG,
   1842                     "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
   1843                             " intercept=" + record.isIntercepted()
   1844             );
   1845 
   1846         final int currentUser;
   1847         final long token = Binder.clearCallingIdentity();
   1848         try {
   1849             currentUser = ActivityManager.getCurrentUser();
   1850         } finally {
   1851             Binder.restoreCallingIdentity(token);
   1852         }
   1853 
   1854         // If we're not supposed to beep, vibrate, etc. then don't.
   1855         final String disableEffects = disableNotificationEffects(record);
   1856         if (disableEffects != null) {
   1857             ZenLog.traceDisableEffects(record, disableEffects);
   1858         }
   1859         if (disableEffects == null
   1860                 && (!(record.isUpdate
   1861                     && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
   1862                 && (record.getUserId() == UserHandle.USER_ALL ||
   1863                     record.getUserId() == currentUser ||
   1864                     mUserProfiles.isCurrentProfile(record.getUserId()))
   1865                 && canInterrupt
   1866                 && mSystemReady
   1867                 && mAudioManager != null) {
   1868             if (DBG) Slog.v(TAG, "Interrupting!");
   1869 
   1870             sendAccessibilityEvent(notification, record.sbn.getPackageName());
   1871 
   1872             // sound
   1873 
   1874             // should we use the default notification sound? (indicated either by
   1875             // DEFAULT_SOUND or because notification.sound is pointing at
   1876             // Settings.System.NOTIFICATION_SOUND)
   1877             final boolean useDefaultSound =
   1878                    (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
   1879                            Settings.System.DEFAULT_NOTIFICATION_URI
   1880                                    .equals(notification.sound);
   1881 
   1882             Uri soundUri = null;
   1883             boolean hasValidSound = false;
   1884 
   1885             if (useDefaultSound) {
   1886                 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
   1887 
   1888                 // check to see if the default notification sound is silent
   1889                 ContentResolver resolver = getContext().getContentResolver();
   1890                 hasValidSound = Settings.System.getString(resolver,
   1891                        Settings.System.NOTIFICATION_SOUND) != null;
   1892             } else if (notification.sound != null) {
   1893                 soundUri = notification.sound;
   1894                 hasValidSound = (soundUri != null);
   1895             }
   1896 
   1897             if (hasValidSound) {
   1898                 boolean looping =
   1899                         (notification.flags & Notification.FLAG_INSISTENT) != 0;
   1900                 AudioAttributes audioAttributes;
   1901                 if (notification.audioAttributes != null) {
   1902                     audioAttributes = notification.audioAttributes;
   1903                 } else {
   1904                     audioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
   1905                 }
   1906                 mSoundNotification = record;
   1907                 // do not play notifications if stream volume is 0 (typically because
   1908                 // ringer mode is silent) or if there is a user of exclusive audio focus
   1909                 if ((mAudioManager.getStreamVolume(
   1910                         AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
   1911                             && !mAudioManager.isAudioFocusExclusive()) {
   1912                     final long identity = Binder.clearCallingIdentity();
   1913                     try {
   1914                         final IRingtonePlayer player =
   1915                                 mAudioManager.getRingtonePlayer();
   1916                         if (player != null) {
   1917                             if (DBG) Slog.v(TAG, "Playing sound " + soundUri
   1918                                     + " with attributes " + audioAttributes);
   1919                             player.playAsync(soundUri, record.sbn.getUser(), looping,
   1920                                     audioAttributes);
   1921                             buzzBeepBlinked = true;
   1922                         }
   1923                     } catch (RemoteException e) {
   1924                     } finally {
   1925                         Binder.restoreCallingIdentity(identity);
   1926                     }
   1927                 }
   1928             }
   1929 
   1930             // vibrate
   1931             // Does the notification want to specify its own vibration?
   1932             final boolean hasCustomVibrate = notification.vibrate != null;
   1933 
   1934             // new in 4.2: if there was supposed to be a sound and we're in vibrate
   1935             // mode, and no other vibration is specified, we fall back to vibration
   1936             final boolean convertSoundToVibration =
   1937                        !hasCustomVibrate
   1938                     && hasValidSound
   1939                     && (mAudioManager.getRingerMode()
   1940                                == AudioManager.RINGER_MODE_VIBRATE);
   1941 
   1942             // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
   1943             final boolean useDefaultVibrate =
   1944                     (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
   1945 
   1946             if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
   1947                     && !(mAudioManager.getRingerMode()
   1948                             == AudioManager.RINGER_MODE_SILENT)) {
   1949                 mVibrateNotification = record;
   1950 
   1951                 if (useDefaultVibrate || convertSoundToVibration) {
   1952                     // Escalate privileges so we can use the vibrator even if the
   1953                     // notifying app does not have the VIBRATE permission.
   1954                     long identity = Binder.clearCallingIdentity();
   1955                     try {
   1956                         mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
   1957                             useDefaultVibrate ? mDefaultVibrationPattern
   1958                                 : mFallbackVibrationPattern,
   1959                             ((notification.flags & Notification.FLAG_INSISTENT) != 0)
   1960                                 ? 0: -1, audioAttributesForNotification(notification));
   1961                         buzzBeepBlinked = true;
   1962                     } finally {
   1963                         Binder.restoreCallingIdentity(identity);
   1964                     }
   1965                 } else if (notification.vibrate.length > 1) {
   1966                     // If you want your own vibration pattern, you need the VIBRATE
   1967                     // permission
   1968                     mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
   1969                             notification.vibrate,
   1970                         ((notification.flags & Notification.FLAG_INSISTENT) != 0)
   1971                                 ? 0: -1, audioAttributesForNotification(notification));
   1972                     buzzBeepBlinked = true;
   1973                 }
   1974             }
   1975         }
   1976 
   1977         // light
   1978         // release the light
   1979         boolean wasShowLights = mLights.remove(record.getKey());
   1980         if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) {
   1981             mLedNotification = null;
   1982         }
   1983         if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) {
   1984             mLights.add(record.getKey());
   1985             updateLightsLocked();
   1986             if (mUseAttentionLight) {
   1987                 mAttentionLight.pulse();
   1988             }
   1989             buzzBeepBlinked = true;
   1990         } else if (wasShowLights) {
   1991             updateLightsLocked();
   1992         }
   1993         if (buzzBeepBlinked) {
   1994             mHandler.post(mBuzzBeepBlinked);
   1995         }
   1996     }
   1997 
   1998     private static AudioAttributes audioAttributesForNotification(Notification n) {
   1999         if (n.audioAttributes != null) {
   2000             return n.audioAttributes;
   2001         } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
   2002             // the stream type is valid, use it
   2003             return new AudioAttributes.Builder()
   2004                     .setInternalLegacyStreamType(n.audioStreamType)
   2005                     .build();
   2006         } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
   2007             return Notification.AUDIO_ATTRIBUTES_DEFAULT;
   2008         } else {
   2009             Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
   2010             return Notification.AUDIO_ATTRIBUTES_DEFAULT;
   2011         }
   2012     }
   2013 
   2014     void showNextToastLocked() {
   2015         ToastRecord record = mToastQueue.get(0);
   2016         while (record != null) {
   2017             if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
   2018             try {
   2019                 record.callback.show();
   2020                 scheduleTimeoutLocked(record);
   2021                 return;
   2022             } catch (RemoteException e) {
   2023                 Slog.w(TAG, "Object died trying to show notification " + record.callback
   2024                         + " in package " + record.pkg);
   2025                 // remove it from the list and let the process die
   2026                 int index = mToastQueue.indexOf(record);
   2027                 if (index >= 0) {
   2028                     mToastQueue.remove(index);
   2029                 }
   2030                 keepProcessAliveLocked(record.pid);
   2031                 if (mToastQueue.size() > 0) {
   2032                     record = mToastQueue.get(0);
   2033                 } else {
   2034                     record = null;
   2035                 }
   2036             }
   2037         }
   2038     }
   2039 
   2040     void cancelToastLocked(int index) {
   2041         ToastRecord record = mToastQueue.get(index);
   2042         try {
   2043             record.callback.hide();
   2044         } catch (RemoteException e) {
   2045             Slog.w(TAG, "Object died trying to hide notification " + record.callback
   2046                     + " in package " + record.pkg);
   2047             // don't worry about this, we're about to remove it from
   2048             // the list anyway
   2049         }
   2050         mToastQueue.remove(index);
   2051         keepProcessAliveLocked(record.pid);
   2052         if (mToastQueue.size() > 0) {
   2053             // Show the next one. If the callback fails, this will remove
   2054             // it from the list, so don't assume that the list hasn't changed
   2055             // after this point.
   2056             showNextToastLocked();
   2057         }
   2058     }
   2059 
   2060     private void scheduleTimeoutLocked(ToastRecord r)
   2061     {
   2062         mHandler.removeCallbacksAndMessages(r);
   2063         Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
   2064         long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
   2065         mHandler.sendMessageDelayed(m, delay);
   2066     }
   2067 
   2068     private void handleTimeout(ToastRecord record)
   2069     {
   2070         if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
   2071         synchronized (mToastQueue) {
   2072             int index = indexOfToastLocked(record.pkg, record.callback);
   2073             if (index >= 0) {
   2074                 cancelToastLocked(index);
   2075             }
   2076         }
   2077     }
   2078 
   2079     // lock on mToastQueue
   2080     int indexOfToastLocked(String pkg, ITransientNotification callback)
   2081     {
   2082         IBinder cbak = callback.asBinder();
   2083         ArrayList<ToastRecord> list = mToastQueue;
   2084         int len = list.size();
   2085         for (int i=0; i<len; i++) {
   2086             ToastRecord r = list.get(i);
   2087             if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
   2088                 return i;
   2089             }
   2090         }
   2091         return -1;
   2092     }
   2093 
   2094     // lock on mToastQueue
   2095     void keepProcessAliveLocked(int pid)
   2096     {
   2097         int toastCount = 0; // toasts from this pid
   2098         ArrayList<ToastRecord> list = mToastQueue;
   2099         int N = list.size();
   2100         for (int i=0; i<N; i++) {
   2101             ToastRecord r = list.get(i);
   2102             if (r.pid == pid) {
   2103                 toastCount++;
   2104             }
   2105         }
   2106         try {
   2107             mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
   2108         } catch (RemoteException e) {
   2109             // Shouldn't happen.
   2110         }
   2111     }
   2112 
   2113     private void handleRankingReconsideration(Message message) {
   2114         if (!(message.obj instanceof RankingReconsideration)) return;
   2115         RankingReconsideration recon = (RankingReconsideration) message.obj;
   2116         recon.run();
   2117         boolean changed;
   2118         synchronized (mNotificationList) {
   2119             final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
   2120             if (record == null) {
   2121                 return;
   2122             }
   2123             int indexBefore = findNotificationRecordIndexLocked(record);
   2124             boolean interceptBefore = record.isIntercepted();
   2125             int visibilityBefore = record.getPackageVisibilityOverride();
   2126             recon.applyChangesLocked(record);
   2127             applyZenModeLocked(record);
   2128             mRankingHelper.sort(mNotificationList);
   2129             int indexAfter = findNotificationRecordIndexLocked(record);
   2130             boolean interceptAfter = record.isIntercepted();
   2131             int visibilityAfter = record.getPackageVisibilityOverride();
   2132             changed = indexBefore != indexAfter || interceptBefore != interceptAfter
   2133                     || visibilityBefore != visibilityAfter;
   2134             if (interceptBefore && !interceptAfter) {
   2135                 buzzBeepBlinkLocked(record);
   2136             }
   2137         }
   2138         if (changed) {
   2139             scheduleSendRankingUpdate();
   2140         }
   2141     }
   2142 
   2143     private void handleRankingConfigChange() {
   2144         synchronized (mNotificationList) {
   2145             final int N = mNotificationList.size();
   2146             ArrayList<String> orderBefore = new ArrayList<String>(N);
   2147             int[] visibilities = new int[N];
   2148             for (int i = 0; i < N; i++) {
   2149                 final NotificationRecord r = mNotificationList.get(i);
   2150                 orderBefore.add(r.getKey());
   2151                 visibilities[i] = r.getPackageVisibilityOverride();
   2152                 mRankingHelper.extractSignals(r);
   2153             }
   2154             for (int i = 0; i < N; i++) {
   2155                 mRankingHelper.sort(mNotificationList);
   2156                 final NotificationRecord r = mNotificationList.get(i);
   2157                 if (!orderBefore.get(i).equals(r.getKey())
   2158                         || visibilities[i] != r.getPackageVisibilityOverride()) {
   2159                     scheduleSendRankingUpdate();
   2160                     return;
   2161                 }
   2162             }
   2163         }
   2164     }
   2165 
   2166     // let zen mode evaluate this record
   2167     private void applyZenModeLocked(NotificationRecord record) {
   2168         record.setIntercepted(mZenModeHelper.shouldIntercept(record));
   2169     }
   2170 
   2171     // lock on mNotificationList
   2172     private int findNotificationRecordIndexLocked(NotificationRecord target) {
   2173         return mRankingHelper.indexOf(mNotificationList, target);
   2174     }
   2175 
   2176     private void scheduleSendRankingUpdate() {
   2177         mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
   2178         Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
   2179         mHandler.sendMessage(m);
   2180     }
   2181 
   2182     private void handleSendRankingUpdate() {
   2183         synchronized (mNotificationList) {
   2184             mListeners.notifyRankingUpdateLocked();
   2185         }
   2186     }
   2187 
   2188     private void scheduleListenerHintsChanged(int state) {
   2189         mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
   2190         mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
   2191     }
   2192 
   2193     private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
   2194         mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
   2195         mHandler.obtainMessage(
   2196                 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
   2197                 listenerInterruptionFilter,
   2198                 0).sendToTarget();
   2199     }
   2200 
   2201     private void handleListenerHintsChanged(int hints) {
   2202         synchronized (mNotificationList) {
   2203             mListeners.notifyListenerHintsChangedLocked(hints);
   2204         }
   2205     }
   2206 
   2207     private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
   2208         synchronized (mNotificationList) {
   2209             mListeners.notifyInterruptionFilterChanged(interruptionFilter);
   2210         }
   2211     }
   2212 
   2213     private final class WorkerHandler extends Handler
   2214     {
   2215         @Override
   2216         public void handleMessage(Message msg)
   2217         {
   2218             switch (msg.what)
   2219             {
   2220                 case MESSAGE_TIMEOUT:
   2221                     handleTimeout((ToastRecord)msg.obj);
   2222                     break;
   2223                 case MESSAGE_SAVE_POLICY_FILE:
   2224                     handleSavePolicyFile();
   2225                     break;
   2226                 case MESSAGE_SEND_RANKING_UPDATE:
   2227                     handleSendRankingUpdate();
   2228                     break;
   2229                 case MESSAGE_LISTENER_HINTS_CHANGED:
   2230                     handleListenerHintsChanged(msg.arg1);
   2231                     break;
   2232                 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
   2233                     handleListenerInterruptionFilterChanged(msg.arg1);
   2234                     break;
   2235             }
   2236         }
   2237 
   2238     }
   2239 
   2240     private final class RankingWorkerHandler extends Handler
   2241     {
   2242         public RankingWorkerHandler(Looper looper) {
   2243             super(looper);
   2244         }
   2245 
   2246         @Override
   2247         public void handleMessage(Message msg) {
   2248             switch (msg.what) {
   2249                 case MESSAGE_RECONSIDER_RANKING:
   2250                     handleRankingReconsideration(msg);
   2251                     break;
   2252                 case MESSAGE_RANKING_CONFIG_CHANGE:
   2253                     handleRankingConfigChange();
   2254                     break;
   2255             }
   2256         }
   2257     }
   2258 
   2259     // Notifications
   2260     // ============================================================================
   2261     static int clamp(int x, int low, int high) {
   2262         return (x < low) ? low : ((x > high) ? high : x);
   2263     }
   2264 
   2265     void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
   2266         AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
   2267         if (!manager.isEnabled()) {
   2268             return;
   2269         }
   2270 
   2271         AccessibilityEvent event =
   2272             AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
   2273         event.setPackageName(packageName);
   2274         event.setClassName(Notification.class.getName());
   2275         event.setParcelableData(notification);
   2276         CharSequence tickerText = notification.tickerText;
   2277         if (!TextUtils.isEmpty(tickerText)) {
   2278             event.getText().add(tickerText);
   2279         }
   2280 
   2281         manager.sendAccessibilityEvent(event);
   2282     }
   2283 
   2284     private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
   2285         // tell the app
   2286         if (sendDelete) {
   2287             if (r.getNotification().deleteIntent != null) {
   2288                 try {
   2289                     r.getNotification().deleteIntent.send();
   2290                 } catch (PendingIntent.CanceledException ex) {
   2291                     // do nothing - there's no relevant way to recover, and
   2292                     //     no reason to let this propagate
   2293                     Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
   2294                 }
   2295             }
   2296         }
   2297 
   2298         // status bar
   2299         if (r.getNotification().icon != 0) {
   2300             r.isCanceled = true;
   2301             mListeners.notifyRemovedLocked(r.sbn);
   2302         }
   2303 
   2304         // sound
   2305         if (mSoundNotification == r) {
   2306             mSoundNotification = null;
   2307             final long identity = Binder.clearCallingIdentity();
   2308             try {
   2309                 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
   2310                 if (player != null) {
   2311                     player.stopAsync();
   2312                 }
   2313             } catch (RemoteException e) {
   2314             } finally {
   2315                 Binder.restoreCallingIdentity(identity);
   2316             }
   2317         }
   2318 
   2319         // vibrate
   2320         if (mVibrateNotification == r) {
   2321             mVibrateNotification = null;
   2322             long identity = Binder.clearCallingIdentity();
   2323             try {
   2324                 mVibrator.cancel();
   2325             }
   2326             finally {
   2327                 Binder.restoreCallingIdentity(identity);
   2328             }
   2329         }
   2330 
   2331         // light
   2332         mLights.remove(r.getKey());
   2333         if (mLedNotification == r) {
   2334             mLedNotification = null;
   2335         }
   2336 
   2337         // Record usage stats
   2338         switch (reason) {
   2339             case REASON_DELEGATE_CANCEL:
   2340             case REASON_DELEGATE_CANCEL_ALL:
   2341             case REASON_LISTENER_CANCEL:
   2342             case REASON_LISTENER_CANCEL_ALL:
   2343                 mUsageStats.registerDismissedByUser(r);
   2344                 break;
   2345             case REASON_NOMAN_CANCEL:
   2346             case REASON_NOMAN_CANCEL_ALL:
   2347                 mUsageStats.registerRemovedByApp(r);
   2348                 break;
   2349             case REASON_DELEGATE_CLICK:
   2350                 mUsageStats.registerCancelDueToClick(r);
   2351                 break;
   2352             default:
   2353                 mUsageStats.registerCancelUnknown(r);
   2354                 break;
   2355         }
   2356 
   2357         mNotificationsByKey.remove(r.sbn.getKey());
   2358 
   2359         // Save it for users of getHistoricalNotifications()
   2360         mArchive.record(r.sbn);
   2361     }
   2362 
   2363     /**
   2364      * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
   2365      * and none of the {@code mustNotHaveFlags}.
   2366      */
   2367     void cancelNotification(final int callingUid, final int callingPid,
   2368             final String pkg, final String tag, final int id,
   2369             final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
   2370             final int userId, final int reason, final ManagedServiceInfo listener) {
   2371         // In enqueueNotificationInternal notifications are added by scheduling the
   2372         // work on the worker handler. Hence, we also schedule the cancel on this
   2373         // handler to avoid a scenario where an add notification call followed by a
   2374         // remove notification call ends up in not removing the notification.
   2375         mHandler.post(new Runnable() {
   2376             @Override
   2377             public void run() {
   2378                 String listenerName = listener == null ? null : listener.component.toShortString();
   2379                 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
   2380                         mustHaveFlags, mustNotHaveFlags, reason, listenerName);
   2381 
   2382                 synchronized (mNotificationList) {
   2383                     int index = indexOfNotificationLocked(pkg, tag, id, userId);
   2384                     if (index >= 0) {
   2385                         NotificationRecord r = mNotificationList.get(index);
   2386 
   2387                         // Ideally we'd do this in the caller of this method. However, that would
   2388                         // require the caller to also find the notification.
   2389                         if (reason == REASON_DELEGATE_CLICK) {
   2390                             mUsageStats.registerClickedByUser(r);
   2391                         }
   2392 
   2393                         if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
   2394                             return;
   2395                         }
   2396                         if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
   2397                             return;
   2398                         }
   2399 
   2400                         mNotificationList.remove(index);
   2401 
   2402                         cancelNotificationLocked(r, sendDelete, reason);
   2403                         cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName);
   2404                         updateLightsLocked();
   2405                     }
   2406                 }
   2407             }
   2408         });
   2409     }
   2410 
   2411     /**
   2412      * Determine whether the userId applies to the notification in question, either because
   2413      * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
   2414      */
   2415     private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
   2416         return
   2417                 // looking for USER_ALL notifications? match everything
   2418                    userId == UserHandle.USER_ALL
   2419                 // a notification sent to USER_ALL matches any query
   2420                 || r.getUserId() == UserHandle.USER_ALL
   2421                 // an exact user match
   2422                 || r.getUserId() == userId;
   2423     }
   2424 
   2425     /**
   2426      * Determine whether the userId applies to the notification in question, either because
   2427      * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
   2428      * because it matches one of the users profiles.
   2429      */
   2430     private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
   2431         return notificationMatchesUserId(r, userId)
   2432                 || mUserProfiles.isCurrentProfile(r.getUserId());
   2433     }
   2434 
   2435     /**
   2436      * Cancels all notifications from a given package that have all of the
   2437      * {@code mustHaveFlags}.
   2438      */
   2439     boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
   2440             int mustNotHaveFlags, boolean doit, int userId, int reason,
   2441             ManagedServiceInfo listener) {
   2442         String listenerName = listener == null ? null : listener.component.toShortString();
   2443         EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
   2444                 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
   2445                 listenerName);
   2446 
   2447         synchronized (mNotificationList) {
   2448             final int N = mNotificationList.size();
   2449             ArrayList<NotificationRecord> canceledNotifications = null;
   2450             for (int i = N-1; i >= 0; --i) {
   2451                 NotificationRecord r = mNotificationList.get(i);
   2452                 if (!notificationMatchesUserId(r, userId)) {
   2453                     continue;
   2454                 }
   2455                 // Don't remove notifications to all, if there's no package name specified
   2456                 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
   2457                     continue;
   2458                 }
   2459                 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
   2460                     continue;
   2461                 }
   2462                 if ((r.getFlags() & mustNotHaveFlags) != 0) {
   2463                     continue;
   2464                 }
   2465                 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
   2466                     continue;
   2467                 }
   2468                 if (canceledNotifications == null) {
   2469                     canceledNotifications = new ArrayList<>();
   2470                 }
   2471                 canceledNotifications.add(r);
   2472                 if (!doit) {
   2473                     return true;
   2474                 }
   2475                 mNotificationList.remove(i);
   2476                 cancelNotificationLocked(r, false, reason);
   2477             }
   2478             if (doit && canceledNotifications != null) {
   2479                 final int M = canceledNotifications.size();
   2480                 for (int i = 0; i < M; i++) {
   2481                     cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
   2482                             listenerName);
   2483                 }
   2484             }
   2485             if (canceledNotifications != null) {
   2486                 updateLightsLocked();
   2487             }
   2488             return canceledNotifications != null;
   2489         }
   2490     }
   2491 
   2492     void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
   2493             ManagedServiceInfo listener, boolean includeCurrentProfiles) {
   2494         String listenerName = listener == null ? null : listener.component.toShortString();
   2495         EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
   2496                 null, userId, 0, 0, reason, listenerName);
   2497 
   2498         ArrayList<NotificationRecord> canceledNotifications = null;
   2499         final int N = mNotificationList.size();
   2500         for (int i=N-1; i>=0; i--) {
   2501             NotificationRecord r = mNotificationList.get(i);
   2502             if (includeCurrentProfiles) {
   2503                 if (!notificationMatchesCurrentProfiles(r, userId)) {
   2504                     continue;
   2505                 }
   2506             } else {
   2507                 if (!notificationMatchesUserId(r, userId)) {
   2508                     continue;
   2509                 }
   2510             }
   2511 
   2512             if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
   2513                             | Notification.FLAG_NO_CLEAR)) == 0) {
   2514                 mNotificationList.remove(i);
   2515                 cancelNotificationLocked(r, true, reason);
   2516                 // Make a note so we can cancel children later.
   2517                 if (canceledNotifications == null) {
   2518                     canceledNotifications = new ArrayList<>();
   2519                 }
   2520                 canceledNotifications.add(r);
   2521             }
   2522         }
   2523         int M = canceledNotifications != null ? canceledNotifications.size() : 0;
   2524         for (int i = 0; i < M; i++) {
   2525             cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
   2526                     listenerName);
   2527         }
   2528         updateLightsLocked();
   2529     }
   2530 
   2531     // Warning: The caller is responsible for invoking updateLightsLocked().
   2532     private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
   2533             String listenerName) {
   2534         Notification n = r.getNotification();
   2535         if (!n.isGroupSummary()) {
   2536             return;
   2537         }
   2538 
   2539         String pkg = r.sbn.getPackageName();
   2540         int userId = r.getUserId();
   2541 
   2542         if (pkg == null) {
   2543             if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
   2544             return;
   2545         }
   2546 
   2547         final int N = mNotificationList.size();
   2548         for (int i = N - 1; i >= 0; i--) {
   2549             NotificationRecord childR = mNotificationList.get(i);
   2550             StatusBarNotification childSbn = childR.sbn;
   2551             if (childR.getNotification().isGroupChild() &&
   2552                     childR.getGroupKey().equals(r.getGroupKey())) {
   2553                 EventLogTags.writeNotificationCancel(callingUid, callingPid,
   2554                         pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0,
   2555                         REASON_GROUP_SUMMARY_CANCELED, listenerName);
   2556                 mNotificationList.remove(i);
   2557                 cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);
   2558             }
   2559         }
   2560     }
   2561 
   2562     // lock on mNotificationList
   2563     void updateLightsLocked()
   2564     {
   2565         // handle notification lights
   2566         if (mLedNotification == null) {
   2567             // get next notification, if any
   2568             int n = mLights.size();
   2569             if (n > 0) {
   2570                 mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
   2571             }
   2572         }
   2573 
   2574         // Don't flash while we are in a call or screen is on
   2575         if (mLedNotification == null || mInCall || mScreenOn) {
   2576             mNotificationLight.turnOff();
   2577             mStatusBar.notificationLightOff();
   2578         } else {
   2579             final Notification ledno = mLedNotification.sbn.getNotification();
   2580             int ledARGB = ledno.ledARGB;
   2581             int ledOnMS = ledno.ledOnMS;
   2582             int ledOffMS = ledno.ledOffMS;
   2583             if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
   2584                 ledARGB = mDefaultNotificationColor;
   2585                 ledOnMS = mDefaultNotificationLedOn;
   2586                 ledOffMS = mDefaultNotificationLedOff;
   2587             }
   2588             if (mNotificationPulseEnabled) {
   2589                 // pulse repeatedly
   2590                 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
   2591                         ledOnMS, ledOffMS);
   2592             }
   2593             // let SystemUI make an independent decision
   2594             mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
   2595         }
   2596     }
   2597 
   2598     // lock on mNotificationList
   2599     int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
   2600     {
   2601         ArrayList<NotificationRecord> list = mNotificationList;
   2602         final int len = list.size();
   2603         for (int i=0; i<len; i++) {
   2604             NotificationRecord r = list.get(i);
   2605             if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
   2606                 continue;
   2607             }
   2608             if (tag == null) {
   2609                 if (r.sbn.getTag() != null) {
   2610                     continue;
   2611                 }
   2612             } else {
   2613                 if (!tag.equals(r.sbn.getTag())) {
   2614                     continue;
   2615                 }
   2616             }
   2617             if (r.sbn.getPackageName().equals(pkg)) {
   2618                 return i;
   2619             }
   2620         }
   2621         return -1;
   2622     }
   2623 
   2624     // lock on mNotificationList
   2625     int indexOfNotificationLocked(String key) {
   2626         final int N = mNotificationList.size();
   2627         for (int i = 0; i < N; i++) {
   2628             if (key.equals(mNotificationList.get(i).getKey())) {
   2629                 return i;
   2630             }
   2631         }
   2632         return -1;
   2633     }
   2634 
   2635     private void updateNotificationPulse() {
   2636         synchronized (mNotificationList) {
   2637             updateLightsLocked();
   2638         }
   2639     }
   2640 
   2641     private static boolean isUidSystem(int uid) {
   2642         final int appid = UserHandle.getAppId(uid);
   2643         return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
   2644     }
   2645 
   2646     private static boolean isCallerSystem() {
   2647         return isUidSystem(Binder.getCallingUid());
   2648     }
   2649 
   2650     private static void checkCallerIsSystem() {
   2651         if (isCallerSystem()) {
   2652             return;
   2653         }
   2654         throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
   2655     }
   2656 
   2657     private static void checkCallerIsSystemOrSameApp(String pkg) {
   2658         if (isCallerSystem()) {
   2659             return;
   2660         }
   2661         final int uid = Binder.getCallingUid();
   2662         try {
   2663             ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
   2664                     pkg, 0, UserHandle.getCallingUserId());
   2665             if (ai == null) {
   2666                 throw new SecurityException("Unknown package " + pkg);
   2667             }
   2668             if (!UserHandle.isSameApp(ai.uid, uid)) {
   2669                 throw new SecurityException("Calling uid " + uid + " gave package"
   2670                         + pkg + " which is owned by uid " + ai.uid);
   2671             }
   2672         } catch (RemoteException re) {
   2673             throw new SecurityException("Unknown package " + pkg + "\n" + re);
   2674         }
   2675     }
   2676 
   2677     private static String callStateToString(int state) {
   2678         switch (state) {
   2679             case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
   2680             case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
   2681             case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
   2682             default: return "CALL_STATE_UNKNOWN_" + state;
   2683         }
   2684     }
   2685 
   2686     private void listenForCallState() {
   2687         TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
   2688             @Override
   2689             public void onCallStateChanged(int state, String incomingNumber) {
   2690                 if (mCallState == state) return;
   2691                 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
   2692                 mCallState = state;
   2693             }
   2694         }, PhoneStateListener.LISTEN_CALL_STATE);
   2695     }
   2696 
   2697     /**
   2698      * Generates a NotificationRankingUpdate from 'sbns', considering only
   2699      * notifications visible to the given listener.
   2700      *
   2701      * <p>Caller must hold a lock on mNotificationList.</p>
   2702      */
   2703     private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
   2704         int speedBumpIndex = -1;
   2705         final int N = mNotificationList.size();
   2706         ArrayList<String> keys = new ArrayList<String>(N);
   2707         ArrayList<String> interceptedKeys = new ArrayList<String>(N);
   2708         Bundle visibilityOverrides = new Bundle();
   2709         for (int i = 0; i < N; i++) {
   2710             NotificationRecord record = mNotificationList.get(i);
   2711             if (!isVisibleToListener(record.sbn, info)) {
   2712                 continue;
   2713             }
   2714             keys.add(record.sbn.getKey());
   2715             if (record.isIntercepted()) {
   2716                 interceptedKeys.add(record.sbn.getKey());
   2717             }
   2718             if (record.getPackageVisibilityOverride()
   2719                     != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
   2720                 visibilityOverrides.putInt(record.sbn.getKey(),
   2721                         record.getPackageVisibilityOverride());
   2722             }
   2723             // Find first min-prio notification for speedbump placement.
   2724             if (speedBumpIndex == -1 &&
   2725                     // Intrusiveness trumps priority, hence ignore intrusives.
   2726                     !record.isRecentlyIntrusive() &&
   2727                     // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
   2728                     // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
   2729                     // (or lower as a safeguard) is sufficient to find the speedbump index.
   2730                     // We'll have to revisit this when more package priority buckets are introduced.
   2731                     record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
   2732                     record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
   2733                 speedBumpIndex = keys.size() - 1;
   2734             }
   2735         }
   2736         String[] keysAr = keys.toArray(new String[keys.size()]);
   2737         String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
   2738         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
   2739                 speedBumpIndex);
   2740     }
   2741 
   2742     private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
   2743         if (!listener.enabledAndUserMatches(sbn.getUserId())) {
   2744             return false;
   2745         }
   2746         // TODO: remove this for older listeners.
   2747         return true;
   2748     }
   2749 
   2750     public class NotificationListeners extends ManagedServices {
   2751 
   2752         private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
   2753 
   2754         public NotificationListeners() {
   2755             super(getContext(), mHandler, mNotificationList, mUserProfiles);
   2756         }
   2757 
   2758         @Override
   2759         protected Config getConfig() {
   2760             Config c = new Config();
   2761             c.caption = "notification listener";
   2762             c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
   2763             c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
   2764             c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
   2765             c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
   2766             c.clientLabel = R.string.notification_listener_binding_label;
   2767             return c;
   2768         }
   2769 
   2770         @Override
   2771         protected IInterface asInterface(IBinder binder) {
   2772             return INotificationListener.Stub.asInterface(binder);
   2773         }
   2774 
   2775         @Override
   2776         public void onServiceAdded(ManagedServiceInfo info) {
   2777             final INotificationListener listener = (INotificationListener) info.service;
   2778             final NotificationRankingUpdate update;
   2779             synchronized (mNotificationList) {
   2780                 update = makeRankingUpdateLocked(info);
   2781             }
   2782             try {
   2783                 listener.onListenerConnected(update);
   2784             } catch (RemoteException e) {
   2785                 // we tried
   2786             }
   2787         }
   2788 
   2789         @Override
   2790         protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
   2791             if (mListenersDisablingEffects.remove(removed)) {
   2792                 updateListenerHintsLocked();
   2793             }
   2794             mLightTrimListeners.remove(removed);
   2795         }
   2796 
   2797         public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
   2798             if (trim == TRIM_LIGHT) {
   2799                 mLightTrimListeners.add(info);
   2800             } else {
   2801                 mLightTrimListeners.remove(info);
   2802             }
   2803         }
   2804 
   2805         public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
   2806             return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
   2807 
   2808         }
   2809 
   2810         /**
   2811          * asynchronously notify all listeners about a new notification
   2812          *
   2813          * <p>
   2814          * Also takes care of removing a notification that has been visible to a listener before,
   2815          * but isn't anymore.
   2816          */
   2817         public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
   2818             // Lazily initialized snapshots of the notification.
   2819             StatusBarNotification sbnClone = null;
   2820             StatusBarNotification sbnCloneLight = null;
   2821 
   2822             for (final ManagedServiceInfo info : mServices) {
   2823                 boolean sbnVisible = isVisibleToListener(sbn, info);
   2824                 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
   2825                 // This notification hasn't been and still isn't visible -> ignore.
   2826                 if (!oldSbnVisible && !sbnVisible) {
   2827                     continue;
   2828                 }
   2829                 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
   2830 
   2831                 // This notification became invisible -> remove the old one.
   2832                 if (oldSbnVisible && !sbnVisible) {
   2833                     final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
   2834                     mHandler.post(new Runnable() {
   2835                         @Override
   2836                         public void run() {
   2837                             notifyRemoved(info, oldSbnLightClone, update);
   2838                         }
   2839                     });
   2840                     continue;
   2841                 }
   2842 
   2843                 final int trim = mListeners.getOnNotificationPostedTrim(info);
   2844 
   2845                 if (trim == TRIM_LIGHT && sbnCloneLight == null) {
   2846                     sbnCloneLight = sbn.cloneLight();
   2847                 } else if (trim == TRIM_FULL && sbnClone == null) {
   2848                     sbnClone = sbn.clone();
   2849                 }
   2850                 final StatusBarNotification sbnToPost =
   2851                         (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
   2852 
   2853                 mHandler.post(new Runnable() {
   2854                     @Override
   2855                     public void run() {
   2856                         notifyPosted(info, sbnToPost, update);
   2857                     }
   2858                 });
   2859             }
   2860         }
   2861 
   2862         /**
   2863          * asynchronously notify all listeners about a removed notification
   2864          */
   2865         public void notifyRemovedLocked(StatusBarNotification sbn) {
   2866             // make a copy in case changes are made to the underlying Notification object
   2867             // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
   2868             // notification
   2869             final StatusBarNotification sbnLight = sbn.cloneLight();
   2870             for (final ManagedServiceInfo info : mServices) {
   2871                 if (!isVisibleToListener(sbn, info)) {
   2872                     continue;
   2873                 }
   2874                 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
   2875                 mHandler.post(new Runnable() {
   2876                     @Override
   2877                     public void run() {
   2878                         notifyRemoved(info, sbnLight, update);
   2879                     }
   2880                 });
   2881             }
   2882         }
   2883 
   2884         /**
   2885          * asynchronously notify all listeners about a reordering of notifications
   2886          */
   2887         public void notifyRankingUpdateLocked() {
   2888             for (final ManagedServiceInfo serviceInfo : mServices) {
   2889                 if (!serviceInfo.isEnabledForCurrentProfiles()) {
   2890                     continue;
   2891                 }
   2892                 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
   2893                 mHandler.post(new Runnable() {
   2894                     @Override
   2895                     public void run() {
   2896                         notifyRankingUpdate(serviceInfo, update);
   2897                     }
   2898                 });
   2899             }
   2900         }
   2901 
   2902         public void notifyListenerHintsChangedLocked(final int hints) {
   2903             for (final ManagedServiceInfo serviceInfo : mServices) {
   2904                 if (!serviceInfo.isEnabledForCurrentProfiles()) {
   2905                     continue;
   2906                 }
   2907                 mHandler.post(new Runnable() {
   2908                     @Override
   2909                     public void run() {
   2910                         notifyListenerHintsChanged(serviceInfo, hints);
   2911                     }
   2912                 });
   2913             }
   2914         }
   2915 
   2916         public void notifyInterruptionFilterChanged(final int interruptionFilter) {
   2917             for (final ManagedServiceInfo serviceInfo : mServices) {
   2918                 if (!serviceInfo.isEnabledForCurrentProfiles()) {
   2919                     continue;
   2920                 }
   2921                 mHandler.post(new Runnable() {
   2922                     @Override
   2923                     public void run() {
   2924                         notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
   2925                     }
   2926                 });
   2927             }
   2928         }
   2929 
   2930         private void notifyPosted(final ManagedServiceInfo info,
   2931                 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
   2932             final INotificationListener listener = (INotificationListener)info.service;
   2933             StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
   2934             try {
   2935                 listener.onNotificationPosted(sbnHolder, rankingUpdate);
   2936             } catch (RemoteException ex) {
   2937                 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
   2938             }
   2939         }
   2940 
   2941         private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
   2942                 NotificationRankingUpdate rankingUpdate) {
   2943             if (!info.enabledAndUserMatches(sbn.getUserId())) {
   2944                 return;
   2945             }
   2946             final INotificationListener listener = (INotificationListener) info.service;
   2947             StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
   2948             try {
   2949                 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
   2950             } catch (RemoteException ex) {
   2951                 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
   2952             }
   2953         }
   2954 
   2955         private void notifyRankingUpdate(ManagedServiceInfo info,
   2956                                          NotificationRankingUpdate rankingUpdate) {
   2957             final INotificationListener listener = (INotificationListener) info.service;
   2958             try {
   2959                 listener.onNotificationRankingUpdate(rankingUpdate);
   2960             } catch (RemoteException ex) {
   2961                 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
   2962             }
   2963         }
   2964 
   2965         private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
   2966             final INotificationListener listener = (INotificationListener) info.service;
   2967             try {
   2968                 listener.onListenerHintsChanged(hints);
   2969             } catch (RemoteException ex) {
   2970                 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
   2971             }
   2972         }
   2973 
   2974         private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
   2975                 int interruptionFilter) {
   2976             final INotificationListener listener = (INotificationListener) info.service;
   2977             try {
   2978                 listener.onInterruptionFilterChanged(interruptionFilter);
   2979             } catch (RemoteException ex) {
   2980                 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
   2981             }
   2982         }
   2983 
   2984         private boolean isListenerPackage(String packageName) {
   2985             if (packageName == null) {
   2986                 return false;
   2987             }
   2988             // TODO: clean up locking object later
   2989             synchronized (mNotificationList) {
   2990                 for (final ManagedServiceInfo serviceInfo : mServices) {
   2991                     if (packageName.equals(serviceInfo.component.getPackageName())) {
   2992                         return true;
   2993                     }
   2994                 }
   2995             }
   2996             return false;
   2997         }
   2998     }
   2999 
   3000     public static final class DumpFilter {
   3001         public String pkgFilter;
   3002         public boolean zen;
   3003 
   3004         public static DumpFilter parseFromArguments(String[] args) {
   3005             if (args != null && args.length == 2 && "p".equals(args[0])
   3006                     && args[1] != null && !args[1].trim().isEmpty()) {
   3007                 final DumpFilter filter = new DumpFilter();
   3008                 filter.pkgFilter = args[1].trim().toLowerCase();
   3009                 return filter;
   3010             }
   3011             if (args != null && args.length == 1 && "zen".equals(args[0])) {
   3012                 final DumpFilter filter = new DumpFilter();
   3013                 filter.zen = true;
   3014                 return filter;
   3015             }
   3016             return null;
   3017         }
   3018 
   3019         public boolean matches(StatusBarNotification sbn) {
   3020             return zen ? true : sbn != null
   3021                     && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
   3022         }
   3023 
   3024         public boolean matches(ComponentName component) {
   3025             return zen ? true : component != null && matches(component.getPackageName());
   3026         }
   3027 
   3028         public boolean matches(String pkg) {
   3029             return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
   3030         }
   3031 
   3032         @Override
   3033         public String toString() {
   3034             return zen ? "zen" : ('\'' + pkgFilter + '\'');
   3035         }
   3036     }
   3037 
   3038     /**
   3039      * Wrapper for a StatusBarNotification object that allows transfer across a oneway
   3040      * binder without sending large amounts of data over a oneway transaction.
   3041      */
   3042     private static final class StatusBarNotificationHolder
   3043             extends IStatusBarNotificationHolder.Stub {
   3044         private StatusBarNotification mValue;
   3045 
   3046         public StatusBarNotificationHolder(StatusBarNotification value) {
   3047             mValue = value;
   3048         }
   3049 
   3050         /** Get the held value and clear it. This function should only be called once per holder */
   3051         @Override
   3052         public StatusBarNotification get() {
   3053             StatusBarNotification value = mValue;
   3054             mValue = null;
   3055             return value;
   3056         }
   3057     }
   3058 }
   3059