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