NotificationManagerService.java revision c8d7ab9ff9f1ca0790d6fd9b9a2076c3eb63a795
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,
606                String pkg, String tag, int id, int userId) {
607            cancelNotification(callingUid, callingPid, pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
608                    Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_DELEGATE_CLICK, null);
609        }
610
611        @Override
612        public void onNotificationClear(int callingUid, int callingPid,
613                String pkg, String tag, int id, int userId) {
614            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
615                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
616                    true, userId, REASON_DELEGATE_CANCEL, null);
617        }
618
619        @Override
620        public void onPanelRevealed() {
621            EventLogTags.writeNotificationPanelRevealed();
622            synchronized (mNotificationList) {
623                // sound
624                mSoundNotification = null;
625
626                long identity = Binder.clearCallingIdentity();
627                try {
628                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
629                    if (player != null) {
630                        player.stopAsync();
631                    }
632                } catch (RemoteException e) {
633                } finally {
634                    Binder.restoreCallingIdentity(identity);
635                }
636
637                // vibrate
638                mVibrateNotification = null;
639                identity = Binder.clearCallingIdentity();
640                try {
641                    mVibrator.cancel();
642                } finally {
643                    Binder.restoreCallingIdentity(identity);
644                }
645
646                // light
647                mLights.clear();
648                mLedNotification = null;
649                updateLightsLocked();
650            }
651        }
652
653        @Override
654        public void onPanelHidden() {
655            EventLogTags.writeNotificationPanelHidden();
656        }
657
658        @Override
659        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
660                int uid, int initialPid, String message, int userId) {
661            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
662                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
663            cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
664                    REASON_DELEGATE_ERROR, null);
665            long ident = Binder.clearCallingIdentity();
666            try {
667                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
668                        "Bad notification posted from package " + pkg
669                        + ": " + message);
670            } catch (RemoteException e) {
671            }
672            Binder.restoreCallingIdentity(ident);
673        }
674
675        @Override
676        public boolean allowDisable(int what, IBinder token, String pkg) {
677            return mZenModeHelper.allowDisable(what, token, pkg);
678        }
679
680        @Override
681        public void onNotificationVisibilityChanged(
682                String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
683            // Using ';' as separator since eventlogs uses ',' to separate
684            // args.
685            EventLogTags.writeNotificationVisibilityChanged(
686                    TextUtils.join(";", newlyVisibleKeys),
687                    TextUtils.join(";", noLongerVisibleKeys));
688        }
689    };
690
691    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
692        @Override
693        public void onReceive(Context context, Intent intent) {
694            String action = intent.getAction();
695
696            boolean queryRestart = false;
697            boolean queryRemove = false;
698            boolean packageChanged = false;
699            boolean cancelNotifications = true;
700
701            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
702                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
703                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
704                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
705                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
706                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
707                String pkgList[] = null;
708                boolean queryReplace = queryRemove &&
709                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
710                if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
711                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
712                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
713                } else if (queryRestart) {
714                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
715                } else {
716                    Uri uri = intent.getData();
717                    if (uri == null) {
718                        return;
719                    }
720                    String pkgName = uri.getSchemeSpecificPart();
721                    if (pkgName == null) {
722                        return;
723                    }
724                    if (packageChanged) {
725                        // We cancel notifications for packages which have just been disabled
726                        try {
727                            final int enabled = getContext().getPackageManager()
728                                    .getApplicationEnabledSetting(pkgName);
729                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
730                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
731                                cancelNotifications = false;
732                            }
733                        } catch (IllegalArgumentException e) {
734                            // Package doesn't exist; probably racing with uninstall.
735                            // cancelNotifications is already true, so nothing to do here.
736                            if (DBG) {
737                                Slog.i(TAG, "Exception trying to look up app enabled setting", e);
738                            }
739                        }
740                    }
741                    pkgList = new String[]{pkgName};
742                }
743
744                if (pkgList != null && (pkgList.length > 0)) {
745                    for (String pkgName : pkgList) {
746                        if (cancelNotifications) {
747                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
748                                    UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null);
749                        }
750                    }
751                }
752                mListeners.onPackagesChanged(queryReplace, pkgList);
753                mConditionProviders.onPackagesChanged(queryReplace, pkgList);
754            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
755                // Keep track of screen on/off state, but do not turn off the notification light
756                // until user passes through the lock screen or views the notification.
757                mScreenOn = true;
758            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
759                mScreenOn = false;
760            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
761                mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
762                        .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
763                updateNotificationPulse();
764            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
765                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
766                if (userHandle >= 0) {
767                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
768                            REASON_USER_STOPPED, null);
769                }
770            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
771                // turn off LED when user passes through lock screen
772                mNotificationLight.turnOff();
773            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
774                // reload per-user settings
775                mSettingsObserver.update(null);
776                mUserProfiles.updateCache(context);
777            } else if (action.equals(Intent.ACTION_USER_ADDED)) {
778                mUserProfiles.updateCache(context);
779            }
780        }
781    };
782
783    class SettingsObserver extends ContentObserver {
784        private final Uri NOTIFICATION_LIGHT_PULSE_URI
785                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
786
787        SettingsObserver(Handler handler) {
788            super(handler);
789        }
790
791        void observe() {
792            ContentResolver resolver = getContext().getContentResolver();
793            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
794                    false, this, UserHandle.USER_ALL);
795            update(null);
796        }
797
798        @Override public void onChange(boolean selfChange, Uri uri) {
799            update(uri);
800        }
801
802        public void update(Uri uri) {
803            ContentResolver resolver = getContext().getContentResolver();
804            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
805                boolean pulseEnabled = Settings.System.getInt(resolver,
806                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
807                if (mNotificationPulseEnabled != pulseEnabled) {
808                    mNotificationPulseEnabled = pulseEnabled;
809                    updateNotificationPulse();
810                }
811            }
812        }
813    }
814
815    private SettingsObserver mSettingsObserver;
816    private ZenModeHelper mZenModeHelper;
817
818    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
819        int[] ar = r.getIntArray(resid);
820        if (ar == null) {
821            return def;
822        }
823        final int len = ar.length > maxlen ? maxlen : ar.length;
824        long[] out = new long[len];
825        for (int i=0; i<len; i++) {
826            out[i] = ar[i];
827        }
828        return out;
829    }
830
831    public NotificationManagerService(Context context) {
832        super(context);
833    }
834
835    @Override
836    public void onStart() {
837        mAm = ActivityManagerNative.getDefault();
838        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
839        mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
840
841        mHandler = new WorkerHandler();
842        mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
843        mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
844            @Override
845            public void onConfigChanged() {
846                savePolicyFile();
847            }
848        });
849        final File systemDir = new File(Environment.getDataDirectory(), "system");
850        mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
851
852        importOldBlockDb();
853
854        mListeners = new NotificationListeners();
855        mConditionProviders = new ConditionProviders(getContext(),
856                mHandler, mUserProfiles, mZenModeHelper);
857        mStatusBar = getLocalService(StatusBarManagerInternal.class);
858        mStatusBar.setNotificationDelegate(mNotificationDelegate);
859
860        final LightsManager lights = getLocalService(LightsManager.class);
861        mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
862        mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
863
864        Resources resources = getContext().getResources();
865        mDefaultNotificationColor = resources.getColor(
866                R.color.config_defaultNotificationColor);
867        mDefaultNotificationLedOn = resources.getInteger(
868                R.integer.config_defaultNotificationLedOn);
869        mDefaultNotificationLedOff = resources.getInteger(
870                R.integer.config_defaultNotificationLedOff);
871
872        mDefaultVibrationPattern = getLongArray(resources,
873                R.array.config_defaultNotificationVibePattern,
874                VIBRATE_PATTERN_MAXLEN,
875                DEFAULT_VIBRATE_PATTERN);
876
877        mFallbackVibrationPattern = getLongArray(resources,
878                R.array.config_notificationFallbackVibePattern,
879                VIBRATE_PATTERN_MAXLEN,
880                DEFAULT_VIBRATE_PATTERN);
881
882        // Don't start allowing notifications until the setup wizard has run once.
883        // After that, including subsequent boots, init with notifications turned on.
884        // This works on the first boot because the setup wizard will toggle this
885        // flag at least once and we'll go back to 0 after that.
886        if (0 == Settings.Global.getInt(getContext().getContentResolver(),
887                    Settings.Global.DEVICE_PROVISIONED, 0)) {
888            mDisableNotificationAlerts = true;
889        }
890        mZenModeHelper.updateZenMode();
891
892        mUserProfiles.updateCache(getContext());
893
894        // register for various Intents
895        IntentFilter filter = new IntentFilter();
896        filter.addAction(Intent.ACTION_SCREEN_ON);
897        filter.addAction(Intent.ACTION_SCREEN_OFF);
898        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
899        filter.addAction(Intent.ACTION_USER_PRESENT);
900        filter.addAction(Intent.ACTION_USER_STOPPED);
901        filter.addAction(Intent.ACTION_USER_SWITCHED);
902        filter.addAction(Intent.ACTION_USER_ADDED);
903        getContext().registerReceiver(mIntentReceiver, filter);
904        IntentFilter pkgFilter = new IntentFilter();
905        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
906        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
907        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
908        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
909        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
910        pkgFilter.addDataScheme("package");
911        getContext().registerReceiver(mIntentReceiver, pkgFilter);
912        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
913        getContext().registerReceiver(mIntentReceiver, sdFilter);
914
915        mSettingsObserver = new SettingsObserver(mHandler);
916
917        // spin up NotificationScorers
918        String[] notificationScorerNames = resources.getStringArray(
919                R.array.config_notificationScorers);
920        for (String scorerName : notificationScorerNames) {
921            try {
922                Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName);
923                NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance();
924                scorer.initialize(getContext());
925                mScorers.add(scorer);
926            } catch (ClassNotFoundException e) {
927                Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e);
928            } catch (InstantiationException e) {
929                Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e);
930            } catch (IllegalAccessException e) {
931                Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e);
932            }
933        }
934
935        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
936        publishLocalService(NotificationManagerInternal.class, mInternalService);
937    }
938
939    /**
940     * Read the old XML-based app block database and import those blockages into the AppOps system.
941     */
942    private void importOldBlockDb() {
943        loadPolicyFile();
944
945        PackageManager pm = getContext().getPackageManager();
946        for (String pkg : mBlockedPackages) {
947            PackageInfo info = null;
948            try {
949                info = pm.getPackageInfo(pkg, 0);
950                setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
951            } catch (NameNotFoundException e) {
952                // forget you
953            }
954        }
955        mBlockedPackages.clear();
956    }
957
958    @Override
959    public void onBootPhase(int phase) {
960        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
961            // no beeping until we're basically done booting
962            mSystemReady = true;
963
964            // Grab our optional AudioService
965            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
966
967        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
968            // This observer will force an update when observe is called, causing us to
969            // bind to listener services.
970            mSettingsObserver.observe();
971            mListeners.onBootPhaseAppsCanStart();
972            mConditionProviders.onBootPhaseAppsCanStart();
973        }
974    }
975
976    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
977        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
978
979        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
980                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
981
982        // Now, cancel any outstanding notifications that are part of a just-disabled app
983        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
984            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
985                    REASON_PACKAGE_BANNED, null);
986        }
987    }
988
989    private final IBinder mService = new INotificationManager.Stub() {
990        // Toasts
991        // ============================================================================
992
993        @Override
994        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
995        {
996            if (DBG) {
997                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
998                        + " duration=" + duration);
999            }
1000
1001            if (pkg == null || callback == null) {
1002                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1003                return ;
1004            }
1005
1006            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1007
1008            if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1009                if (!isSystemToast) {
1010                    Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1011                    return;
1012                }
1013            }
1014
1015            synchronized (mToastQueue) {
1016                int callingPid = Binder.getCallingPid();
1017                long callingId = Binder.clearCallingIdentity();
1018                try {
1019                    ToastRecord record;
1020                    int index = indexOfToastLocked(pkg, callback);
1021                    // If it's already in the queue, we update it in place, we don't
1022                    // move it to the end of the queue.
1023                    if (index >= 0) {
1024                        record = mToastQueue.get(index);
1025                        record.update(duration);
1026                    } else {
1027                        // Limit the number of toasts that any given package except the android
1028                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1029                        if (!isSystemToast) {
1030                            int count = 0;
1031                            final int N = mToastQueue.size();
1032                            for (int i=0; i<N; i++) {
1033                                 final ToastRecord r = mToastQueue.get(i);
1034                                 if (r.pkg.equals(pkg)) {
1035                                     count++;
1036                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1037                                         Slog.e(TAG, "Package has already posted " + count
1038                                                + " toasts. Not showing more. Package=" + pkg);
1039                                         return;
1040                                     }
1041                                 }
1042                            }
1043                        }
1044
1045                        record = new ToastRecord(callingPid, pkg, callback, duration);
1046                        mToastQueue.add(record);
1047                        index = mToastQueue.size() - 1;
1048                        keepProcessAliveLocked(callingPid);
1049                    }
1050                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
1051                    // new or just been updated.  Call back and tell it to show itself.
1052                    // If the callback fails, this will remove it from the list, so don't
1053                    // assume that it's valid after this.
1054                    if (index == 0) {
1055                        showNextToastLocked();
1056                    }
1057                } finally {
1058                    Binder.restoreCallingIdentity(callingId);
1059                }
1060            }
1061        }
1062
1063        @Override
1064        public void cancelToast(String pkg, ITransientNotification callback) {
1065            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1066
1067            if (pkg == null || callback == null) {
1068                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1069                return ;
1070            }
1071
1072            synchronized (mToastQueue) {
1073                long callingId = Binder.clearCallingIdentity();
1074                try {
1075                    int index = indexOfToastLocked(pkg, callback);
1076                    if (index >= 0) {
1077                        cancelToastLocked(index);
1078                    } else {
1079                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1080                                + " callback=" + callback);
1081                    }
1082                } finally {
1083                    Binder.restoreCallingIdentity(callingId);
1084                }
1085            }
1086        }
1087
1088        @Override
1089        public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1090                Notification notification, int[] idOut, int userId) throws RemoteException {
1091            enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1092                    Binder.getCallingPid(), tag, id, notification, idOut, userId);
1093        }
1094
1095        @Override
1096        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1097            checkCallerIsSystemOrSameApp(pkg);
1098            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1099                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1100            // Don't allow client applications to cancel foreground service notis.
1101            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1102                    Binder.getCallingUid() == Process.SYSTEM_UID
1103                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1104                    null);
1105        }
1106
1107        @Override
1108        public void cancelAllNotifications(String pkg, int userId) {
1109            checkCallerIsSystemOrSameApp(pkg);
1110
1111            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1112                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1113
1114            // Calling from user space, don't allow the canceling of actively
1115            // running foreground services.
1116            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1117                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1118                    REASON_NOMAN_CANCEL_ALL, null);
1119        }
1120
1121        @Override
1122        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1123            checkCallerIsSystem();
1124
1125            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1126        }
1127
1128        /**
1129         * Use this when you just want to know if notifications are OK for this package.
1130         */
1131        @Override
1132        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1133            checkCallerIsSystem();
1134            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1135                    == AppOpsManager.MODE_ALLOWED);
1136        }
1137
1138        /**
1139         * System-only API for getting a list of current (i.e. not cleared) notifications.
1140         *
1141         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1142         */
1143        @Override
1144        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1145            // enforce() will ensure the calling uid has the correct permission
1146            getContext().enforceCallingOrSelfPermission(
1147                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1148                    "NotificationManagerService.getActiveNotifications");
1149
1150            StatusBarNotification[] tmp = null;
1151            int uid = Binder.getCallingUid();
1152
1153            // noteOp will check to make sure the callingPkg matches the uid
1154            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1155                    == AppOpsManager.MODE_ALLOWED) {
1156                synchronized (mNotificationList) {
1157                    tmp = new StatusBarNotification[mNotificationList.size()];
1158                    final int N = mNotificationList.size();
1159                    for (int i=0; i<N; i++) {
1160                        tmp[i] = mNotificationList.get(i).sbn;
1161                    }
1162                }
1163            }
1164            return tmp;
1165        }
1166
1167        /**
1168         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1169         *
1170         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1171         */
1172        @Override
1173        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1174            // enforce() will ensure the calling uid has the correct permission
1175            getContext().enforceCallingOrSelfPermission(
1176                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1177                    "NotificationManagerService.getHistoricalNotifications");
1178
1179            StatusBarNotification[] tmp = null;
1180            int uid = Binder.getCallingUid();
1181
1182            // noteOp will check to make sure the callingPkg matches the uid
1183            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1184                    == AppOpsManager.MODE_ALLOWED) {
1185                synchronized (mArchive) {
1186                    tmp = mArchive.getArray(count);
1187                }
1188            }
1189            return tmp;
1190        }
1191
1192        /**
1193         * Register a listener binder directly with the notification manager.
1194         *
1195         * Only works with system callers. Apps should extend
1196         * {@link android.service.notification.NotificationListenerService}.
1197         */
1198        @Override
1199        public void registerListener(final INotificationListener listener,
1200                final ComponentName component, final int userid) {
1201            checkCallerIsSystem();
1202            mListeners.registerService(listener, component, userid);
1203        }
1204
1205        /**
1206         * Remove a listener binder directly
1207         */
1208        @Override
1209        public void unregisterListener(INotificationListener listener, int userid) {
1210            mListeners.unregisterService(listener, userid);
1211        }
1212
1213        /**
1214         * Allow an INotificationListener to simulate a "clear all" operation.
1215         *
1216         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1217         *
1218         * @param token The binder for the listener, to check that the caller is allowed
1219         */
1220        @Override
1221        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1222            final int callingUid = Binder.getCallingUid();
1223            final int callingPid = Binder.getCallingPid();
1224            long identity = Binder.clearCallingIdentity();
1225            try {
1226                synchronized (mNotificationList) {
1227                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1228                    if (keys != null) {
1229                        final int N = keys.length;
1230                        for (int i = 0; i < N; i++) {
1231                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1232                            final int userId = r.sbn.getUserId();
1233                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
1234                                    !mUserProfiles.isCurrentProfile(userId)) {
1235                                throw new SecurityException("Disallowed call from listener: "
1236                                        + info.service);
1237                            }
1238                            if (r != null) {
1239                                cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1240                                        r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1241                                        userId);
1242                            }
1243                        }
1244                    } else {
1245                        cancelAllLocked(callingUid, callingPid, info.userid,
1246                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1247                    }
1248                }
1249            } finally {
1250                Binder.restoreCallingIdentity(identity);
1251            }
1252        }
1253
1254        private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1255                int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1256            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1257                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1258                    true,
1259                    userId, REASON_LISTENER_CANCEL, info);
1260        }
1261
1262        /**
1263         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1264         *
1265         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1266         *
1267         * @param token The binder for the listener, to check that the caller is allowed
1268         */
1269        @Override
1270        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1271                String tag, int id) {
1272            final int callingUid = Binder.getCallingUid();
1273            final int callingPid = Binder.getCallingPid();
1274            long identity = Binder.clearCallingIdentity();
1275            try {
1276                synchronized (mNotificationList) {
1277                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1278                    if (info.supportsProfiles()) {
1279                        Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1280                                + "from " + info.component
1281                                + " use cancelNotification(key) instead.");
1282                    } else {
1283                        cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1284                                pkg, tag, id, info.userid);
1285                    }
1286                }
1287            } finally {
1288                Binder.restoreCallingIdentity(identity);
1289            }
1290        }
1291
1292        /**
1293         * Allow an INotificationListener to request the list of outstanding notifications seen by
1294         * the current user. Useful when starting up, after which point the listener callbacks
1295         * should be used.
1296         *
1297         * @param token The binder for the listener, to check that the caller is allowed
1298         */
1299        @Override
1300        public StatusBarNotification[] getActiveNotificationsFromListener(
1301                INotificationListener token, String[] keys) {
1302            synchronized (mNotificationList) {
1303                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1304                final ArrayList<StatusBarNotification> list
1305                        = new ArrayList<StatusBarNotification>();
1306                if (keys == null) {
1307                    final int N = mNotificationList.size();
1308                    for (int i=0; i<N; i++) {
1309                        StatusBarNotification sbn = mNotificationList.get(i).sbn;
1310                        if (info.enabledAndUserMatches(sbn.getUserId())) {
1311                            list.add(sbn);
1312                        }
1313                    }
1314                } else {
1315                    final int N = keys.length;
1316                    for (int i=0; i<N; i++) {
1317                        NotificationRecord r = mNotificationsByKey.get(keys[i]);
1318                        if (r != null && info.enabledAndUserMatches(r.sbn.getUserId())) {
1319                            list.add(r.sbn);
1320                        }
1321                    }
1322                }
1323                return list.toArray(new StatusBarNotification[list.size()]);
1324            }
1325        }
1326
1327        @Override
1328        public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1329            return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
1330        }
1331
1332        @Override
1333        public ZenModeConfig getZenModeConfig() {
1334            checkCallerIsSystem();
1335            return mZenModeHelper.getConfig();
1336        }
1337
1338        @Override
1339        public boolean setZenModeConfig(ZenModeConfig config) {
1340            checkCallerIsSystem();
1341            return mZenModeHelper.setConfig(config);
1342        }
1343
1344        @Override
1345        public void notifyConditions(String pkg, IConditionProvider provider,
1346                Condition[] conditions) {
1347            final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1348            checkCallerIsSystemOrSameApp(pkg);
1349            final long identity = Binder.clearCallingIdentity();
1350            try {
1351                mConditionProviders.notifyConditions(pkg, info, conditions);
1352            } finally {
1353                Binder.restoreCallingIdentity(identity);
1354            }
1355        }
1356
1357        @Override
1358        public void requestZenModeConditions(IConditionListener callback, int relevance) {
1359            enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
1360            mConditionProviders.requestZenModeConditions(callback, relevance);
1361        }
1362
1363        @Override
1364        public void setZenModeCondition(Uri conditionId) {
1365            enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
1366            mConditionProviders.setZenModeCondition(conditionId);
1367        }
1368
1369        @Override
1370        public void setAutomaticZenModeConditions(Uri[] conditionIds) {
1371            enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions");
1372            mConditionProviders.setAutomaticZenModeConditions(conditionIds);
1373        }
1374
1375        @Override
1376        public Condition[] getAutomaticZenModeConditions() {
1377            enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions");
1378            return mConditionProviders.getAutomaticZenModeConditions();
1379        }
1380
1381        private void enforceSystemOrSystemUI(String message) {
1382            if (isCallerSystem()) return;
1383            getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1384                    message);
1385        }
1386
1387        @Override
1388        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1389            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1390                    != PackageManager.PERMISSION_GRANTED) {
1391                pw.println("Permission Denial: can't dump NotificationManager from from pid="
1392                        + Binder.getCallingPid()
1393                        + ", uid=" + Binder.getCallingUid());
1394                return;
1395            }
1396
1397            dumpImpl(pw);
1398        }
1399    };
1400
1401    private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1402        synchronized (mNotificationList) {
1403            final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1404            final ArrayList<String> keys = new ArrayList<String>();
1405            final int N = mNotificationList.size();
1406            for (int i=0; i<N; i++) {
1407                final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1408                if (info.enabledAndUserMatches(sbn.getUserId())) {
1409                    keys.add(sbn.getKey());
1410                }
1411            }
1412            return keys.toArray(new String[keys.size()]);
1413        }
1414    }
1415
1416    void dumpImpl(PrintWriter pw) {
1417        pw.println("Current Notification Manager state:");
1418
1419        int N;
1420
1421        synchronized (mToastQueue) {
1422            N = mToastQueue.size();
1423            if (N > 0) {
1424                pw.println("  Toast Queue:");
1425                for (int i=0; i<N; i++) {
1426                    mToastQueue.get(i).dump(pw, "    ");
1427                }
1428                pw.println("  ");
1429            }
1430
1431        }
1432
1433        synchronized (mNotificationList) {
1434            N = mNotificationList.size();
1435            if (N > 0) {
1436                pw.println("  Notification List:");
1437                for (int i=0; i<N; i++) {
1438                    mNotificationList.get(i).dump(pw, "    ", getContext());
1439                }
1440                pw.println("  ");
1441            }
1442
1443            N = mLights.size();
1444            if (N > 0) {
1445                pw.println("  Lights List:");
1446                for (int i=0; i<N; i++) {
1447                    pw.println("    " + mLights.get(i));
1448                }
1449                pw.println("  ");
1450            }
1451
1452            pw.println("  mSoundNotification=" + mSoundNotification);
1453            pw.println("  mVibrateNotification=" + mVibrateNotification);
1454            pw.println("  mDisableNotificationAlerts=" + mDisableNotificationAlerts);
1455            pw.println("  mSystemReady=" + mSystemReady);
1456            pw.println("  mArchive=" + mArchive.toString());
1457            Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1458            int i=0;
1459            while (iter.hasNext()) {
1460                pw.println("    " + iter.next());
1461                if (++i >= 5) {
1462                    if (iter.hasNext()) pw.println("    ...");
1463                    break;
1464                }
1465            }
1466
1467            pw.println("\n  Usage Stats:");
1468            mUsageStats.dump(pw, "    ");
1469
1470            pw.println("\n  Zen Mode:");
1471            mZenModeHelper.dump(pw, "    ");
1472
1473            pw.println("\n  Notification listeners:");
1474            mListeners.dump(pw);
1475
1476            pw.println("\n  Condition providers:");
1477            mConditionProviders.dump(pw);
1478        }
1479    }
1480
1481    /**
1482     * The private API only accessible to the system process.
1483     */
1484    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1485        @Override
1486        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
1487                String tag, int id, Notification notification, int[] idReceived, int userId) {
1488            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
1489                    idReceived, userId);
1490        }
1491    };
1492
1493    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
1494            final int callingPid, final String tag, final int id, final Notification notification,
1495            int[] idOut, int incomingUserId) {
1496        if (DBG) {
1497            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1498                    + " notification=" + notification);
1499        }
1500        checkCallerIsSystemOrSameApp(pkg);
1501        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
1502
1503        final int userId = ActivityManager.handleIncomingUser(callingPid,
1504                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1505        final UserHandle user = new UserHandle(userId);
1506
1507        // Limit the number of notifications that any given package except the android
1508        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1509        if (!isSystemNotification) {
1510            synchronized (mNotificationList) {
1511                int count = 0;
1512                final int N = mNotificationList.size();
1513                for (int i=0; i<N; i++) {
1514                    final NotificationRecord r = mNotificationList.get(i);
1515                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1516                        count++;
1517                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1518                            Slog.e(TAG, "Package has already posted " + count
1519                                    + " notifications.  Not showing more.  package=" + pkg);
1520                            return;
1521                        }
1522                    }
1523                }
1524            }
1525        }
1526
1527        // This conditional is a dirty hack to limit the logging done on
1528        //     behalf of the download manager without affecting other apps.
1529        if (!pkg.equals("com.android.providers.downloads")
1530                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1531            EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1532                    pkg, id, tag, userId, notification.toString());
1533        }
1534
1535        if (pkg == null || notification == null) {
1536            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1537                    + " id=" + id + " notification=" + notification);
1538        }
1539        if (notification.icon != 0) {
1540            if (notification.contentView == null) {
1541                throw new IllegalArgumentException("contentView required: pkg=" + pkg
1542                        + " id=" + id + " notification=" + notification);
1543            }
1544        }
1545
1546        mHandler.post(new Runnable() {
1547            @Override
1548            public void run() {
1549
1550                // === Scoring ===
1551
1552                // 0. Sanitize inputs
1553                notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1554                        Notification.PRIORITY_MAX);
1555                // Migrate notification flags to scores
1556                if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1557                    if (notification.priority < Notification.PRIORITY_MAX) {
1558                        notification.priority = Notification.PRIORITY_MAX;
1559                    }
1560                } else if (SCORE_ONGOING_HIGHER &&
1561                        0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1562                    if (notification.priority < Notification.PRIORITY_HIGH) {
1563                        notification.priority = Notification.PRIORITY_HIGH;
1564                    }
1565                }
1566
1567                // 1. initial score: buckets of 10, around the app
1568                int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1569
1570                // 2. Consult external heuristics (TBD)
1571
1572                // 3. Apply local rules
1573
1574                int initialScore = score;
1575                if (!mScorers.isEmpty()) {
1576                    if (DBG) Slog.v(TAG, "Initial score is " + score + ".");
1577                    for (NotificationScorer scorer : mScorers) {
1578                        try {
1579                            score = scorer.getScore(notification, score);
1580                        } catch (Throwable t) {
1581                            Slog.w(TAG, "Scorer threw on .getScore.", t);
1582                        }
1583                    }
1584                    if (DBG) Slog.v(TAG, "Final score is " + score + ".");
1585                }
1586
1587                // add extra to indicate score modified by NotificationScorer
1588                notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED,
1589                        score != initialScore);
1590
1591                // blocked apps
1592                if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1593                    if (!isSystemNotification) {
1594                        score = JUNK_SCORE;
1595                        Slog.e(TAG, "Suppressing notification from package " + pkg
1596                                + " by user request.");
1597                    }
1598                }
1599
1600                if (DBG) {
1601                    Slog.v(TAG, "Assigned score=" + score + " to " + notification);
1602                }
1603
1604                if (score < SCORE_DISPLAY_THRESHOLD) {
1605                    // Notification will be blocked because the score is too low.
1606                    return;
1607                }
1608
1609                // Is this notification intercepted by zen mode?
1610                final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification);
1611                notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
1612
1613                // Should this notification make noise, vibe, or use the LED?
1614                final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
1615                if (DBG || intercept) Slog.v(TAG,
1616                        "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept);
1617                synchronized (mNotificationList) {
1618                    final StatusBarNotification n = new StatusBarNotification(
1619                            pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1620                            user);
1621                    NotificationRecord r = new NotificationRecord(n);
1622                    NotificationRecord old = null;
1623
1624                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
1625                    if (index < 0) {
1626                        mNotificationList.add(r);
1627                        mUsageStats.registerPostedByApp(r);
1628                    } else {
1629                        old = mNotificationList.get(index);
1630                        mNotificationList.set(index, r);
1631                        mUsageStats.registerUpdatedByApp(r);
1632                        // Make sure we don't lose the foreground service state.
1633                        if (old != null) {
1634                            notification.flags |=
1635                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1636                        }
1637                    }
1638                    if (old != null) {
1639                        mNotificationsByKey.remove(old.sbn.getKey());
1640                    }
1641                    mNotificationsByKey.put(n.getKey(), r);
1642
1643                    // Ensure if this is a foreground service that the proper additional
1644                    // flags are set.
1645                    if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1646                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1647                                | Notification.FLAG_NO_CLEAR;
1648                    }
1649
1650                    final int currentUser;
1651                    final long token = Binder.clearCallingIdentity();
1652                    try {
1653                        currentUser = ActivityManager.getCurrentUser();
1654                    } finally {
1655                        Binder.restoreCallingIdentity(token);
1656                    }
1657
1658                    if (notification.icon != 0) {
1659                        if (old != null && old.statusBarKey != null) {
1660                            r.statusBarKey = old.statusBarKey;
1661                            final long identity = Binder.clearCallingIdentity();
1662                            try {
1663                                mStatusBar.updateNotification(r.statusBarKey, n);
1664                            } finally {
1665                                Binder.restoreCallingIdentity(identity);
1666                            }
1667                        } else {
1668                            final long identity = Binder.clearCallingIdentity();
1669                            try {
1670                                r.statusBarKey = mStatusBar.addNotification(n);
1671                                if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
1672                                        && canInterrupt) {
1673                                    mAttentionLight.pulse();
1674                                }
1675                            } finally {
1676                                Binder.restoreCallingIdentity(identity);
1677                            }
1678                        }
1679                        // Send accessibility events only for the current user.
1680                        if (currentUser == userId) {
1681                            sendAccessibilityEvent(notification, pkg);
1682                        }
1683
1684                        mListeners.notifyPostedLocked(r.sbn);
1685                    } else {
1686                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1687                        if (old != null && old.statusBarKey != null) {
1688                            final long identity = Binder.clearCallingIdentity();
1689                            try {
1690                                mStatusBar.removeNotification(old.statusBarKey);
1691                            } finally {
1692                                Binder.restoreCallingIdentity(identity);
1693                            }
1694
1695                            mListeners.notifyRemovedLocked(r.sbn);
1696                        }
1697                        // ATTENTION: in a future release we will bail out here
1698                        // so that we do not play sounds, show lights, etc. for invalid
1699                        // notifications
1700                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1701                                + n.getPackageName());
1702                    }
1703
1704                    // If we're not supposed to beep, vibrate, etc. then don't.
1705                    if (!mDisableNotificationAlerts
1706                            && (!(old != null
1707                                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1708                            && (r.getUserId() == UserHandle.USER_ALL ||
1709                                (r.getUserId() == userId && r.getUserId() == currentUser) ||
1710                                mUserProfiles.isCurrentProfile(r.getUserId()))
1711                            && canInterrupt
1712                            && mSystemReady
1713                            && mAudioManager != null) {
1714                        if (DBG) Slog.v(TAG, "Interrupting!");
1715                        // sound
1716
1717                        // should we use the default notification sound? (indicated either by
1718                        // DEFAULT_SOUND or because notification.sound is pointing at
1719                        // Settings.System.NOTIFICATION_SOUND)
1720                        final boolean useDefaultSound =
1721                               (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1722                                       Settings.System.DEFAULT_NOTIFICATION_URI
1723                                               .equals(notification.sound);
1724
1725                        Uri soundUri = null;
1726                        boolean hasValidSound = false;
1727
1728                        if (useDefaultSound) {
1729                            soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1730
1731                            // check to see if the default notification sound is silent
1732                            ContentResolver resolver = getContext().getContentResolver();
1733                            hasValidSound = Settings.System.getString(resolver,
1734                                   Settings.System.NOTIFICATION_SOUND) != null;
1735                        } else if (notification.sound != null) {
1736                            soundUri = notification.sound;
1737                            hasValidSound = (soundUri != null);
1738                        }
1739
1740                        if (hasValidSound) {
1741                            boolean looping =
1742                                    (notification.flags & Notification.FLAG_INSISTENT) != 0;
1743                            int audioStreamType;
1744                            if (notification.audioStreamType >= 0) {
1745                                audioStreamType = notification.audioStreamType;
1746                            } else {
1747                                audioStreamType = DEFAULT_STREAM_TYPE;
1748                            }
1749                            mSoundNotification = r;
1750                            // do not play notifications if stream volume is 0 (typically because
1751                            // ringer mode is silent) or if there is a user of exclusive audio focus
1752                            if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
1753                                    && !mAudioManager.isAudioFocusExclusive()) {
1754                                final long identity = Binder.clearCallingIdentity();
1755                                try {
1756                                    final IRingtonePlayer player =
1757                                            mAudioManager.getRingtonePlayer();
1758                                    if (player != null) {
1759                                        if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1760                                                + " on stream " + audioStreamType);
1761                                        player.playAsync(soundUri, user, looping, audioStreamType);
1762                                    }
1763                                } catch (RemoteException e) {
1764                                } finally {
1765                                    Binder.restoreCallingIdentity(identity);
1766                                }
1767                            }
1768                        }
1769
1770                        // vibrate
1771                        // Does the notification want to specify its own vibration?
1772                        final boolean hasCustomVibrate = notification.vibrate != null;
1773
1774                        // new in 4.2: if there was supposed to be a sound and we're in vibrate
1775                        // mode, and no other vibration is specified, we fall back to vibration
1776                        final boolean convertSoundToVibration =
1777                                   !hasCustomVibrate
1778                                && hasValidSound
1779                                && (mAudioManager.getRingerMode()
1780                                           == AudioManager.RINGER_MODE_VIBRATE);
1781
1782                        // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1783                        final boolean useDefaultVibrate =
1784                                (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1785
1786                        if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1787                                && !(mAudioManager.getRingerMode()
1788                                        == AudioManager.RINGER_MODE_SILENT)) {
1789                            mVibrateNotification = r;
1790
1791                            if (useDefaultVibrate || convertSoundToVibration) {
1792                                // Escalate privileges so we can use the vibrator even if the
1793                                // notifying app does not have the VIBRATE permission.
1794                                long identity = Binder.clearCallingIdentity();
1795                                try {
1796                                    mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
1797                                        useDefaultVibrate ? mDefaultVibrationPattern
1798                                            : mFallbackVibrationPattern,
1799                                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1800                                                ? 0: -1, notification.audioStreamType);
1801                                } finally {
1802                                    Binder.restoreCallingIdentity(identity);
1803                                }
1804                            } else if (notification.vibrate.length > 1) {
1805                                // If you want your own vibration pattern, you need the VIBRATE
1806                                // permission
1807                                mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
1808                                        notification.vibrate,
1809                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1810                                            ? 0: -1, notification.audioStreamType);
1811                            }
1812                        }
1813                    }
1814
1815                    // light
1816                    // the most recent thing gets the light
1817                    mLights.remove(old);
1818                    if (mLedNotification == old) {
1819                        mLedNotification = null;
1820                    }
1821                    //Slog.i(TAG, "notification.lights="
1822                    //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
1823                    //                  != 0));
1824                    if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1825                            && canInterrupt) {
1826                        mLights.add(r);
1827                        updateLightsLocked();
1828                    } else {
1829                        if (old != null
1830                                && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
1831                            updateLightsLocked();
1832                        }
1833                    }
1834                }
1835            }
1836        });
1837
1838        idOut[0] = id;
1839    }
1840
1841    void showNextToastLocked() {
1842        ToastRecord record = mToastQueue.get(0);
1843        while (record != null) {
1844            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1845            try {
1846                record.callback.show();
1847                scheduleTimeoutLocked(record);
1848                return;
1849            } catch (RemoteException e) {
1850                Slog.w(TAG, "Object died trying to show notification " + record.callback
1851                        + " in package " + record.pkg);
1852                // remove it from the list and let the process die
1853                int index = mToastQueue.indexOf(record);
1854                if (index >= 0) {
1855                    mToastQueue.remove(index);
1856                }
1857                keepProcessAliveLocked(record.pid);
1858                if (mToastQueue.size() > 0) {
1859                    record = mToastQueue.get(0);
1860                } else {
1861                    record = null;
1862                }
1863            }
1864        }
1865    }
1866
1867    void cancelToastLocked(int index) {
1868        ToastRecord record = mToastQueue.get(index);
1869        try {
1870            record.callback.hide();
1871        } catch (RemoteException e) {
1872            Slog.w(TAG, "Object died trying to hide notification " + record.callback
1873                    + " in package " + record.pkg);
1874            // don't worry about this, we're about to remove it from
1875            // the list anyway
1876        }
1877        mToastQueue.remove(index);
1878        keepProcessAliveLocked(record.pid);
1879        if (mToastQueue.size() > 0) {
1880            // Show the next one. If the callback fails, this will remove
1881            // it from the list, so don't assume that the list hasn't changed
1882            // after this point.
1883            showNextToastLocked();
1884        }
1885    }
1886
1887    private void scheduleTimeoutLocked(ToastRecord r)
1888    {
1889        mHandler.removeCallbacksAndMessages(r);
1890        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1891        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1892        mHandler.sendMessageDelayed(m, delay);
1893    }
1894
1895    private void handleTimeout(ToastRecord record)
1896    {
1897        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1898        synchronized (mToastQueue) {
1899            int index = indexOfToastLocked(record.pkg, record.callback);
1900            if (index >= 0) {
1901                cancelToastLocked(index);
1902            }
1903        }
1904    }
1905
1906    // lock on mToastQueue
1907    int indexOfToastLocked(String pkg, ITransientNotification callback)
1908    {
1909        IBinder cbak = callback.asBinder();
1910        ArrayList<ToastRecord> list = mToastQueue;
1911        int len = list.size();
1912        for (int i=0; i<len; i++) {
1913            ToastRecord r = list.get(i);
1914            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1915                return i;
1916            }
1917        }
1918        return -1;
1919    }
1920
1921    // lock on mToastQueue
1922    void keepProcessAliveLocked(int pid)
1923    {
1924        int toastCount = 0; // toasts from this pid
1925        ArrayList<ToastRecord> list = mToastQueue;
1926        int N = list.size();
1927        for (int i=0; i<N; i++) {
1928            ToastRecord r = list.get(i);
1929            if (r.pid == pid) {
1930                toastCount++;
1931            }
1932        }
1933        try {
1934            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1935        } catch (RemoteException e) {
1936            // Shouldn't happen.
1937        }
1938    }
1939
1940    private final class WorkerHandler extends Handler
1941    {
1942        @Override
1943        public void handleMessage(Message msg)
1944        {
1945            switch (msg.what)
1946            {
1947                case MESSAGE_TIMEOUT:
1948                    handleTimeout((ToastRecord)msg.obj);
1949                    break;
1950                case MESSAGE_SAVE_POLICY_FILE:
1951                    handleSavePolicyFile();
1952                    break;
1953            }
1954        }
1955    }
1956
1957
1958    // Notifications
1959    // ============================================================================
1960    static int clamp(int x, int low, int high) {
1961        return (x < low) ? low : ((x > high) ? high : x);
1962    }
1963
1964    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
1965        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
1966        if (!manager.isEnabled()) {
1967            return;
1968        }
1969
1970        AccessibilityEvent event =
1971            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1972        event.setPackageName(packageName);
1973        event.setClassName(Notification.class.getName());
1974        event.setParcelableData(notification);
1975        CharSequence tickerText = notification.tickerText;
1976        if (!TextUtils.isEmpty(tickerText)) {
1977            event.getText().add(tickerText);
1978        }
1979
1980        manager.sendAccessibilityEvent(event);
1981    }
1982
1983    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
1984        // tell the app
1985        if (sendDelete) {
1986            if (r.getNotification().deleteIntent != null) {
1987                try {
1988                    r.getNotification().deleteIntent.send();
1989                } catch (PendingIntent.CanceledException ex) {
1990                    // do nothing - there's no relevant way to recover, and
1991                    //     no reason to let this propagate
1992                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
1993                }
1994            }
1995        }
1996
1997        // status bar
1998        if (r.getNotification().icon != 0) {
1999            final long identity = Binder.clearCallingIdentity();
2000            try {
2001                mStatusBar.removeNotification(r.statusBarKey);
2002            } finally {
2003                Binder.restoreCallingIdentity(identity);
2004            }
2005            r.statusBarKey = null;
2006            mListeners.notifyRemovedLocked(r.sbn);
2007        }
2008
2009        // sound
2010        if (mSoundNotification == r) {
2011            mSoundNotification = null;
2012            final long identity = Binder.clearCallingIdentity();
2013            try {
2014                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2015                if (player != null) {
2016                    player.stopAsync();
2017                }
2018            } catch (RemoteException e) {
2019            } finally {
2020                Binder.restoreCallingIdentity(identity);
2021            }
2022        }
2023
2024        // vibrate
2025        if (mVibrateNotification == r) {
2026            mVibrateNotification = null;
2027            long identity = Binder.clearCallingIdentity();
2028            try {
2029                mVibrator.cancel();
2030            }
2031            finally {
2032                Binder.restoreCallingIdentity(identity);
2033            }
2034        }
2035
2036        // light
2037        mLights.remove(r);
2038        if (mLedNotification == r) {
2039            mLedNotification = null;
2040        }
2041
2042        // Record usage stats
2043        switch (reason) {
2044            case REASON_DELEGATE_CANCEL:
2045            case REASON_DELEGATE_CANCEL_ALL:
2046            case REASON_LISTENER_CANCEL:
2047            case REASON_LISTENER_CANCEL_ALL:
2048                mUsageStats.registerDismissedByUser(r);
2049                break;
2050            case REASON_NOMAN_CANCEL:
2051            case REASON_NOMAN_CANCEL_ALL:
2052                mUsageStats.registerRemovedByApp(r);
2053                break;
2054            case REASON_DELEGATE_CLICK:
2055                mUsageStats.registerCancelDueToClick(r);
2056                break;
2057            default:
2058                mUsageStats.registerCancelUnknown(r);
2059                break;
2060        }
2061
2062        // Save it for users of getHistoricalNotifications()
2063        mArchive.record(r.sbn);
2064    }
2065
2066    /**
2067     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2068     * and none of the {@code mustNotHaveFlags}.
2069     */
2070    void cancelNotification(final int callingUid, final int callingPid,
2071            final String pkg, final String tag, final int id,
2072            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2073            final int userId, final int reason, final ManagedServiceInfo listener) {
2074        // In enqueueNotificationInternal notifications are added by scheduling the
2075        // work on the worker handler. Hence, we also schedule the cancel on this
2076        // handler to avoid a scenario where an add notification call followed by a
2077        // remove notification call ends up in not removing the notification.
2078        mHandler.post(new Runnable() {
2079            @Override
2080            public void run() {
2081                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2082                        mustHaveFlags, mustNotHaveFlags, reason,
2083                        listener == null ? null : listener.component.toShortString());
2084
2085                synchronized (mNotificationList) {
2086                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2087                    if (index >= 0) {
2088                        NotificationRecord r = mNotificationList.get(index);
2089
2090                        // Ideally we'd do this in the caller of this method. However, that would
2091                        // require the caller to also find the notification.
2092                        if (reason == REASON_DELEGATE_CLICK) {
2093                            mUsageStats.registerClickedByUser(r);
2094                        }
2095
2096                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2097                            return;
2098                        }
2099                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2100                            return;
2101                        }
2102
2103                        mNotificationList.remove(index);
2104                        mNotificationsByKey.remove(r.sbn.getKey());
2105
2106                        cancelNotificationLocked(r, sendDelete, reason);
2107                        updateLightsLocked();
2108                    }
2109                }
2110            }
2111        });
2112    }
2113
2114    /**
2115     * Determine whether the userId applies to the notification in question, either because
2116     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2117     */
2118    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2119        return
2120                // looking for USER_ALL notifications? match everything
2121                   userId == UserHandle.USER_ALL
2122                // a notification sent to USER_ALL matches any query
2123                || r.getUserId() == UserHandle.USER_ALL
2124                // an exact user match
2125                || r.getUserId() == userId;
2126    }
2127
2128    /**
2129     * Determine whether the userId applies to the notification in question, either because
2130     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2131     * because it matches one of the users profiles.
2132     */
2133    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2134        return notificationMatchesUserId(r, userId)
2135                || mUserProfiles.isCurrentProfile(r.getUserId());
2136    }
2137
2138    /**
2139     * Cancels all notifications from a given package that have all of the
2140     * {@code mustHaveFlags}.
2141     */
2142    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2143            int mustNotHaveFlags, boolean doit, int userId, int reason,
2144            ManagedServiceInfo listener) {
2145        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2146                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2147                listener == null ? null : listener.component.toShortString());
2148
2149        synchronized (mNotificationList) {
2150            final int N = mNotificationList.size();
2151            boolean canceledSomething = false;
2152            for (int i = N-1; i >= 0; --i) {
2153                NotificationRecord r = mNotificationList.get(i);
2154                if (!notificationMatchesUserId(r, userId)) {
2155                    continue;
2156                }
2157                // Don't remove notifications to all, if there's no package name specified
2158                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2159                    continue;
2160                }
2161                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2162                    continue;
2163                }
2164                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2165                    continue;
2166                }
2167                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2168                    continue;
2169                }
2170                canceledSomething = true;
2171                if (!doit) {
2172                    return true;
2173                }
2174                mNotificationList.remove(i);
2175                mNotificationsByKey.remove(r.sbn.getKey());
2176                cancelNotificationLocked(r, false, reason);
2177            }
2178            if (canceledSomething) {
2179                updateLightsLocked();
2180            }
2181            return canceledSomething;
2182        }
2183    }
2184
2185    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2186            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2187        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2188                null, userId, 0, 0, reason,
2189                listener == null ? null : listener.component.toShortString());
2190
2191        final int N = mNotificationList.size();
2192        for (int i=N-1; i>=0; i--) {
2193            NotificationRecord r = mNotificationList.get(i);
2194            if (includeCurrentProfiles) {
2195                if (!notificationMatchesCurrentProfiles(r, userId)) {
2196                    continue;
2197                }
2198            } else {
2199                if (!notificationMatchesUserId(r, userId)) {
2200                    continue;
2201                }
2202            }
2203
2204            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2205                            | Notification.FLAG_NO_CLEAR)) == 0) {
2206                mNotificationList.remove(i);
2207                mNotificationsByKey.remove(r.sbn.getKey());
2208                cancelNotificationLocked(r, true, reason);
2209            }
2210        }
2211        updateLightsLocked();
2212    }
2213
2214    // lock on mNotificationList
2215    void updateLightsLocked()
2216    {
2217        // handle notification lights
2218        if (mLedNotification == null) {
2219            // get next notification, if any
2220            int n = mLights.size();
2221            if (n > 0) {
2222                mLedNotification = mLights.get(n-1);
2223            }
2224        }
2225
2226        // Don't flash while we are in a call or screen is on
2227        if (mLedNotification == null || mInCall || mScreenOn) {
2228            mNotificationLight.turnOff();
2229        } else {
2230            final Notification ledno = mLedNotification.sbn.getNotification();
2231            int ledARGB = ledno.ledARGB;
2232            int ledOnMS = ledno.ledOnMS;
2233            int ledOffMS = ledno.ledOffMS;
2234            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2235                ledARGB = mDefaultNotificationColor;
2236                ledOnMS = mDefaultNotificationLedOn;
2237                ledOffMS = mDefaultNotificationLedOff;
2238            }
2239            if (mNotificationPulseEnabled) {
2240                // pulse repeatedly
2241                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2242                        ledOnMS, ledOffMS);
2243            }
2244        }
2245    }
2246
2247    // lock on mNotificationList
2248    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2249    {
2250        ArrayList<NotificationRecord> list = mNotificationList;
2251        final int len = list.size();
2252        for (int i=0; i<len; i++) {
2253            NotificationRecord r = list.get(i);
2254            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2255                continue;
2256            }
2257            if (tag == null) {
2258                if (r.sbn.getTag() != null) {
2259                    continue;
2260                }
2261            } else {
2262                if (!tag.equals(r.sbn.getTag())) {
2263                    continue;
2264                }
2265            }
2266            if (r.sbn.getPackageName().equals(pkg)) {
2267                return i;
2268            }
2269        }
2270        return -1;
2271    }
2272
2273    private void updateNotificationPulse() {
2274        synchronized (mNotificationList) {
2275            updateLightsLocked();
2276        }
2277    }
2278
2279    private static boolean isUidSystem(int uid) {
2280        final int appid = UserHandle.getAppId(uid);
2281        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2282    }
2283
2284    private static boolean isCallerSystem() {
2285        return isUidSystem(Binder.getCallingUid());
2286    }
2287
2288    private static void checkCallerIsSystem() {
2289        if (isCallerSystem()) {
2290            return;
2291        }
2292        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2293    }
2294
2295    private static void checkCallerIsSystemOrSameApp(String pkg) {
2296        if (isCallerSystem()) {
2297            return;
2298        }
2299        final int uid = Binder.getCallingUid();
2300        try {
2301            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2302                    pkg, 0, UserHandle.getCallingUserId());
2303            if (!UserHandle.isSameApp(ai.uid, uid)) {
2304                throw new SecurityException("Calling uid " + uid + " gave package"
2305                        + pkg + " which is owned by uid " + ai.uid);
2306            }
2307        } catch (RemoteException re) {
2308            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2309        }
2310    }
2311
2312    public class NotificationListeners extends ManagedServices {
2313
2314        public NotificationListeners() {
2315            super(getContext(), mHandler, mNotificationList, mUserProfiles);
2316        }
2317
2318        @Override
2319        protected Config getConfig() {
2320            Config c = new Config();
2321            c.caption = "notification listener";
2322            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2323            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2324            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2325            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2326            c.clientLabel = R.string.notification_listener_binding_label;
2327            return c;
2328        }
2329
2330        @Override
2331        protected IInterface asInterface(IBinder binder) {
2332            return INotificationListener.Stub.asInterface(binder);
2333        }
2334
2335        @Override
2336        public void onServiceAdded(ManagedServiceInfo info) {
2337            final INotificationListener listener = (INotificationListener) info.service;
2338            final String[] keys = getActiveNotificationKeysFromListener(listener);
2339            try {
2340                listener.onListenerConnected(keys);
2341            } catch (RemoteException e) {
2342                // we tried
2343            }
2344        }
2345
2346        /**
2347         * asynchronously notify all listeners about a new notification
2348         */
2349        public void notifyPostedLocked(StatusBarNotification sbn) {
2350            // make a copy in case changes are made to the underlying Notification object
2351            final StatusBarNotification sbnClone = sbn.clone();
2352            for (final ManagedServiceInfo info : mServices) {
2353                mHandler.post(new Runnable() {
2354                    @Override
2355                    public void run() {
2356                        notifyPostedIfUserMatch(info, sbnClone);
2357                    }
2358                });
2359            }
2360        }
2361
2362        /**
2363         * asynchronously notify all listeners about a removed notification
2364         */
2365        public void notifyRemovedLocked(StatusBarNotification sbn) {
2366            // make a copy in case changes are made to the underlying Notification object
2367            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2368            // notification
2369            final StatusBarNotification sbnLight = sbn.cloneLight();
2370            for (ManagedServiceInfo serviceInfo : mServices) {
2371                final ManagedServiceInfo info = (ManagedServiceInfo) serviceInfo;
2372                mHandler.post(new Runnable() {
2373                    @Override
2374                    public void run() {
2375                        notifyRemovedIfUserMatch(info, sbnLight);
2376                    }
2377                });
2378            }
2379        }
2380
2381        private void notifyPostedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
2382            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2383                return;
2384            }
2385            final INotificationListener listener = (INotificationListener)info.service;
2386            try {
2387                listener.onNotificationPosted(sbn);
2388            } catch (RemoteException ex) {
2389                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2390            }
2391        }
2392
2393        private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
2394            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2395                return;
2396            }
2397            final INotificationListener listener = (INotificationListener)info.service;
2398            try {
2399                listener.onNotificationRemoved(sbn);
2400            } catch (RemoteException ex) {
2401                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
2402            }
2403        }
2404    }
2405}
2406