NotificationManagerService.java revision bd6937841983230bd65dc08548367caa7d409238
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        /**
1130         * System-only API for getting a list of current (i.e. not cleared) notifications.
1131         *
1132         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1133         * @returns A list of all the notifications, in natural order.
1134         */
1135        @Override
1136        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1137            // enforce() will ensure the calling uid has the correct permission
1138            getContext().enforceCallingOrSelfPermission(
1139                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1140                    "NotificationManagerService.getActiveNotifications");
1141
1142            StatusBarNotification[] tmp = null;
1143            int uid = Binder.getCallingUid();
1144
1145            // noteOp will check to make sure the callingPkg matches the uid
1146            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1147                    == AppOpsManager.MODE_ALLOWED) {
1148                synchronized (mNotificationList) {
1149                    tmp = new StatusBarNotification[mNotificationList.size()];
1150                    final int N = mNotificationList.size();
1151                    for (int i=0; i<N; i++) {
1152                        tmp[i] = mNotificationList.get(i).sbn;
1153                    }
1154                }
1155            }
1156            return tmp;
1157        }
1158
1159        /**
1160         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1161         *
1162         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1163         */
1164        @Override
1165        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1166            // enforce() will ensure the calling uid has the correct permission
1167            getContext().enforceCallingOrSelfPermission(
1168                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1169                    "NotificationManagerService.getHistoricalNotifications");
1170
1171            StatusBarNotification[] tmp = null;
1172            int uid = Binder.getCallingUid();
1173
1174            // noteOp will check to make sure the callingPkg matches the uid
1175            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1176                    == AppOpsManager.MODE_ALLOWED) {
1177                synchronized (mArchive) {
1178                    tmp = mArchive.getArray(count);
1179                }
1180            }
1181            return tmp;
1182        }
1183
1184        /**
1185         * Register a listener binder directly with the notification manager.
1186         *
1187         * Only works with system callers. Apps should extend
1188         * {@link android.service.notification.NotificationListenerService}.
1189         */
1190        @Override
1191        public void registerListener(final INotificationListener listener,
1192                final ComponentName component, final int userid) {
1193            enforceSystemOrSystemUI("INotificationManager.registerListener");
1194            mListeners.registerService(listener, component, userid);
1195        }
1196
1197        /**
1198         * Remove a listener binder directly
1199         */
1200        @Override
1201        public void unregisterListener(INotificationListener listener, int userid) {
1202            mListeners.unregisterService(listener, userid);
1203        }
1204
1205        /**
1206         * Allow an INotificationListener to simulate a "clear all" operation.
1207         *
1208         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1209         *
1210         * @param token The binder for the listener, to check that the caller is allowed
1211         */
1212        @Override
1213        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1214            final int callingUid = Binder.getCallingUid();
1215            final int callingPid = Binder.getCallingPid();
1216            long identity = Binder.clearCallingIdentity();
1217            try {
1218                synchronized (mNotificationList) {
1219                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1220                    if (keys != null) {
1221                        final int N = keys.length;
1222                        for (int i = 0; i < N; i++) {
1223                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1224                            final int userId = r.sbn.getUserId();
1225                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
1226                                    !mUserProfiles.isCurrentProfile(userId)) {
1227                                throw new SecurityException("Disallowed call from listener: "
1228                                        + info.service);
1229                            }
1230                            if (r != null) {
1231                                cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1232                                        r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1233                                        userId);
1234                            }
1235                        }
1236                    } else {
1237                        cancelAllLocked(callingUid, callingPid, info.userid,
1238                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1239                    }
1240                }
1241            } finally {
1242                Binder.restoreCallingIdentity(identity);
1243            }
1244        }
1245
1246        private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1247                int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1248            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1249                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1250                    true,
1251                    userId, REASON_LISTENER_CANCEL, info);
1252        }
1253
1254        /**
1255         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1256         *
1257         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1258         *
1259         * @param token The binder for the listener, to check that the caller is allowed
1260         */
1261        @Override
1262        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1263                String tag, int id) {
1264            final int callingUid = Binder.getCallingUid();
1265            final int callingPid = Binder.getCallingPid();
1266            long identity = Binder.clearCallingIdentity();
1267            try {
1268                synchronized (mNotificationList) {
1269                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1270                    if (info.supportsProfiles()) {
1271                        Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1272                                + "from " + info.component
1273                                + " use cancelNotification(key) instead.");
1274                    } else {
1275                        cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1276                                pkg, tag, id, info.userid);
1277                    }
1278                }
1279            } finally {
1280                Binder.restoreCallingIdentity(identity);
1281            }
1282        }
1283
1284        /**
1285         * Allow an INotificationListener to request the list of outstanding notifications seen by
1286         * the current user. Useful when starting up, after which point the listener callbacks
1287         * should be used.
1288         *
1289         * @param token The binder for the listener, to check that the caller is allowed
1290         * @param keys An array of notification keys to fetch, or null to fetch everything
1291         * @returns The return value will contain the notifications specified in keys, in that
1292         *      order, or if keys is null, all the notifications, in natural order.
1293         */
1294        @Override
1295        public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1296                INotificationListener token, String[] keys, int trim) {
1297            synchronized (mNotificationList) {
1298                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1299                final boolean getKeys = keys != null;
1300                final int N = getKeys ? keys.length : mNotificationList.size();
1301                final ArrayList<StatusBarNotification> list
1302                        = new ArrayList<StatusBarNotification>(N);
1303                for (int i=0; i<N; i++) {
1304                    final NotificationRecord r = getKeys
1305                            ? mNotificationsByKey.get(keys[i])
1306                            : mNotificationList.get(i);
1307                    if (r == null) continue;
1308                    StatusBarNotification sbn = r.sbn;
1309                    if (!isVisibleToListener(sbn, info)) continue;
1310                    StatusBarNotification sbnToSend =
1311                            (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1312                    list.add(sbnToSend);
1313                }
1314                return new ParceledListSlice<StatusBarNotification>(list);
1315            }
1316        }
1317
1318        @Override
1319        public void requestHintsFromListener(INotificationListener token, int hints) {
1320            final long identity = Binder.clearCallingIdentity();
1321            try {
1322                synchronized (mNotificationList) {
1323                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1324                    final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
1325                    if (disableEffects) {
1326                        mListenersDisablingEffects.add(info);
1327                    } else {
1328                        mListenersDisablingEffects.remove(info);
1329                    }
1330                    updateListenerHintsLocked();
1331                    updateEffectsSuppressorLocked();
1332                }
1333            } finally {
1334                Binder.restoreCallingIdentity(identity);
1335            }
1336        }
1337
1338        @Override
1339        public int getHintsFromListener(INotificationListener token) {
1340            synchronized (mNotificationList) {
1341                return mListenerHints;
1342            }
1343        }
1344
1345        @Override
1346        public void requestInterruptionFilterFromListener(INotificationListener token,
1347                int interruptionFilter) throws RemoteException {
1348            final long identity = Binder.clearCallingIdentity();
1349            try {
1350                synchronized (mNotificationList) {
1351                    mListeners.checkServiceTokenLocked(token);
1352                    mZenModeHelper.requestFromListener(interruptionFilter);
1353                    updateInterruptionFilterLocked();
1354                }
1355            } finally {
1356                Binder.restoreCallingIdentity(identity);
1357            }
1358        }
1359
1360        @Override
1361        public int getInterruptionFilterFromListener(INotificationListener token)
1362                throws RemoteException {
1363            synchronized (mNotificationLight) {
1364                return mInterruptionFilter;
1365            }
1366        }
1367
1368        @Override
1369        public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1370                throws RemoteException {
1371            synchronized (mNotificationList) {
1372                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1373                if (info == null) return;
1374                mListeners.setOnNotificationPostedTrimLocked(info, trim);
1375            }
1376        }
1377
1378        @Override
1379        public ZenModeConfig getZenModeConfig() {
1380            enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
1381            return mZenModeHelper.getConfig();
1382        }
1383
1384        @Override
1385        public boolean setZenModeConfig(ZenModeConfig config) {
1386            checkCallerIsSystem();
1387            return mZenModeHelper.setConfig(config);
1388        }
1389
1390        @Override
1391        public void notifyConditions(String pkg, IConditionProvider provider,
1392                Condition[] conditions) {
1393            final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1394            checkCallerIsSystemOrSameApp(pkg);
1395            final long identity = Binder.clearCallingIdentity();
1396            try {
1397                mConditionProviders.notifyConditions(pkg, info, conditions);
1398            } finally {
1399                Binder.restoreCallingIdentity(identity);
1400            }
1401        }
1402
1403        @Override
1404        public void requestZenModeConditions(IConditionListener callback, int relevance) {
1405            enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
1406            mConditionProviders.requestZenModeConditions(callback, relevance);
1407        }
1408
1409        @Override
1410        public void setZenModeCondition(Condition condition) {
1411            enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
1412            final long identity = Binder.clearCallingIdentity();
1413            try {
1414                mConditionProviders.setZenModeCondition(condition, "binderCall");
1415            } finally {
1416                Binder.restoreCallingIdentity(identity);
1417            }
1418        }
1419
1420        @Override
1421        public void setAutomaticZenModeConditions(Uri[] conditionIds) {
1422            enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions");
1423            mConditionProviders.setAutomaticZenModeConditions(conditionIds);
1424        }
1425
1426        @Override
1427        public Condition[] getAutomaticZenModeConditions() {
1428            enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions");
1429            return mConditionProviders.getAutomaticZenModeConditions();
1430        }
1431
1432        private void enforceSystemOrSystemUI(String message) {
1433            if (isCallerSystem()) return;
1434            getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1435                    message);
1436        }
1437
1438        @Override
1439        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1440            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1441                    != PackageManager.PERMISSION_GRANTED) {
1442                pw.println("Permission Denial: can't dump NotificationManager from pid="
1443                        + Binder.getCallingPid()
1444                        + ", uid=" + Binder.getCallingUid());
1445                return;
1446            }
1447
1448            dumpImpl(pw, DumpFilter.parseFromArguments(args));
1449        }
1450
1451        @Override
1452        public ComponentName getEffectsSuppressor() {
1453            enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor");
1454            return mEffectsSuppressor;
1455        }
1456
1457        @Override
1458        public boolean matchesCallFilter(Bundle extras) {
1459            enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
1460            return mZenModeHelper.matchesCallFilter(extras,
1461                    mRankingHelper.findExtractor(ValidateNotificationPeople.class));
1462        }
1463    };
1464
1465    private String[] getActiveNotificationKeys(INotificationListener token) {
1466        final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1467        final ArrayList<String> keys = new ArrayList<String>();
1468        if (info.isEnabledForCurrentProfiles()) {
1469            synchronized (mNotificationList) {
1470                final int N = mNotificationList.size();
1471                for (int i = 0; i < N; i++) {
1472                    final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1473                    if (info.enabledAndUserMatches(sbn.getUserId())) {
1474                        keys.add(sbn.getKey());
1475                    }
1476                }
1477            }
1478        }
1479        return keys.toArray(new String[keys.size()]);
1480    }
1481
1482    private boolean disableNotificationEffects() {
1483        return mDisableNotificationEffects || (mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0;
1484    }
1485
1486    void dumpImpl(PrintWriter pw, DumpFilter filter) {
1487        pw.print("Current Notification Manager state");
1488        if (filter != null) {
1489            pw.print(" (filtered to "); pw.print(filter); pw.print(")");
1490        }
1491        pw.println(':');
1492        int N;
1493        final boolean zenOnly = filter != null && filter.zen;
1494
1495        if (!zenOnly) {
1496            synchronized (mToastQueue) {
1497                N = mToastQueue.size();
1498                if (N > 0) {
1499                    pw.println("  Toast Queue:");
1500                    for (int i=0; i<N; i++) {
1501                        mToastQueue.get(i).dump(pw, "    ", filter);
1502                    }
1503                    pw.println("  ");
1504                }
1505            }
1506        }
1507
1508        synchronized (mNotificationList) {
1509            if (!zenOnly) {
1510                N = mNotificationList.size();
1511                if (N > 0) {
1512                    pw.println("  Notification List:");
1513                    for (int i=0; i<N; i++) {
1514                        final NotificationRecord nr = mNotificationList.get(i);
1515                        if (filter != null && !filter.matches(nr.sbn)) continue;
1516                        nr.dump(pw, "    ", getContext());
1517                    }
1518                    pw.println("  ");
1519                }
1520
1521                if (filter == null) {
1522                    N = mLights.size();
1523                    if (N > 0) {
1524                        pw.println("  Lights List:");
1525                        for (int i=0; i<N; i++) {
1526                            pw.println("    " + mLights.get(i));
1527                        }
1528                        pw.println("  ");
1529                    }
1530                    pw.println("  mUseAttentionLight=" + mUseAttentionLight);
1531                    pw.println("  mNotificationPulseEnabled=" + mNotificationPulseEnabled);
1532                    pw.println("  mSoundNotification=" + mSoundNotification);
1533                    pw.println("  mVibrateNotification=" + mVibrateNotification);
1534                    pw.println("  mDisableNotificationEffects=" + mDisableNotificationEffects);
1535                    pw.println("  mSystemReady=" + mSystemReady);
1536                }
1537                pw.println("  mArchive=" + mArchive.toString());
1538                Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1539                int i=0;
1540                while (iter.hasNext()) {
1541                    final StatusBarNotification sbn = iter.next();
1542                    if (filter != null && !filter.matches(sbn)) continue;
1543                    pw.println("    " + sbn);
1544                    if (++i >= 5) {
1545                        if (iter.hasNext()) pw.println("    ...");
1546                        break;
1547                    }
1548                }
1549            }
1550
1551            if (!zenOnly) {
1552                pw.println("\n  Usage Stats:");
1553                mUsageStats.dump(pw, "    ", filter);
1554            }
1555
1556            if (filter == null || zenOnly) {
1557                pw.println("\n  Zen Mode:");
1558                mZenModeHelper.dump(pw, "    ");
1559
1560                pw.println("\n  Zen Log:");
1561                ZenLog.dump(pw, "    ");
1562            }
1563
1564            if (!zenOnly) {
1565                pw.println("\n  Ranking Config:");
1566                mRankingHelper.dump(pw, "    ", filter);
1567
1568                pw.println("\n  Notification listeners:");
1569                mListeners.dump(pw, filter);
1570                pw.print("    mListenerHints: "); pw.println(mListenerHints);
1571                pw.print("    mListenersDisablingEffects: (");
1572                N = mListenersDisablingEffects.size();
1573                for (int i = 0; i < N; i++) {
1574                    final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
1575                    if (i > 0) pw.print(',');
1576                    pw.print(listener.component);
1577                }
1578                pw.println(')');
1579            }
1580
1581            pw.println("\n  Condition providers:");
1582            mConditionProviders.dump(pw, filter);
1583        }
1584    }
1585
1586    /**
1587     * The private API only accessible to the system process.
1588     */
1589    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1590        @Override
1591        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
1592                String tag, int id, Notification notification, int[] idReceived, int userId) {
1593            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
1594                    idReceived, userId);
1595        }
1596    };
1597
1598    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
1599            final int callingPid, final String tag, final int id, final Notification notification,
1600            int[] idOut, int incomingUserId) {
1601        if (DBG) {
1602            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1603                    + " notification=" + notification);
1604        }
1605        checkCallerIsSystemOrSameApp(pkg);
1606        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
1607        final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
1608
1609        final int userId = ActivityManager.handleIncomingUser(callingPid,
1610                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1611        final UserHandle user = new UserHandle(userId);
1612
1613        // Limit the number of notifications that any given package except the android
1614        // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
1615        if (!isSystemNotification && !isNotificationFromListener) {
1616            synchronized (mNotificationList) {
1617                int count = 0;
1618                final int N = mNotificationList.size();
1619                for (int i=0; i<N; i++) {
1620                    final NotificationRecord r = mNotificationList.get(i);
1621                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1622                        count++;
1623                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1624                            Slog.e(TAG, "Package has already posted " + count
1625                                    + " notifications.  Not showing more.  package=" + pkg);
1626                            return;
1627                        }
1628                    }
1629                }
1630            }
1631        }
1632
1633        // This conditional is a dirty hack to limit the logging done on
1634        //     behalf of the download manager without affecting other apps.
1635        if (!pkg.equals("com.android.providers.downloads")
1636                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1637            EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1638                    pkg, id, tag, userId, notification.toString());
1639        }
1640
1641        if (pkg == null || notification == null) {
1642            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1643                    + " id=" + id + " notification=" + notification);
1644        }
1645        if (notification.icon != 0) {
1646            if (!notification.isValid()) {
1647                throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
1648                        + " id=" + id + " notification=" + notification);
1649            }
1650        }
1651
1652        mHandler.post(new Runnable() {
1653            @Override
1654            public void run() {
1655
1656                synchronized (mNotificationList) {
1657
1658                    // === Scoring ===
1659
1660                    // 0. Sanitize inputs
1661                    notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1662                            Notification.PRIORITY_MAX);
1663                    // Migrate notification flags to scores
1664                    if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1665                        if (notification.priority < Notification.PRIORITY_MAX) {
1666                            notification.priority = Notification.PRIORITY_MAX;
1667                        }
1668                    } else if (SCORE_ONGOING_HIGHER &&
1669                            0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1670                        if (notification.priority < Notification.PRIORITY_HIGH) {
1671                            notification.priority = Notification.PRIORITY_HIGH;
1672                        }
1673                    }
1674
1675                    // 1. initial score: buckets of 10, around the app [-20..20]
1676                    final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
1677
1678                    // 2. extract ranking signals from the notification data
1679                    final StatusBarNotification n = new StatusBarNotification(
1680                            pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1681                            user);
1682                    NotificationRecord r = new NotificationRecord(n, score);
1683                    NotificationRecord old = mNotificationsByKey.get(n.getKey());
1684                    if (old != null) {
1685                        // Retain ranking information from previous record
1686                        r.copyRankingInformation(old);
1687                    }
1688                    mRankingHelper.extractSignals(r);
1689
1690                    // 3. Apply local rules
1691
1692                    // blocked apps
1693                    if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1694                        if (!isSystemNotification) {
1695                            r.score = JUNK_SCORE;
1696                            Slog.e(TAG, "Suppressing notification from package " + pkg
1697                                    + " by user request.");
1698                        }
1699                    }
1700
1701                    if (r.score < SCORE_DISPLAY_THRESHOLD) {
1702                        // Notification will be blocked because the score is too low.
1703                        return;
1704                    }
1705
1706                    // Clear out group children of the old notification if the update causes the
1707                    // group summary to go away. This happens when the old notification was a
1708                    // summary and the new one isn't, or when the old notification was a summary
1709                    // and its group key changed.
1710                    if (old != null && old.getNotification().isGroupSummary() &&
1711                            (!notification.isGroupSummary() ||
1712                                    !old.getGroupKey().equals(r.getGroupKey()))) {
1713                        cancelGroupChildrenLocked(old, callingUid, callingPid, null);
1714                    }
1715
1716                    int index = indexOfNotificationLocked(n.getKey());
1717                    if (index < 0) {
1718                        mNotificationList.add(r);
1719                        mUsageStats.registerPostedByApp(r);
1720                    } else {
1721                        old = mNotificationList.get(index);
1722                        mNotificationList.set(index, r);
1723                        mUsageStats.registerUpdatedByApp(r, old);
1724                        // Make sure we don't lose the foreground service state.
1725                        notification.flags |=
1726                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1727                        r.isUpdate = true;
1728                    }
1729
1730                    mNotificationsByKey.put(n.getKey(), r);
1731
1732                    // Ensure if this is a foreground service that the proper additional
1733                    // flags are set.
1734                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1735                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1736                                | Notification.FLAG_NO_CLEAR;
1737                    }
1738
1739                    applyZenModeLocked(r);
1740                    mRankingHelper.sort(mNotificationList);
1741
1742                    if (notification.icon != 0) {
1743                        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
1744                        mListeners.notifyPostedLocked(n, oldSbn);
1745                    } else {
1746                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1747                        if (old != null && !old.isCanceled) {
1748                            mListeners.notifyRemovedLocked(n);
1749                        }
1750                        // ATTENTION: in a future release we will bail out here
1751                        // so that we do not play sounds, show lights, etc. for invalid
1752                        // notifications
1753                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1754                                + n.getPackageName());
1755                    }
1756
1757                    buzzBeepBlinkLocked(r);
1758                }
1759            }
1760        });
1761
1762        idOut[0] = id;
1763    }
1764
1765    private void buzzBeepBlinkLocked(NotificationRecord record) {
1766        boolean buzzBeepBlinked = false;
1767        final Notification notification = record.sbn.getNotification();
1768
1769        // Should this notification make noise, vibe, or use the LED?
1770        final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) &&
1771                !record.isIntercepted();
1772        if (DBG || record.isIntercepted())
1773            Slog.v(TAG,
1774                    "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
1775                            " intercept=" + record.isIntercepted()
1776            );
1777
1778        final int currentUser;
1779        final long token = Binder.clearCallingIdentity();
1780        try {
1781            currentUser = ActivityManager.getCurrentUser();
1782        } finally {
1783            Binder.restoreCallingIdentity(token);
1784        }
1785
1786        // If we're not supposed to beep, vibrate, etc. then don't.
1787        if (!disableNotificationEffects()
1788                && (!(record.isUpdate
1789                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1790                && (record.getUserId() == UserHandle.USER_ALL ||
1791                    record.getUserId() == currentUser ||
1792                    mUserProfiles.isCurrentProfile(record.getUserId()))
1793                && canInterrupt
1794                && mSystemReady
1795                && mAudioManager != null) {
1796            if (DBG) Slog.v(TAG, "Interrupting!");
1797
1798            sendAccessibilityEvent(notification, record.sbn.getPackageName());
1799
1800            // sound
1801
1802            // should we use the default notification sound? (indicated either by
1803            // DEFAULT_SOUND or because notification.sound is pointing at
1804            // Settings.System.NOTIFICATION_SOUND)
1805            final boolean useDefaultSound =
1806                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1807                           Settings.System.DEFAULT_NOTIFICATION_URI
1808                                   .equals(notification.sound);
1809
1810            Uri soundUri = null;
1811            boolean hasValidSound = false;
1812
1813            if (useDefaultSound) {
1814                soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1815
1816                // check to see if the default notification sound is silent
1817                ContentResolver resolver = getContext().getContentResolver();
1818                hasValidSound = Settings.System.getString(resolver,
1819                       Settings.System.NOTIFICATION_SOUND) != null;
1820            } else if (notification.sound != null) {
1821                soundUri = notification.sound;
1822                hasValidSound = (soundUri != null);
1823            }
1824
1825            if (hasValidSound) {
1826                boolean looping =
1827                        (notification.flags & Notification.FLAG_INSISTENT) != 0;
1828                AudioAttributes audioAttributes;
1829                if (notification.audioAttributes != null) {
1830                    audioAttributes = notification.audioAttributes;
1831                } else {
1832                    audioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
1833                }
1834                mSoundNotification = record;
1835                // do not play notifications if stream volume is 0 (typically because
1836                // ringer mode is silent) or if there is a user of exclusive audio focus
1837                if ((mAudioManager.getStreamVolume(
1838                        AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
1839                            && !mAudioManager.isAudioFocusExclusive()) {
1840                    final long identity = Binder.clearCallingIdentity();
1841                    try {
1842                        final IRingtonePlayer player =
1843                                mAudioManager.getRingtonePlayer();
1844                        if (player != null) {
1845                            if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1846                                    + " with attributes " + audioAttributes);
1847                            player.playAsync(soundUri, record.sbn.getUser(), looping,
1848                                    audioAttributes);
1849                            buzzBeepBlinked = true;
1850                        }
1851                    } catch (RemoteException e) {
1852                    } finally {
1853                        Binder.restoreCallingIdentity(identity);
1854                    }
1855                }
1856            }
1857
1858            // vibrate
1859            // Does the notification want to specify its own vibration?
1860            final boolean hasCustomVibrate = notification.vibrate != null;
1861
1862            // new in 4.2: if there was supposed to be a sound and we're in vibrate
1863            // mode, and no other vibration is specified, we fall back to vibration
1864            final boolean convertSoundToVibration =
1865                       !hasCustomVibrate
1866                    && hasValidSound
1867                    && (mAudioManager.getRingerMode()
1868                               == AudioManager.RINGER_MODE_VIBRATE);
1869
1870            // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1871            final boolean useDefaultVibrate =
1872                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1873
1874            if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1875                    && !(mAudioManager.getRingerMode()
1876                            == AudioManager.RINGER_MODE_SILENT)) {
1877                mVibrateNotification = record;
1878
1879                if (useDefaultVibrate || convertSoundToVibration) {
1880                    // Escalate privileges so we can use the vibrator even if the
1881                    // notifying app does not have the VIBRATE permission.
1882                    long identity = Binder.clearCallingIdentity();
1883                    try {
1884                        mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1885                            useDefaultVibrate ? mDefaultVibrationPattern
1886                                : mFallbackVibrationPattern,
1887                            ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1888                                ? 0: -1, audioAttributesForNotification(notification));
1889                        buzzBeepBlinked = true;
1890                    } finally {
1891                        Binder.restoreCallingIdentity(identity);
1892                    }
1893                } else if (notification.vibrate.length > 1) {
1894                    // If you want your own vibration pattern, you need the VIBRATE
1895                    // permission
1896                    mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1897                            notification.vibrate,
1898                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1899                                ? 0: -1, audioAttributesForNotification(notification));
1900                    buzzBeepBlinked = true;
1901                }
1902            }
1903        }
1904
1905        // light
1906        // release the light
1907        boolean wasShowLights = mLights.remove(record.getKey());
1908        if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) {
1909            mLedNotification = null;
1910        }
1911        if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) {
1912            mLights.add(record.getKey());
1913            updateLightsLocked();
1914            if (mUseAttentionLight) {
1915                mAttentionLight.pulse();
1916            }
1917            buzzBeepBlinked = true;
1918        } else if (wasShowLights) {
1919            updateLightsLocked();
1920        }
1921        if (buzzBeepBlinked) {
1922            mHandler.post(mBuzzBeepBlinked);
1923        }
1924    }
1925
1926    private static AudioAttributes audioAttributesForNotification(Notification n) {
1927        if (n.audioAttributes != null
1928                && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
1929            return n.audioAttributes;
1930        }
1931        return new AudioAttributes.Builder()
1932                .setLegacyStreamType(n.audioStreamType)
1933                .setUsage(AudioAttributes.usageForLegacyStreamType(n.audioStreamType))
1934                .build();
1935    }
1936
1937    void showNextToastLocked() {
1938        ToastRecord record = mToastQueue.get(0);
1939        while (record != null) {
1940            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1941            try {
1942                record.callback.show();
1943                scheduleTimeoutLocked(record);
1944                return;
1945            } catch (RemoteException e) {
1946                Slog.w(TAG, "Object died trying to show notification " + record.callback
1947                        + " in package " + record.pkg);
1948                // remove it from the list and let the process die
1949                int index = mToastQueue.indexOf(record);
1950                if (index >= 0) {
1951                    mToastQueue.remove(index);
1952                }
1953                keepProcessAliveLocked(record.pid);
1954                if (mToastQueue.size() > 0) {
1955                    record = mToastQueue.get(0);
1956                } else {
1957                    record = null;
1958                }
1959            }
1960        }
1961    }
1962
1963    void cancelToastLocked(int index) {
1964        ToastRecord record = mToastQueue.get(index);
1965        try {
1966            record.callback.hide();
1967        } catch (RemoteException e) {
1968            Slog.w(TAG, "Object died trying to hide notification " + record.callback
1969                    + " in package " + record.pkg);
1970            // don't worry about this, we're about to remove it from
1971            // the list anyway
1972        }
1973        mToastQueue.remove(index);
1974        keepProcessAliveLocked(record.pid);
1975        if (mToastQueue.size() > 0) {
1976            // Show the next one. If the callback fails, this will remove
1977            // it from the list, so don't assume that the list hasn't changed
1978            // after this point.
1979            showNextToastLocked();
1980        }
1981    }
1982
1983    private void scheduleTimeoutLocked(ToastRecord r)
1984    {
1985        mHandler.removeCallbacksAndMessages(r);
1986        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1987        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1988        mHandler.sendMessageDelayed(m, delay);
1989    }
1990
1991    private void handleTimeout(ToastRecord record)
1992    {
1993        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1994        synchronized (mToastQueue) {
1995            int index = indexOfToastLocked(record.pkg, record.callback);
1996            if (index >= 0) {
1997                cancelToastLocked(index);
1998            }
1999        }
2000    }
2001
2002    // lock on mToastQueue
2003    int indexOfToastLocked(String pkg, ITransientNotification callback)
2004    {
2005        IBinder cbak = callback.asBinder();
2006        ArrayList<ToastRecord> list = mToastQueue;
2007        int len = list.size();
2008        for (int i=0; i<len; i++) {
2009            ToastRecord r = list.get(i);
2010            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2011                return i;
2012            }
2013        }
2014        return -1;
2015    }
2016
2017    // lock on mToastQueue
2018    void keepProcessAliveLocked(int pid)
2019    {
2020        int toastCount = 0; // toasts from this pid
2021        ArrayList<ToastRecord> list = mToastQueue;
2022        int N = list.size();
2023        for (int i=0; i<N; i++) {
2024            ToastRecord r = list.get(i);
2025            if (r.pid == pid) {
2026                toastCount++;
2027            }
2028        }
2029        try {
2030            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2031        } catch (RemoteException e) {
2032            // Shouldn't happen.
2033        }
2034    }
2035
2036    private void handleRankingReconsideration(Message message) {
2037        if (!(message.obj instanceof RankingReconsideration)) return;
2038        RankingReconsideration recon = (RankingReconsideration) message.obj;
2039        recon.run();
2040        boolean changed;
2041        synchronized (mNotificationList) {
2042            final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
2043            if (record == null) {
2044                return;
2045            }
2046            int indexBefore = findNotificationRecordIndexLocked(record);
2047            boolean interceptBefore = record.isIntercepted();
2048            recon.applyChangesLocked(record);
2049            applyZenModeLocked(record);
2050            mRankingHelper.sort(mNotificationList);
2051            int indexAfter = findNotificationRecordIndexLocked(record);
2052            boolean interceptAfter = record.isIntercepted();
2053            changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
2054            if (interceptBefore && !interceptAfter) {
2055                buzzBeepBlinkLocked(record);
2056            }
2057        }
2058        if (changed) {
2059            scheduleSendRankingUpdate();
2060        }
2061    }
2062
2063    private void handleRankingConfigChange() {
2064        synchronized (mNotificationList) {
2065            final int N = mNotificationList.size();
2066            ArrayList<String> orderBefore = new ArrayList<String>(N);
2067            for (int i = 0; i < N; i++) {
2068                final NotificationRecord r = mNotificationList.get(i);
2069                orderBefore.add(r.getKey());
2070                mRankingHelper.extractSignals(r);
2071            }
2072            mRankingHelper.sort(mNotificationList);
2073            for (int i = 0; i < N; i++) {
2074                if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) {
2075                    scheduleSendRankingUpdate();
2076                    return;
2077                }
2078            }
2079        }
2080    }
2081
2082    // let zen mode evaluate this record
2083    private void applyZenModeLocked(NotificationRecord record) {
2084        record.setIntercepted(mZenModeHelper.shouldIntercept(record));
2085    }
2086
2087    // lock on mNotificationList
2088    private int findNotificationRecordIndexLocked(NotificationRecord target) {
2089        return mRankingHelper.indexOf(mNotificationList, target);
2090    }
2091
2092    private void scheduleSendRankingUpdate() {
2093        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2094        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2095        mHandler.sendMessage(m);
2096    }
2097
2098    private void handleSendRankingUpdate() {
2099        synchronized (mNotificationList) {
2100            mListeners.notifyRankingUpdateLocked();
2101        }
2102    }
2103
2104    private void scheduleListenerHintsChanged(int state) {
2105        mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2106        mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
2107    }
2108
2109    private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
2110        mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
2111        mHandler.obtainMessage(
2112                MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
2113                listenerInterruptionFilter,
2114                0).sendToTarget();
2115    }
2116
2117    private void handleListenerHintsChanged(int hints) {
2118        synchronized (mNotificationList) {
2119            mListeners.notifyListenerHintsChangedLocked(hints);
2120        }
2121    }
2122
2123    private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
2124        synchronized (mNotificationList) {
2125            mListeners.notifyInterruptionFilterChanged(interruptionFilter);
2126        }
2127    }
2128
2129    private final class WorkerHandler extends Handler
2130    {
2131        @Override
2132        public void handleMessage(Message msg)
2133        {
2134            switch (msg.what)
2135            {
2136                case MESSAGE_TIMEOUT:
2137                    handleTimeout((ToastRecord)msg.obj);
2138                    break;
2139                case MESSAGE_SAVE_POLICY_FILE:
2140                    handleSavePolicyFile();
2141                    break;
2142                case MESSAGE_SEND_RANKING_UPDATE:
2143                    handleSendRankingUpdate();
2144                    break;
2145                case MESSAGE_LISTENER_HINTS_CHANGED:
2146                    handleListenerHintsChanged(msg.arg1);
2147                    break;
2148                case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
2149                    handleListenerInterruptionFilterChanged(msg.arg1);
2150                    break;
2151            }
2152        }
2153
2154    }
2155
2156    private final class RankingWorkerHandler extends Handler
2157    {
2158        public RankingWorkerHandler(Looper looper) {
2159            super(looper);
2160        }
2161
2162        @Override
2163        public void handleMessage(Message msg) {
2164            switch (msg.what) {
2165                case MESSAGE_RECONSIDER_RANKING:
2166                    handleRankingReconsideration(msg);
2167                    break;
2168                case MESSAGE_RANKING_CONFIG_CHANGE:
2169                    handleRankingConfigChange();
2170                    break;
2171            }
2172        }
2173    }
2174
2175    // Notifications
2176    // ============================================================================
2177    static int clamp(int x, int low, int high) {
2178        return (x < low) ? low : ((x > high) ? high : x);
2179    }
2180
2181    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2182        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2183        if (!manager.isEnabled()) {
2184            return;
2185        }
2186
2187        AccessibilityEvent event =
2188            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2189        event.setPackageName(packageName);
2190        event.setClassName(Notification.class.getName());
2191        event.setParcelableData(notification);
2192        CharSequence tickerText = notification.tickerText;
2193        if (!TextUtils.isEmpty(tickerText)) {
2194            event.getText().add(tickerText);
2195        }
2196
2197        manager.sendAccessibilityEvent(event);
2198    }
2199
2200    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2201        // tell the app
2202        if (sendDelete) {
2203            if (r.getNotification().deleteIntent != null) {
2204                try {
2205                    r.getNotification().deleteIntent.send();
2206                } catch (PendingIntent.CanceledException ex) {
2207                    // do nothing - there's no relevant way to recover, and
2208                    //     no reason to let this propagate
2209                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2210                }
2211            }
2212        }
2213
2214        // status bar
2215        if (r.getNotification().icon != 0) {
2216            r.isCanceled = true;
2217            mListeners.notifyRemovedLocked(r.sbn);
2218        }
2219
2220        // sound
2221        if (mSoundNotification == r) {
2222            mSoundNotification = null;
2223            final long identity = Binder.clearCallingIdentity();
2224            try {
2225                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2226                if (player != null) {
2227                    player.stopAsync();
2228                }
2229            } catch (RemoteException e) {
2230            } finally {
2231                Binder.restoreCallingIdentity(identity);
2232            }
2233        }
2234
2235        // vibrate
2236        if (mVibrateNotification == r) {
2237            mVibrateNotification = null;
2238            long identity = Binder.clearCallingIdentity();
2239            try {
2240                mVibrator.cancel();
2241            }
2242            finally {
2243                Binder.restoreCallingIdentity(identity);
2244            }
2245        }
2246
2247        // light
2248        mLights.remove(r.getKey());
2249        if (mLedNotification == r) {
2250            mLedNotification = null;
2251        }
2252
2253        // Record usage stats
2254        switch (reason) {
2255            case REASON_DELEGATE_CANCEL:
2256            case REASON_DELEGATE_CANCEL_ALL:
2257            case REASON_LISTENER_CANCEL:
2258            case REASON_LISTENER_CANCEL_ALL:
2259                mUsageStats.registerDismissedByUser(r);
2260                break;
2261            case REASON_NOMAN_CANCEL:
2262            case REASON_NOMAN_CANCEL_ALL:
2263                mUsageStats.registerRemovedByApp(r);
2264                break;
2265            case REASON_DELEGATE_CLICK:
2266                mUsageStats.registerCancelDueToClick(r);
2267                break;
2268            default:
2269                mUsageStats.registerCancelUnknown(r);
2270                break;
2271        }
2272
2273        mNotificationsByKey.remove(r.sbn.getKey());
2274
2275        // Save it for users of getHistoricalNotifications()
2276        mArchive.record(r.sbn);
2277    }
2278
2279    /**
2280     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2281     * and none of the {@code mustNotHaveFlags}.
2282     */
2283    void cancelNotification(final int callingUid, final int callingPid,
2284            final String pkg, final String tag, final int id,
2285            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2286            final int userId, final int reason, final ManagedServiceInfo listener) {
2287        // In enqueueNotificationInternal notifications are added by scheduling the
2288        // work on the worker handler. Hence, we also schedule the cancel on this
2289        // handler to avoid a scenario where an add notification call followed by a
2290        // remove notification call ends up in not removing the notification.
2291        mHandler.post(new Runnable() {
2292            @Override
2293            public void run() {
2294                String listenerName = listener == null ? null : listener.component.toShortString();
2295                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2296                        mustHaveFlags, mustNotHaveFlags, reason, listenerName);
2297
2298                synchronized (mNotificationList) {
2299                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2300                    if (index >= 0) {
2301                        NotificationRecord r = mNotificationList.get(index);
2302
2303                        // Ideally we'd do this in the caller of this method. However, that would
2304                        // require the caller to also find the notification.
2305                        if (reason == REASON_DELEGATE_CLICK) {
2306                            mUsageStats.registerClickedByUser(r);
2307                        }
2308
2309                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2310                            return;
2311                        }
2312                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2313                            return;
2314                        }
2315
2316                        mNotificationList.remove(index);
2317
2318                        cancelNotificationLocked(r, sendDelete, reason);
2319                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName);
2320                        updateLightsLocked();
2321                    }
2322                }
2323            }
2324        });
2325    }
2326
2327    /**
2328     * Determine whether the userId applies to the notification in question, either because
2329     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2330     */
2331    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2332        return
2333                // looking for USER_ALL notifications? match everything
2334                   userId == UserHandle.USER_ALL
2335                // a notification sent to USER_ALL matches any query
2336                || r.getUserId() == UserHandle.USER_ALL
2337                // an exact user match
2338                || r.getUserId() == userId;
2339    }
2340
2341    /**
2342     * Determine whether the userId applies to the notification in question, either because
2343     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2344     * because it matches one of the users profiles.
2345     */
2346    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2347        return notificationMatchesUserId(r, userId)
2348                || mUserProfiles.isCurrentProfile(r.getUserId());
2349    }
2350
2351    /**
2352     * Cancels all notifications from a given package that have all of the
2353     * {@code mustHaveFlags}.
2354     */
2355    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2356            int mustNotHaveFlags, boolean doit, int userId, int reason,
2357            ManagedServiceInfo listener) {
2358        String listenerName = listener == null ? null : listener.component.toShortString();
2359        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2360                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2361                listenerName);
2362
2363        synchronized (mNotificationList) {
2364            final int N = mNotificationList.size();
2365            ArrayList<NotificationRecord> canceledNotifications = null;
2366            for (int i = N-1; i >= 0; --i) {
2367                NotificationRecord r = mNotificationList.get(i);
2368                if (!notificationMatchesUserId(r, userId)) {
2369                    continue;
2370                }
2371                // Don't remove notifications to all, if there's no package name specified
2372                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2373                    continue;
2374                }
2375                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2376                    continue;
2377                }
2378                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2379                    continue;
2380                }
2381                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2382                    continue;
2383                }
2384                if (canceledNotifications == null) {
2385                    canceledNotifications = new ArrayList<>();
2386                }
2387                canceledNotifications.add(r);
2388                if (!doit) {
2389                    return true;
2390                }
2391                mNotificationList.remove(i);
2392                cancelNotificationLocked(r, false, reason);
2393            }
2394            if (doit && canceledNotifications != null) {
2395                final int M = canceledNotifications.size();
2396                for (int i = 0; i < M; i++) {
2397                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2398                            listenerName);
2399                }
2400            }
2401            if (canceledNotifications != null) {
2402                updateLightsLocked();
2403            }
2404            return canceledNotifications != null;
2405        }
2406    }
2407
2408    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2409            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2410        String listenerName = listener == null ? null : listener.component.toShortString();
2411        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2412                null, userId, 0, 0, reason, listenerName);
2413
2414        ArrayList<NotificationRecord> canceledNotifications = null;
2415        final int N = mNotificationList.size();
2416        for (int i=N-1; i>=0; i--) {
2417            NotificationRecord r = mNotificationList.get(i);
2418            if (includeCurrentProfiles) {
2419                if (!notificationMatchesCurrentProfiles(r, userId)) {
2420                    continue;
2421                }
2422            } else {
2423                if (!notificationMatchesUserId(r, userId)) {
2424                    continue;
2425                }
2426            }
2427
2428            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2429                            | Notification.FLAG_NO_CLEAR)) == 0) {
2430                mNotificationList.remove(i);
2431                cancelNotificationLocked(r, true, reason);
2432                // Make a note so we can cancel children later.
2433                if (canceledNotifications == null) {
2434                    canceledNotifications = new ArrayList<>();
2435                }
2436                canceledNotifications.add(r);
2437            }
2438        }
2439        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
2440        for (int i = 0; i < M; i++) {
2441            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2442                    listenerName);
2443        }
2444        updateLightsLocked();
2445    }
2446
2447    // Warning: The caller is responsible for invoking updateLightsLocked().
2448    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
2449            String listenerName) {
2450        Notification n = r.getNotification();
2451        if (!n.isGroupSummary()) {
2452            return;
2453        }
2454
2455        String pkg = r.sbn.getPackageName();
2456        int userId = r.getUserId();
2457
2458        if (pkg == null) {
2459            if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
2460            return;
2461        }
2462
2463        final int N = mNotificationList.size();
2464        for (int i = N - 1; i >= 0; i--) {
2465            NotificationRecord childR = mNotificationList.get(i);
2466            StatusBarNotification childSbn = childR.sbn;
2467            if (childR.getNotification().isGroupChild() &&
2468                    childR.getGroupKey().equals(r.getGroupKey())) {
2469                EventLogTags.writeNotificationCancel(callingUid, callingPid,
2470                        pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0,
2471                        REASON_GROUP_SUMMARY_CANCELED, listenerName);
2472                mNotificationList.remove(i);
2473                cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);
2474            }
2475        }
2476    }
2477
2478    // lock on mNotificationList
2479    void updateLightsLocked()
2480    {
2481        // handle notification lights
2482        if (mLedNotification == null) {
2483            // get next notification, if any
2484            int n = mLights.size();
2485            if (n > 0) {
2486                mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
2487            }
2488        }
2489
2490        // Don't flash while we are in a call or screen is on
2491        if (mLedNotification == null || mInCall || mScreenOn) {
2492            mNotificationLight.turnOff();
2493            mStatusBar.notificationLightOff();
2494        } else {
2495            final Notification ledno = mLedNotification.sbn.getNotification();
2496            int ledARGB = ledno.ledARGB;
2497            int ledOnMS = ledno.ledOnMS;
2498            int ledOffMS = ledno.ledOffMS;
2499            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2500                ledARGB = mDefaultNotificationColor;
2501                ledOnMS = mDefaultNotificationLedOn;
2502                ledOffMS = mDefaultNotificationLedOff;
2503            }
2504            if (mNotificationPulseEnabled) {
2505                // pulse repeatedly
2506                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2507                        ledOnMS, ledOffMS);
2508            }
2509            // let SystemUI make an independent decision
2510            mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
2511        }
2512    }
2513
2514    // lock on mNotificationList
2515    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2516    {
2517        ArrayList<NotificationRecord> list = mNotificationList;
2518        final int len = list.size();
2519        for (int i=0; i<len; i++) {
2520            NotificationRecord r = list.get(i);
2521            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2522                continue;
2523            }
2524            if (tag == null) {
2525                if (r.sbn.getTag() != null) {
2526                    continue;
2527                }
2528            } else {
2529                if (!tag.equals(r.sbn.getTag())) {
2530                    continue;
2531                }
2532            }
2533            if (r.sbn.getPackageName().equals(pkg)) {
2534                return i;
2535            }
2536        }
2537        return -1;
2538    }
2539
2540    // lock on mNotificationList
2541    int indexOfNotificationLocked(String key) {
2542        final int N = mNotificationList.size();
2543        for (int i = 0; i < N; i++) {
2544            if (key.equals(mNotificationList.get(i).getKey())) {
2545                return i;
2546            }
2547        }
2548        return -1;
2549    }
2550
2551    private void updateNotificationPulse() {
2552        synchronized (mNotificationList) {
2553            updateLightsLocked();
2554        }
2555    }
2556
2557    private static boolean isUidSystem(int uid) {
2558        final int appid = UserHandle.getAppId(uid);
2559        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2560    }
2561
2562    private static boolean isCallerSystem() {
2563        return isUidSystem(Binder.getCallingUid());
2564    }
2565
2566    private static void checkCallerIsSystem() {
2567        if (isCallerSystem()) {
2568            return;
2569        }
2570        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2571    }
2572
2573    private static void checkCallerIsSystemOrSameApp(String pkg) {
2574        if (isCallerSystem()) {
2575            return;
2576        }
2577        final int uid = Binder.getCallingUid();
2578        try {
2579            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2580                    pkg, 0, UserHandle.getCallingUserId());
2581            if (ai == null) {
2582                throw new SecurityException("Unknown package " + pkg);
2583            }
2584            if (!UserHandle.isSameApp(ai.uid, uid)) {
2585                throw new SecurityException("Calling uid " + uid + " gave package"
2586                        + pkg + " which is owned by uid " + ai.uid);
2587            }
2588        } catch (RemoteException re) {
2589            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2590        }
2591    }
2592
2593    /**
2594     * Generates a NotificationRankingUpdate from 'sbns', considering only
2595     * notifications visible to the given listener.
2596     *
2597     * <p>Caller must hold a lock on mNotificationList.</p>
2598     */
2599    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
2600        int speedBumpIndex = -1;
2601        final int N = mNotificationList.size();
2602        ArrayList<String> keys = new ArrayList<String>(N);
2603        ArrayList<String> interceptedKeys = new ArrayList<String>(N);
2604        for (int i = 0; i < N; i++) {
2605            NotificationRecord record = mNotificationList.get(i);
2606            if (!isVisibleToListener(record.sbn, info)) {
2607                continue;
2608            }
2609            keys.add(record.sbn.getKey());
2610            if (record.isIntercepted()) {
2611                interceptedKeys.add(record.sbn.getKey());
2612            }
2613            if (speedBumpIndex == -1 &&
2614                    record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
2615                speedBumpIndex = keys.size() - 1;
2616            }
2617        }
2618        String[] keysAr = keys.toArray(new String[keys.size()]);
2619        String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
2620        return new NotificationRankingUpdate(keysAr, interceptedKeysAr, speedBumpIndex);
2621    }
2622
2623    private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
2624        if (!listener.enabledAndUserMatches(sbn.getUserId())) {
2625            return false;
2626        }
2627        // TODO: remove this for older listeners.
2628        return true;
2629    }
2630
2631    public class NotificationListeners extends ManagedServices {
2632
2633        private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
2634
2635        public NotificationListeners() {
2636            super(getContext(), mHandler, mNotificationList, mUserProfiles);
2637        }
2638
2639        @Override
2640        protected Config getConfig() {
2641            Config c = new Config();
2642            c.caption = "notification listener";
2643            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2644            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2645            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2646            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2647            c.clientLabel = R.string.notification_listener_binding_label;
2648            return c;
2649        }
2650
2651        @Override
2652        protected IInterface asInterface(IBinder binder) {
2653            return INotificationListener.Stub.asInterface(binder);
2654        }
2655
2656        @Override
2657        public void onServiceAdded(ManagedServiceInfo info) {
2658            final INotificationListener listener = (INotificationListener) info.service;
2659            final NotificationRankingUpdate update;
2660            synchronized (mNotificationList) {
2661                update = makeRankingUpdateLocked(info);
2662            }
2663            try {
2664                listener.onListenerConnected(update);
2665            } catch (RemoteException e) {
2666                // we tried
2667            }
2668        }
2669
2670        @Override
2671        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
2672            if (mListenersDisablingEffects.remove(removed)) {
2673                updateListenerHintsLocked();
2674            }
2675            mLightTrimListeners.remove(removed);
2676        }
2677
2678        public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
2679            if (trim == TRIM_LIGHT) {
2680                mLightTrimListeners.add(info);
2681            } else {
2682                mLightTrimListeners.remove(info);
2683            }
2684        }
2685
2686        public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
2687            return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
2688
2689        }
2690
2691        /**
2692         * asynchronously notify all listeners about a new notification
2693         *
2694         * <p>
2695         * Also takes care of removing a notification that has been visible to a listener before,
2696         * but isn't anymore.
2697         */
2698        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
2699            // Lazily initialized snapshots of the notification.
2700            StatusBarNotification sbnClone = null;
2701            StatusBarNotification sbnCloneLight = null;
2702
2703            for (final ManagedServiceInfo info : mServices) {
2704                boolean sbnVisible = isVisibleToListener(sbn, info);
2705                boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
2706                // This notification hasn't been and still isn't visible -> ignore.
2707                if (!oldSbnVisible && !sbnVisible) {
2708                    continue;
2709                }
2710                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2711
2712                // This notification became invisible -> remove the old one.
2713                if (oldSbnVisible && !sbnVisible) {
2714                    final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
2715                    mHandler.post(new Runnable() {
2716                        @Override
2717                        public void run() {
2718                            notifyRemoved(info, oldSbnLightClone, update);
2719                        }
2720                    });
2721                    continue;
2722                }
2723
2724                final int trim = mListeners.getOnNotificationPostedTrim(info);
2725
2726                if (trim == TRIM_LIGHT && sbnCloneLight == null) {
2727                    sbnCloneLight = sbn.cloneLight();
2728                } else if (trim == TRIM_FULL && sbnClone == null) {
2729                    sbnClone = sbn.clone();
2730                }
2731                final StatusBarNotification sbnToPost =
2732                        (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
2733
2734                mHandler.post(new Runnable() {
2735                    @Override
2736                    public void run() {
2737                        notifyPosted(info, sbnToPost, update);
2738                    }
2739                });
2740            }
2741        }
2742
2743        /**
2744         * asynchronously notify all listeners about a removed notification
2745         */
2746        public void notifyRemovedLocked(StatusBarNotification sbn) {
2747            // make a copy in case changes are made to the underlying Notification object
2748            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2749            // notification
2750            final StatusBarNotification sbnLight = sbn.cloneLight();
2751            for (final ManagedServiceInfo info : mServices) {
2752                if (!isVisibleToListener(sbn, info)) {
2753                    continue;
2754                }
2755                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2756                mHandler.post(new Runnable() {
2757                    @Override
2758                    public void run() {
2759                        notifyRemoved(info, sbnLight, update);
2760                    }
2761                });
2762            }
2763        }
2764
2765        /**
2766         * asynchronously notify all listeners about a reordering of notifications
2767         */
2768        public void notifyRankingUpdateLocked() {
2769            for (final ManagedServiceInfo serviceInfo : mServices) {
2770                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2771                    continue;
2772                }
2773                final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
2774                mHandler.post(new Runnable() {
2775                    @Override
2776                    public void run() {
2777                        notifyRankingUpdate(serviceInfo, update);
2778                    }
2779                });
2780            }
2781        }
2782
2783        public void notifyListenerHintsChangedLocked(final int hints) {
2784            for (final ManagedServiceInfo serviceInfo : mServices) {
2785                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2786                    continue;
2787                }
2788                mHandler.post(new Runnable() {
2789                    @Override
2790                    public void run() {
2791                        notifyListenerHintsChanged(serviceInfo, hints);
2792                    }
2793                });
2794            }
2795        }
2796
2797        public void notifyInterruptionFilterChanged(final int interruptionFilter) {
2798            for (final ManagedServiceInfo serviceInfo : mServices) {
2799                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2800                    continue;
2801                }
2802                mHandler.post(new Runnable() {
2803                    @Override
2804                    public void run() {
2805                        notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
2806                    }
2807                });
2808            }
2809        }
2810
2811        private void notifyPosted(final ManagedServiceInfo info,
2812                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
2813            final INotificationListener listener = (INotificationListener)info.service;
2814            try {
2815                listener.onNotificationPosted(sbn, rankingUpdate);
2816            } catch (RemoteException ex) {
2817                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2818            }
2819        }
2820
2821        private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
2822                NotificationRankingUpdate rankingUpdate) {
2823            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2824                return;
2825            }
2826            final INotificationListener listener = (INotificationListener) info.service;
2827            try {
2828                listener.onNotificationRemoved(sbn, rankingUpdate);
2829            } catch (RemoteException ex) {
2830                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
2831            }
2832        }
2833
2834        private void notifyRankingUpdate(ManagedServiceInfo info,
2835                                         NotificationRankingUpdate rankingUpdate) {
2836            final INotificationListener listener = (INotificationListener) info.service;
2837            try {
2838                listener.onNotificationRankingUpdate(rankingUpdate);
2839            } catch (RemoteException ex) {
2840                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
2841            }
2842        }
2843
2844        private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
2845            final INotificationListener listener = (INotificationListener) info.service;
2846            try {
2847                listener.onListenerHintsChanged(hints);
2848            } catch (RemoteException ex) {
2849                Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
2850            }
2851        }
2852
2853        private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
2854                int interruptionFilter) {
2855            final INotificationListener listener = (INotificationListener) info.service;
2856            try {
2857                listener.onInterruptionFilterChanged(interruptionFilter);
2858            } catch (RemoteException ex) {
2859                Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
2860            }
2861        }
2862
2863        private boolean isListenerPackage(String packageName) {
2864            if (packageName == null) {
2865                return false;
2866            }
2867            // TODO: clean up locking object later
2868            synchronized (mNotificationList) {
2869                for (final ManagedServiceInfo serviceInfo : mServices) {
2870                    if (packageName.equals(serviceInfo.component.getPackageName())) {
2871                        return true;
2872                    }
2873                }
2874            }
2875            return false;
2876        }
2877    }
2878
2879    public static final class DumpFilter {
2880        public String pkgFilter;
2881        public boolean zen;
2882
2883        public static DumpFilter parseFromArguments(String[] args) {
2884            if (args != null && args.length == 2 && "p".equals(args[0])
2885                    && args[1] != null && !args[1].trim().isEmpty()) {
2886                final DumpFilter filter = new DumpFilter();
2887                filter.pkgFilter = args[1].trim().toLowerCase();
2888                return filter;
2889            }
2890            if (args != null && args.length == 1 && "zen".equals(args[0])) {
2891                final DumpFilter filter = new DumpFilter();
2892                filter.zen = true;
2893                return filter;
2894            }
2895            return null;
2896        }
2897
2898        public boolean matches(StatusBarNotification sbn) {
2899            return zen ? true : sbn != null
2900                    && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
2901        }
2902
2903        public boolean matches(ComponentName component) {
2904            return zen ? true : component != null && matches(component.getPackageName());
2905        }
2906
2907        public boolean matches(String pkg) {
2908            return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
2909        }
2910
2911        @Override
2912        public String toString() {
2913            return zen ? "zen" : ('\'' + pkgFilter + '\'');
2914        }
2915    }
2916}
2917