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