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