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