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