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