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