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