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