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