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