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