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