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