NotificationManagerService.java revision 4da84cd56582fb4025eb5a9a1d4bb5b5b4a50cef
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
2377    /**
2378     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2379     * and none of the {@code mustNotHaveFlags}.
2380     */
2381    void cancelNotification(final int callingUid, final int callingPid,
2382            final String pkg, final String tag, final int id,
2383            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2384            final int userId, final int reason, final ManagedServiceInfo listener) {
2385        // In enqueueNotificationInternal notifications are added by scheduling the
2386        // work on the worker handler. Hence, we also schedule the cancel on this
2387        // handler to avoid a scenario where an add notification call followed by a
2388        // remove notification call ends up in not removing the notification.
2389        mHandler.post(new Runnable() {
2390            @Override
2391            public void run() {
2392                String listenerName = listener == null ? null : listener.component.toShortString();
2393                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2394                        mustHaveFlags, mustNotHaveFlags, reason, listenerName);
2395
2396                synchronized (mNotificationList) {
2397                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2398                    if (index >= 0) {
2399                        NotificationRecord r = mNotificationList.get(index);
2400
2401                        // Ideally we'd do this in the caller of this method. However, that would
2402                        // require the caller to also find the notification.
2403                        if (reason == REASON_DELEGATE_CLICK) {
2404                            mUsageStats.registerClickedByUser(r);
2405                        }
2406
2407                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2408                            return;
2409                        }
2410                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2411                            return;
2412                        }
2413
2414                        mNotificationList.remove(index);
2415
2416                        cancelNotificationLocked(r, sendDelete, reason);
2417                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName);
2418                        updateLightsLocked();
2419                    }
2420                }
2421            }
2422        });
2423    }
2424
2425    /**
2426     * Determine whether the userId applies to the notification in question, either because
2427     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2428     */
2429    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2430        return
2431                // looking for USER_ALL notifications? match everything
2432                   userId == UserHandle.USER_ALL
2433                // a notification sent to USER_ALL matches any query
2434                || r.getUserId() == UserHandle.USER_ALL
2435                // an exact user match
2436                || r.getUserId() == userId;
2437    }
2438
2439    /**
2440     * Determine whether the userId applies to the notification in question, either because
2441     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2442     * because it matches one of the users profiles.
2443     */
2444    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2445        return notificationMatchesUserId(r, userId)
2446                || mUserProfiles.isCurrentProfile(r.getUserId());
2447    }
2448
2449    /**
2450     * Cancels all notifications from a given package that have all of the
2451     * {@code mustHaveFlags}.
2452     */
2453    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2454            int mustNotHaveFlags, boolean doit, int userId, int reason,
2455            ManagedServiceInfo listener) {
2456        String listenerName = listener == null ? null : listener.component.toShortString();
2457        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2458                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2459                listenerName);
2460
2461        synchronized (mNotificationList) {
2462            final int N = mNotificationList.size();
2463            ArrayList<NotificationRecord> canceledNotifications = null;
2464            for (int i = N-1; i >= 0; --i) {
2465                NotificationRecord r = mNotificationList.get(i);
2466                if (!notificationMatchesUserId(r, userId)) {
2467                    continue;
2468                }
2469                // Don't remove notifications to all, if there's no package name specified
2470                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2471                    continue;
2472                }
2473                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2474                    continue;
2475                }
2476                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2477                    continue;
2478                }
2479                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2480                    continue;
2481                }
2482                if (canceledNotifications == null) {
2483                    canceledNotifications = new ArrayList<>();
2484                }
2485                canceledNotifications.add(r);
2486                if (!doit) {
2487                    return true;
2488                }
2489                mNotificationList.remove(i);
2490                cancelNotificationLocked(r, false, reason);
2491            }
2492            if (doit && canceledNotifications != null) {
2493                final int M = canceledNotifications.size();
2494                for (int i = 0; i < M; i++) {
2495                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2496                            listenerName);
2497                }
2498            }
2499            if (canceledNotifications != null) {
2500                updateLightsLocked();
2501            }
2502            return canceledNotifications != null;
2503        }
2504    }
2505
2506    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2507            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2508        String listenerName = listener == null ? null : listener.component.toShortString();
2509        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2510                null, userId, 0, 0, reason, listenerName);
2511
2512        ArrayList<NotificationRecord> canceledNotifications = null;
2513        final int N = mNotificationList.size();
2514        for (int i=N-1; i>=0; i--) {
2515            NotificationRecord r = mNotificationList.get(i);
2516            if (includeCurrentProfiles) {
2517                if (!notificationMatchesCurrentProfiles(r, userId)) {
2518                    continue;
2519                }
2520            } else {
2521                if (!notificationMatchesUserId(r, userId)) {
2522                    continue;
2523                }
2524            }
2525
2526            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2527                            | Notification.FLAG_NO_CLEAR)) == 0) {
2528                mNotificationList.remove(i);
2529                cancelNotificationLocked(r, true, reason);
2530                // Make a note so we can cancel children later.
2531                if (canceledNotifications == null) {
2532                    canceledNotifications = new ArrayList<>();
2533                }
2534                canceledNotifications.add(r);
2535            }
2536        }
2537        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
2538        for (int i = 0; i < M; i++) {
2539            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2540                    listenerName);
2541        }
2542        updateLightsLocked();
2543    }
2544
2545    // Warning: The caller is responsible for invoking updateLightsLocked().
2546    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
2547            String listenerName) {
2548        Notification n = r.getNotification();
2549        if (!n.isGroupSummary()) {
2550            return;
2551        }
2552
2553        String pkg = r.sbn.getPackageName();
2554        int userId = r.getUserId();
2555
2556        if (pkg == null) {
2557            if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
2558            return;
2559        }
2560
2561        final int N = mNotificationList.size();
2562        for (int i = N - 1; i >= 0; i--) {
2563            NotificationRecord childR = mNotificationList.get(i);
2564            StatusBarNotification childSbn = childR.sbn;
2565            if (childR.getNotification().isGroupChild() &&
2566                    childR.getGroupKey().equals(r.getGroupKey())) {
2567                EventLogTags.writeNotificationCancel(callingUid, callingPid,
2568                        pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0,
2569                        REASON_GROUP_SUMMARY_CANCELED, listenerName);
2570                mNotificationList.remove(i);
2571                cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);
2572            }
2573        }
2574    }
2575
2576    // lock on mNotificationList
2577    void updateLightsLocked()
2578    {
2579        // handle notification lights
2580        if (mLedNotification == null) {
2581            // get next notification, if any
2582            int n = mLights.size();
2583            if (n > 0) {
2584                mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
2585            }
2586        }
2587
2588        // Don't flash while we are in a call or screen is on
2589        if (mLedNotification == null || mInCall || mScreenOn) {
2590            mNotificationLight.turnOff();
2591            mStatusBar.notificationLightOff();
2592        } else {
2593            final Notification ledno = mLedNotification.sbn.getNotification();
2594            int ledARGB = ledno.ledARGB;
2595            int ledOnMS = ledno.ledOnMS;
2596            int ledOffMS = ledno.ledOffMS;
2597            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2598                ledARGB = mDefaultNotificationColor;
2599                ledOnMS = mDefaultNotificationLedOn;
2600                ledOffMS = mDefaultNotificationLedOff;
2601            }
2602            if (mNotificationPulseEnabled) {
2603                // pulse repeatedly
2604                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2605                        ledOnMS, ledOffMS);
2606            }
2607            // let SystemUI make an independent decision
2608            mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
2609        }
2610    }
2611
2612    // lock on mNotificationList
2613    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2614    {
2615        ArrayList<NotificationRecord> list = mNotificationList;
2616        final int len = list.size();
2617        for (int i=0; i<len; i++) {
2618            NotificationRecord r = list.get(i);
2619            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2620                continue;
2621            }
2622            if (tag == null) {
2623                if (r.sbn.getTag() != null) {
2624                    continue;
2625                }
2626            } else {
2627                if (!tag.equals(r.sbn.getTag())) {
2628                    continue;
2629                }
2630            }
2631            if (r.sbn.getPackageName().equals(pkg)) {
2632                return i;
2633            }
2634        }
2635        return -1;
2636    }
2637
2638    // lock on mNotificationList
2639    int indexOfNotificationLocked(String key) {
2640        final int N = mNotificationList.size();
2641        for (int i = 0; i < N; i++) {
2642            if (key.equals(mNotificationList.get(i).getKey())) {
2643                return i;
2644            }
2645        }
2646        return -1;
2647    }
2648
2649    private void updateNotificationPulse() {
2650        synchronized (mNotificationList) {
2651            updateLightsLocked();
2652        }
2653    }
2654
2655    private static boolean isUidSystem(int uid) {
2656        final int appid = UserHandle.getAppId(uid);
2657        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2658    }
2659
2660    private static boolean isCallerSystem() {
2661        return isUidSystem(Binder.getCallingUid());
2662    }
2663
2664    private static void checkCallerIsSystem() {
2665        if (isCallerSystem()) {
2666            return;
2667        }
2668        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2669    }
2670
2671    private static void checkCallerIsSystemOrSameApp(String pkg) {
2672        if (isCallerSystem()) {
2673            return;
2674        }
2675        final int uid = Binder.getCallingUid();
2676        try {
2677            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2678                    pkg, 0, UserHandle.getCallingUserId());
2679            if (ai == null) {
2680                throw new SecurityException("Unknown package " + pkg);
2681            }
2682            if (!UserHandle.isSameApp(ai.uid, uid)) {
2683                throw new SecurityException("Calling uid " + uid + " gave package"
2684                        + pkg + " which is owned by uid " + ai.uid);
2685            }
2686        } catch (RemoteException re) {
2687            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2688        }
2689    }
2690
2691    private static String callStateToString(int state) {
2692        switch (state) {
2693            case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
2694            case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
2695            case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
2696            default: return "CALL_STATE_UNKNOWN_" + state;
2697        }
2698    }
2699
2700    private void listenForCallState() {
2701        TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
2702            @Override
2703            public void onCallStateChanged(int state, String incomingNumber) {
2704                if (mCallState == state) return;
2705                if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
2706                mCallState = state;
2707            }
2708        }, PhoneStateListener.LISTEN_CALL_STATE);
2709    }
2710
2711    /**
2712     * Generates a NotificationRankingUpdate from 'sbns', considering only
2713     * notifications visible to the given listener.
2714     *
2715     * <p>Caller must hold a lock on mNotificationList.</p>
2716     */
2717    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
2718        int speedBumpIndex = -1;
2719        final int N = mNotificationList.size();
2720        ArrayList<String> keys = new ArrayList<String>(N);
2721        ArrayList<String> interceptedKeys = new ArrayList<String>(N);
2722        Bundle visibilityOverrides = new Bundle();
2723        for (int i = 0; i < N; i++) {
2724            NotificationRecord record = mNotificationList.get(i);
2725            if (!isVisibleToListener(record.sbn, info)) {
2726                continue;
2727            }
2728            keys.add(record.sbn.getKey());
2729            if (record.isIntercepted()) {
2730                interceptedKeys.add(record.sbn.getKey());
2731            }
2732            if (record.getPackageVisibilityOverride()
2733                    != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
2734                visibilityOverrides.putInt(record.sbn.getKey(),
2735                        record.getPackageVisibilityOverride());
2736            }
2737            // Find first min-prio notification for speedbump placement.
2738            if (speedBumpIndex == -1 &&
2739                    // Intrusiveness trumps priority, hence ignore intrusives.
2740                    !record.isRecentlyIntrusive() &&
2741                    // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
2742                    // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
2743                    // (or lower as a safeguard) is sufficient to find the speedbump index.
2744                    // We'll have to revisit this when more package priority buckets are introduced.
2745                    record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
2746                    record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
2747                speedBumpIndex = keys.size() - 1;
2748            }
2749        }
2750        String[] keysAr = keys.toArray(new String[keys.size()]);
2751        String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
2752        return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
2753                speedBumpIndex);
2754    }
2755
2756    private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
2757        if (!listener.enabledAndUserMatches(sbn.getUserId())) {
2758            return false;
2759        }
2760        // TODO: remove this for older listeners.
2761        return true;
2762    }
2763
2764    public class NotificationListeners extends ManagedServices {
2765
2766        private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
2767
2768        public NotificationListeners() {
2769            super(getContext(), mHandler, mNotificationList, mUserProfiles);
2770        }
2771
2772        @Override
2773        protected Config getConfig() {
2774            Config c = new Config();
2775            c.caption = "notification listener";
2776            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2777            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2778            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2779            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2780            c.clientLabel = R.string.notification_listener_binding_label;
2781            return c;
2782        }
2783
2784        @Override
2785        protected IInterface asInterface(IBinder binder) {
2786            return INotificationListener.Stub.asInterface(binder);
2787        }
2788
2789        @Override
2790        public void onServiceAdded(ManagedServiceInfo info) {
2791            final INotificationListener listener = (INotificationListener) info.service;
2792            final NotificationRankingUpdate update;
2793            synchronized (mNotificationList) {
2794                update = makeRankingUpdateLocked(info);
2795            }
2796            try {
2797                listener.onListenerConnected(update);
2798            } catch (RemoteException e) {
2799                // we tried
2800            }
2801        }
2802
2803        @Override
2804        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
2805            if (mListenersDisablingEffects.remove(removed)) {
2806                updateListenerHintsLocked();
2807            }
2808            mLightTrimListeners.remove(removed);
2809        }
2810
2811        public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
2812            if (trim == TRIM_LIGHT) {
2813                mLightTrimListeners.add(info);
2814            } else {
2815                mLightTrimListeners.remove(info);
2816            }
2817        }
2818
2819        public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
2820            return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
2821
2822        }
2823
2824        /**
2825         * asynchronously notify all listeners about a new notification
2826         *
2827         * <p>
2828         * Also takes care of removing a notification that has been visible to a listener before,
2829         * but isn't anymore.
2830         */
2831        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
2832            // Lazily initialized snapshots of the notification.
2833            StatusBarNotification sbnClone = null;
2834            StatusBarNotification sbnCloneLight = null;
2835
2836            for (final ManagedServiceInfo info : mServices) {
2837                boolean sbnVisible = isVisibleToListener(sbn, info);
2838                boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
2839                // This notification hasn't been and still isn't visible -> ignore.
2840                if (!oldSbnVisible && !sbnVisible) {
2841                    continue;
2842                }
2843                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2844
2845                // This notification became invisible -> remove the old one.
2846                if (oldSbnVisible && !sbnVisible) {
2847                    final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
2848                    mHandler.post(new Runnable() {
2849                        @Override
2850                        public void run() {
2851                            notifyRemoved(info, oldSbnLightClone, update);
2852                        }
2853                    });
2854                    continue;
2855                }
2856
2857                final int trim = mListeners.getOnNotificationPostedTrim(info);
2858
2859                if (trim == TRIM_LIGHT && sbnCloneLight == null) {
2860                    sbnCloneLight = sbn.cloneLight();
2861                } else if (trim == TRIM_FULL && sbnClone == null) {
2862                    sbnClone = sbn.clone();
2863                }
2864                final StatusBarNotification sbnToPost =
2865                        (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
2866
2867                mHandler.post(new Runnable() {
2868                    @Override
2869                    public void run() {
2870                        notifyPosted(info, sbnToPost, update);
2871                    }
2872                });
2873            }
2874        }
2875
2876        /**
2877         * asynchronously notify all listeners about a removed notification
2878         */
2879        public void notifyRemovedLocked(StatusBarNotification sbn) {
2880            // make a copy in case changes are made to the underlying Notification object
2881            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2882            // notification
2883            final StatusBarNotification sbnLight = sbn.cloneLight();
2884            for (final ManagedServiceInfo info : mServices) {
2885                if (!isVisibleToListener(sbn, info)) {
2886                    continue;
2887                }
2888                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2889                mHandler.post(new Runnable() {
2890                    @Override
2891                    public void run() {
2892                        notifyRemoved(info, sbnLight, update);
2893                    }
2894                });
2895            }
2896        }
2897
2898        /**
2899         * asynchronously notify all listeners about a reordering of notifications
2900         */
2901        public void notifyRankingUpdateLocked() {
2902            for (final ManagedServiceInfo serviceInfo : mServices) {
2903                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2904                    continue;
2905                }
2906                final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
2907                mHandler.post(new Runnable() {
2908                    @Override
2909                    public void run() {
2910                        notifyRankingUpdate(serviceInfo, update);
2911                    }
2912                });
2913            }
2914        }
2915
2916        public void notifyListenerHintsChangedLocked(final int hints) {
2917            for (final ManagedServiceInfo serviceInfo : mServices) {
2918                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2919                    continue;
2920                }
2921                mHandler.post(new Runnable() {
2922                    @Override
2923                    public void run() {
2924                        notifyListenerHintsChanged(serviceInfo, hints);
2925                    }
2926                });
2927            }
2928        }
2929
2930        public void notifyInterruptionFilterChanged(final int interruptionFilter) {
2931            for (final ManagedServiceInfo serviceInfo : mServices) {
2932                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2933                    continue;
2934                }
2935                mHandler.post(new Runnable() {
2936                    @Override
2937                    public void run() {
2938                        notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
2939                    }
2940                });
2941            }
2942        }
2943
2944        private void notifyPosted(final ManagedServiceInfo info,
2945                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
2946            final INotificationListener listener = (INotificationListener)info.service;
2947            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
2948            try {
2949                listener.onNotificationPosted(sbnHolder, rankingUpdate);
2950            } catch (RemoteException ex) {
2951                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2952            }
2953        }
2954
2955        private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
2956                NotificationRankingUpdate rankingUpdate) {
2957            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2958                return;
2959            }
2960            final INotificationListener listener = (INotificationListener) info.service;
2961            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
2962            try {
2963                listener.onNotificationRemoved(sbnHolder, rankingUpdate);
2964            } catch (RemoteException ex) {
2965                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
2966            }
2967        }
2968
2969        private void notifyRankingUpdate(ManagedServiceInfo info,
2970                                         NotificationRankingUpdate rankingUpdate) {
2971            final INotificationListener listener = (INotificationListener) info.service;
2972            try {
2973                listener.onNotificationRankingUpdate(rankingUpdate);
2974            } catch (RemoteException ex) {
2975                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
2976            }
2977        }
2978
2979        private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
2980            final INotificationListener listener = (INotificationListener) info.service;
2981            try {
2982                listener.onListenerHintsChanged(hints);
2983            } catch (RemoteException ex) {
2984                Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
2985            }
2986        }
2987
2988        private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
2989                int interruptionFilter) {
2990            final INotificationListener listener = (INotificationListener) info.service;
2991            try {
2992                listener.onInterruptionFilterChanged(interruptionFilter);
2993            } catch (RemoteException ex) {
2994                Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
2995            }
2996        }
2997
2998        private boolean isListenerPackage(String packageName) {
2999            if (packageName == null) {
3000                return false;
3001            }
3002            // TODO: clean up locking object later
3003            synchronized (mNotificationList) {
3004                for (final ManagedServiceInfo serviceInfo : mServices) {
3005                    if (packageName.equals(serviceInfo.component.getPackageName())) {
3006                        return true;
3007                    }
3008                }
3009            }
3010            return false;
3011        }
3012    }
3013
3014    public static final class DumpFilter {
3015        public String pkgFilter;
3016        public boolean zen;
3017
3018        public static DumpFilter parseFromArguments(String[] args) {
3019            if (args != null && args.length == 2 && "p".equals(args[0])
3020                    && args[1] != null && !args[1].trim().isEmpty()) {
3021                final DumpFilter filter = new DumpFilter();
3022                filter.pkgFilter = args[1].trim().toLowerCase();
3023                return filter;
3024            }
3025            if (args != null && args.length == 1 && "zen".equals(args[0])) {
3026                final DumpFilter filter = new DumpFilter();
3027                filter.zen = true;
3028                return filter;
3029            }
3030            return null;
3031        }
3032
3033        public boolean matches(StatusBarNotification sbn) {
3034            return zen ? true : sbn != null
3035                    && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
3036        }
3037
3038        public boolean matches(ComponentName component) {
3039            return zen ? true : component != null && matches(component.getPackageName());
3040        }
3041
3042        public boolean matches(String pkg) {
3043            return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
3044        }
3045
3046        @Override
3047        public String toString() {
3048            return zen ? "zen" : ('\'' + pkgFilter + '\'');
3049        }
3050    }
3051
3052    /**
3053     * Wrapper for a StatusBarNotification object that allows transfer across a oneway
3054     * binder without sending large amounts of data over a oneway transaction.
3055     */
3056    private static final class StatusBarNotificationHolder
3057            extends IStatusBarNotificationHolder.Stub {
3058        private StatusBarNotification mValue;
3059
3060        public StatusBarNotificationHolder(StatusBarNotification value) {
3061            mValue = value;
3062        }
3063
3064        /** Get the held value and clear it. This function should only be called once per holder */
3065        @Override
3066        public StatusBarNotification get() {
3067            StatusBarNotification value = mValue;
3068            mValue = null;
3069            return value;
3070        }
3071    }
3072}
3073