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