NotificationManagerService.java revision 3929232cf9d17f81e4fc31004fef3deaa9096826
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                int audioStreamType;
1765                if (notification.audioStreamType >= 0) {
1766                    audioStreamType = notification.audioStreamType;
1767                } else {
1768                    audioStreamType = DEFAULT_STREAM_TYPE;
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(audioStreamType) != 0)
1774                        && !mAudioManager.isAudioFocusExclusive()) {
1775                    final long identity = Binder.clearCallingIdentity();
1776                    try {
1777                        final IRingtonePlayer player =
1778                                mAudioManager.getRingtonePlayer();
1779                        if (player != null) {
1780                            if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1781                                    + " on stream " + audioStreamType);
1782                            player.playAsync(soundUri, record.sbn.getUser(), looping,
1783                                    audioStreamType);
1784                            buzzBeepBlinked = true;
1785                        }
1786                    } catch (RemoteException e) {
1787                    } finally {
1788                        Binder.restoreCallingIdentity(identity);
1789                    }
1790                }
1791            }
1792
1793            // vibrate
1794            // Does the notification want to specify its own vibration?
1795            final boolean hasCustomVibrate = notification.vibrate != null;
1796
1797            // new in 4.2: if there was supposed to be a sound and we're in vibrate
1798            // mode, and no other vibration is specified, we fall back to vibration
1799            final boolean convertSoundToVibration =
1800                       !hasCustomVibrate
1801                    && hasValidSound
1802                    && (mAudioManager.getRingerMode()
1803                               == AudioManager.RINGER_MODE_VIBRATE);
1804
1805            // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1806            final boolean useDefaultVibrate =
1807                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1808
1809            if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1810                    && !(mAudioManager.getRingerMode()
1811                            == AudioManager.RINGER_MODE_SILENT)) {
1812                mVibrateNotification = record;
1813
1814                if (useDefaultVibrate || convertSoundToVibration) {
1815                    // Escalate privileges so we can use the vibrator even if the
1816                    // notifying app does not have the VIBRATE permission.
1817                    long identity = Binder.clearCallingIdentity();
1818                    try {
1819                        mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1820                            useDefaultVibrate ? mDefaultVibrationPattern
1821                                : mFallbackVibrationPattern,
1822                            ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1823                                ? 0: -1, audioAttributesForNotification(notification));
1824                        buzzBeepBlinked = true;
1825                    } finally {
1826                        Binder.restoreCallingIdentity(identity);
1827                    }
1828                } else if (notification.vibrate.length > 1) {
1829                    // If you want your own vibration pattern, you need the VIBRATE
1830                    // permission
1831                    mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1832                            notification.vibrate,
1833                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1834                                ? 0: -1, audioAttributesForNotification(notification));
1835                    buzzBeepBlinked = true;
1836                }
1837            }
1838        }
1839
1840        // light
1841        // release the light
1842        boolean wasShowLights = mLights.remove(record.getKey());
1843        if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) {
1844            mLedNotification = null;
1845        }
1846        if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) {
1847            mLights.add(record.getKey());
1848            updateLightsLocked();
1849            if (mUseAttentionLight) {
1850                mAttentionLight.pulse();
1851            }
1852            buzzBeepBlinked = true;
1853        } else if (wasShowLights) {
1854            updateLightsLocked();
1855        }
1856        if (buzzBeepBlinked) {
1857            mHandler.post(mBuzzBeepBlinked);
1858        }
1859    }
1860
1861    private static AudioAttributes audioAttributesForNotification(Notification n) {
1862        if (n.audioAttributes != null
1863                && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
1864            return n.audioAttributes;
1865        }
1866        return new AudioAttributes.Builder()
1867                .setLegacyStreamType(n.audioStreamType)
1868                .setUsage(AudioAttributes.usageForLegacyStreamType(n.audioStreamType))
1869                .build();
1870    }
1871
1872    void showNextToastLocked() {
1873        ToastRecord record = mToastQueue.get(0);
1874        while (record != null) {
1875            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1876            try {
1877                record.callback.show();
1878                scheduleTimeoutLocked(record);
1879                return;
1880            } catch (RemoteException e) {
1881                Slog.w(TAG, "Object died trying to show notification " + record.callback
1882                        + " in package " + record.pkg);
1883                // remove it from the list and let the process die
1884                int index = mToastQueue.indexOf(record);
1885                if (index >= 0) {
1886                    mToastQueue.remove(index);
1887                }
1888                keepProcessAliveLocked(record.pid);
1889                if (mToastQueue.size() > 0) {
1890                    record = mToastQueue.get(0);
1891                } else {
1892                    record = null;
1893                }
1894            }
1895        }
1896    }
1897
1898    void cancelToastLocked(int index) {
1899        ToastRecord record = mToastQueue.get(index);
1900        try {
1901            record.callback.hide();
1902        } catch (RemoteException e) {
1903            Slog.w(TAG, "Object died trying to hide notification " + record.callback
1904                    + " in package " + record.pkg);
1905            // don't worry about this, we're about to remove it from
1906            // the list anyway
1907        }
1908        mToastQueue.remove(index);
1909        keepProcessAliveLocked(record.pid);
1910        if (mToastQueue.size() > 0) {
1911            // Show the next one. If the callback fails, this will remove
1912            // it from the list, so don't assume that the list hasn't changed
1913            // after this point.
1914            showNextToastLocked();
1915        }
1916    }
1917
1918    private void scheduleTimeoutLocked(ToastRecord r)
1919    {
1920        mHandler.removeCallbacksAndMessages(r);
1921        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1922        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1923        mHandler.sendMessageDelayed(m, delay);
1924    }
1925
1926    private void handleTimeout(ToastRecord record)
1927    {
1928        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1929        synchronized (mToastQueue) {
1930            int index = indexOfToastLocked(record.pkg, record.callback);
1931            if (index >= 0) {
1932                cancelToastLocked(index);
1933            }
1934        }
1935    }
1936
1937    // lock on mToastQueue
1938    int indexOfToastLocked(String pkg, ITransientNotification callback)
1939    {
1940        IBinder cbak = callback.asBinder();
1941        ArrayList<ToastRecord> list = mToastQueue;
1942        int len = list.size();
1943        for (int i=0; i<len; i++) {
1944            ToastRecord r = list.get(i);
1945            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1946                return i;
1947            }
1948        }
1949        return -1;
1950    }
1951
1952    // lock on mToastQueue
1953    void keepProcessAliveLocked(int pid)
1954    {
1955        int toastCount = 0; // toasts from this pid
1956        ArrayList<ToastRecord> list = mToastQueue;
1957        int N = list.size();
1958        for (int i=0; i<N; i++) {
1959            ToastRecord r = list.get(i);
1960            if (r.pid == pid) {
1961                toastCount++;
1962            }
1963        }
1964        try {
1965            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1966        } catch (RemoteException e) {
1967            // Shouldn't happen.
1968        }
1969    }
1970
1971    private void handleRankingReconsideration(Message message) {
1972        if (!(message.obj instanceof RankingReconsideration)) return;
1973        RankingReconsideration recon = (RankingReconsideration) message.obj;
1974        recon.run();
1975        boolean changed;
1976        synchronized (mNotificationList) {
1977            final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
1978            if (record == null) {
1979                return;
1980            }
1981            int indexBefore = findNotificationRecordIndexLocked(record);
1982            boolean interceptBefore = record.isIntercepted();
1983            recon.applyChangesLocked(record);
1984            applyZenModeLocked(record);
1985            mRankingHelper.sort(mNotificationList);
1986            int indexAfter = findNotificationRecordIndexLocked(record);
1987            boolean interceptAfter = record.isIntercepted();
1988            changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
1989            if (interceptBefore && !interceptAfter) {
1990                buzzBeepBlinkLocked(record);
1991            }
1992        }
1993        if (changed) {
1994            scheduleSendRankingUpdate();
1995        }
1996    }
1997
1998    private void handleRankingConfigChange() {
1999        synchronized (mNotificationList) {
2000            final int N = mNotificationList.size();
2001            ArrayList<String> orderBefore = new ArrayList<String>(N);
2002            for (int i = 0; i < N; i++) {
2003                final NotificationRecord r = mNotificationList.get(i);
2004                orderBefore.add(r.getKey());
2005                mRankingHelper.extractSignals(r);
2006            }
2007            mRankingHelper.sort(mNotificationList);
2008            for (int i = 0; i < N; i++) {
2009                if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) {
2010                    scheduleSendRankingUpdate();
2011                    return;
2012                }
2013            }
2014        }
2015    }
2016
2017    // let zen mode evaluate this record
2018    private void applyZenModeLocked(NotificationRecord record) {
2019        record.setIntercepted(mZenModeHelper.shouldIntercept(record));
2020    }
2021
2022    // lock on mNotificationList
2023    private int findNotificationRecordIndexLocked(NotificationRecord target) {
2024        return mRankingHelper.indexOf(mNotificationList, target);
2025    }
2026
2027    private void scheduleSendRankingUpdate() {
2028        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2029        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2030        mHandler.sendMessage(m);
2031    }
2032
2033    private void handleSendRankingUpdate() {
2034        synchronized (mNotificationList) {
2035            mListeners.notifyRankingUpdateLocked();
2036        }
2037    }
2038
2039    private void scheduleListenerHintsChanged(int state) {
2040        mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2041        mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
2042    }
2043
2044    private void handleListenerHintsChanged(int hints) {
2045        synchronized (mNotificationList) {
2046            mListeners.notifyListenerHintsChangedLocked(hints);
2047        }
2048    }
2049
2050    private final class WorkerHandler extends Handler
2051    {
2052        @Override
2053        public void handleMessage(Message msg)
2054        {
2055            switch (msg.what)
2056            {
2057                case MESSAGE_TIMEOUT:
2058                    handleTimeout((ToastRecord)msg.obj);
2059                    break;
2060                case MESSAGE_SAVE_POLICY_FILE:
2061                    handleSavePolicyFile();
2062                    break;
2063                case MESSAGE_SEND_RANKING_UPDATE:
2064                    handleSendRankingUpdate();
2065                    break;
2066                case MESSAGE_LISTENER_HINTS_CHANGED:
2067                    handleListenerHintsChanged(msg.arg1);
2068                    break;
2069            }
2070        }
2071
2072    }
2073
2074    private final class RankingWorkerHandler extends Handler
2075    {
2076        public RankingWorkerHandler(Looper looper) {
2077            super(looper);
2078        }
2079
2080        @Override
2081        public void handleMessage(Message msg) {
2082            switch (msg.what) {
2083                case MESSAGE_RECONSIDER_RANKING:
2084                    handleRankingReconsideration(msg);
2085                    break;
2086                case MESSAGE_RANKING_CONFIG_CHANGE:
2087                    handleRankingConfigChange();
2088                    break;
2089            }
2090        }
2091    }
2092
2093    // Notifications
2094    // ============================================================================
2095    static int clamp(int x, int low, int high) {
2096        return (x < low) ? low : ((x > high) ? high : x);
2097    }
2098
2099    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2100        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2101        if (!manager.isEnabled()) {
2102            return;
2103        }
2104
2105        AccessibilityEvent event =
2106            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2107        event.setPackageName(packageName);
2108        event.setClassName(Notification.class.getName());
2109        event.setParcelableData(notification);
2110        CharSequence tickerText = notification.tickerText;
2111        if (!TextUtils.isEmpty(tickerText)) {
2112            event.getText().add(tickerText);
2113        }
2114
2115        manager.sendAccessibilityEvent(event);
2116    }
2117
2118    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2119        // tell the app
2120        if (sendDelete) {
2121            if (r.getNotification().deleteIntent != null) {
2122                try {
2123                    r.getNotification().deleteIntent.send();
2124                } catch (PendingIntent.CanceledException ex) {
2125                    // do nothing - there's no relevant way to recover, and
2126                    //     no reason to let this propagate
2127                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2128                }
2129            }
2130        }
2131
2132        // status bar
2133        if (r.getNotification().icon != 0) {
2134            r.isCanceled = true;
2135            mListeners.notifyRemovedLocked(r.sbn);
2136        }
2137
2138        // sound
2139        if (mSoundNotification == r) {
2140            mSoundNotification = null;
2141            final long identity = Binder.clearCallingIdentity();
2142            try {
2143                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2144                if (player != null) {
2145                    player.stopAsync();
2146                }
2147            } catch (RemoteException e) {
2148            } finally {
2149                Binder.restoreCallingIdentity(identity);
2150            }
2151        }
2152
2153        // vibrate
2154        if (mVibrateNotification == r) {
2155            mVibrateNotification = null;
2156            long identity = Binder.clearCallingIdentity();
2157            try {
2158                mVibrator.cancel();
2159            }
2160            finally {
2161                Binder.restoreCallingIdentity(identity);
2162            }
2163        }
2164
2165        // light
2166        mLights.remove(r.getKey());
2167        if (mLedNotification == r) {
2168            mLedNotification = null;
2169        }
2170
2171        // Record usage stats
2172        switch (reason) {
2173            case REASON_DELEGATE_CANCEL:
2174            case REASON_DELEGATE_CANCEL_ALL:
2175            case REASON_LISTENER_CANCEL:
2176            case REASON_LISTENER_CANCEL_ALL:
2177                mUsageStats.registerDismissedByUser(r);
2178                break;
2179            case REASON_NOMAN_CANCEL:
2180            case REASON_NOMAN_CANCEL_ALL:
2181                mUsageStats.registerRemovedByApp(r);
2182                break;
2183            case REASON_DELEGATE_CLICK:
2184                mUsageStats.registerCancelDueToClick(r);
2185                break;
2186            default:
2187                mUsageStats.registerCancelUnknown(r);
2188                break;
2189        }
2190
2191        mNotificationsByKey.remove(r.sbn.getKey());
2192
2193        // Save it for users of getHistoricalNotifications()
2194        mArchive.record(r.sbn);
2195    }
2196
2197    /**
2198     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2199     * and none of the {@code mustNotHaveFlags}.
2200     */
2201    void cancelNotification(final int callingUid, final int callingPid,
2202            final String pkg, final String tag, final int id,
2203            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2204            final int userId, final int reason, final ManagedServiceInfo listener) {
2205        // In enqueueNotificationInternal notifications are added by scheduling the
2206        // work on the worker handler. Hence, we also schedule the cancel on this
2207        // handler to avoid a scenario where an add notification call followed by a
2208        // remove notification call ends up in not removing the notification.
2209        mHandler.post(new Runnable() {
2210            @Override
2211            public void run() {
2212                String listenerName = listener == null ? null : listener.component.toShortString();
2213                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2214                        mustHaveFlags, mustNotHaveFlags, reason, listenerName);
2215
2216                synchronized (mNotificationList) {
2217                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2218                    if (index >= 0) {
2219                        NotificationRecord r = mNotificationList.get(index);
2220
2221                        // Ideally we'd do this in the caller of this method. However, that would
2222                        // require the caller to also find the notification.
2223                        if (reason == REASON_DELEGATE_CLICK) {
2224                            mUsageStats.registerClickedByUser(r);
2225                        }
2226
2227                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2228                            return;
2229                        }
2230                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2231                            return;
2232                        }
2233
2234                        mNotificationList.remove(index);
2235
2236                        cancelNotificationLocked(r, sendDelete, reason);
2237                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName);
2238                        updateLightsLocked();
2239                    }
2240                }
2241            }
2242        });
2243    }
2244
2245    /**
2246     * Determine whether the userId applies to the notification in question, either because
2247     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2248     */
2249    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2250        return
2251                // looking for USER_ALL notifications? match everything
2252                   userId == UserHandle.USER_ALL
2253                // a notification sent to USER_ALL matches any query
2254                || r.getUserId() == UserHandle.USER_ALL
2255                // an exact user match
2256                || r.getUserId() == userId;
2257    }
2258
2259    /**
2260     * Determine whether the userId applies to the notification in question, either because
2261     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2262     * because it matches one of the users profiles.
2263     */
2264    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2265        return notificationMatchesUserId(r, userId)
2266                || mUserProfiles.isCurrentProfile(r.getUserId());
2267    }
2268
2269    /**
2270     * Cancels all notifications from a given package that have all of the
2271     * {@code mustHaveFlags}.
2272     */
2273    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2274            int mustNotHaveFlags, boolean doit, int userId, int reason,
2275            ManagedServiceInfo listener) {
2276        String listenerName = listener == null ? null : listener.component.toShortString();
2277        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2278                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2279                listenerName);
2280
2281        synchronized (mNotificationList) {
2282            final int N = mNotificationList.size();
2283            ArrayList<NotificationRecord> canceledNotifications = null;
2284            for (int i = N-1; i >= 0; --i) {
2285                NotificationRecord r = mNotificationList.get(i);
2286                if (!notificationMatchesUserId(r, userId)) {
2287                    continue;
2288                }
2289                // Don't remove notifications to all, if there's no package name specified
2290                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2291                    continue;
2292                }
2293                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2294                    continue;
2295                }
2296                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2297                    continue;
2298                }
2299                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2300                    continue;
2301                }
2302                if (canceledNotifications == null) {
2303                    canceledNotifications = new ArrayList<>();
2304                }
2305                canceledNotifications.add(r);
2306                if (!doit) {
2307                    return true;
2308                }
2309                mNotificationList.remove(i);
2310                cancelNotificationLocked(r, false, reason);
2311            }
2312            if (doit && canceledNotifications != null) {
2313                final int M = canceledNotifications.size();
2314                for (int i = 0; i < M; i++) {
2315                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2316                            listenerName);
2317                }
2318            }
2319            if (canceledNotifications != null) {
2320                updateLightsLocked();
2321            }
2322            return canceledNotifications != null;
2323        }
2324    }
2325
2326    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2327            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2328        String listenerName = listener == null ? null : listener.component.toShortString();
2329        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2330                null, userId, 0, 0, reason, listenerName);
2331
2332        ArrayList<NotificationRecord> canceledNotifications = null;
2333        final int N = mNotificationList.size();
2334        for (int i=N-1; i>=0; i--) {
2335            NotificationRecord r = mNotificationList.get(i);
2336            if (includeCurrentProfiles) {
2337                if (!notificationMatchesCurrentProfiles(r, userId)) {
2338                    continue;
2339                }
2340            } else {
2341                if (!notificationMatchesUserId(r, userId)) {
2342                    continue;
2343                }
2344            }
2345
2346            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2347                            | Notification.FLAG_NO_CLEAR)) == 0) {
2348                mNotificationList.remove(i);
2349                cancelNotificationLocked(r, true, reason);
2350                // Make a note so we can cancel children later.
2351                if (canceledNotifications == null) {
2352                    canceledNotifications = new ArrayList<>();
2353                }
2354                canceledNotifications.add(r);
2355            }
2356        }
2357        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
2358        for (int i = 0; i < M; i++) {
2359            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2360                    listenerName);
2361        }
2362        updateLightsLocked();
2363    }
2364
2365    // Warning: The caller is responsible for invoking updateLightsLocked().
2366    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
2367            String listenerName) {
2368        Notification n = r.getNotification();
2369        if (!n.isGroupSummary()) {
2370            return;
2371        }
2372
2373        String pkg = r.sbn.getPackageName();
2374        int userId = r.getUserId();
2375
2376        if (pkg == null) {
2377            if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
2378            return;
2379        }
2380
2381        final int N = mNotificationList.size();
2382        for (int i = N - 1; i >= 0; i--) {
2383            NotificationRecord childR = mNotificationList.get(i);
2384            Notification childN = childR.getNotification();
2385            StatusBarNotification childSbn = childR.sbn;
2386            if (childR.getUserId() == userId && pkg.equals(childSbn.getPackageName()) &&
2387                    n.getGroup().equals(childN.getGroup())) {
2388                EventLogTags.writeNotificationCancel(callingUid, callingPid,
2389                        pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0,
2390                        REASON_GROUP_SUMMARY_CANCELED, listenerName);
2391                mNotificationList.remove(i);
2392                cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);
2393            }
2394        }
2395    }
2396
2397    // lock on mNotificationList
2398    void updateLightsLocked()
2399    {
2400        // handle notification lights
2401        if (mLedNotification == null) {
2402            // get next notification, if any
2403            int n = mLights.size();
2404            if (n > 0) {
2405                mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
2406            }
2407        }
2408
2409        // Don't flash while we are in a call or screen is on
2410        if (mLedNotification == null || mInCall || mScreenOn) {
2411            mNotificationLight.turnOff();
2412            mStatusBar.notificationLightOff();
2413        } else {
2414            final Notification ledno = mLedNotification.sbn.getNotification();
2415            int ledARGB = ledno.ledARGB;
2416            int ledOnMS = ledno.ledOnMS;
2417            int ledOffMS = ledno.ledOffMS;
2418            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2419                ledARGB = mDefaultNotificationColor;
2420                ledOnMS = mDefaultNotificationLedOn;
2421                ledOffMS = mDefaultNotificationLedOff;
2422            }
2423            if (mNotificationPulseEnabled) {
2424                // pulse repeatedly
2425                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2426                        ledOnMS, ledOffMS);
2427            }
2428            // let SystemUI make an independent decision
2429            mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
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