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