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