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