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