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