NotificationManagerService.java revision 5feceebb892d4cb5777cea3c6174b206705d456b
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;
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.ServiceConnection;
40import android.content.pm.ApplicationInfo;
41import android.content.pm.PackageInfo;
42import android.content.pm.PackageManager;
43import android.content.pm.PackageManager.NameNotFoundException;
44import android.content.res.Resources;
45import android.database.ContentObserver;
46import android.media.AudioManager;
47import android.media.IAudioService;
48import android.media.IRingtonePlayer;
49import android.net.Uri;
50import android.os.Binder;
51import android.os.Handler;
52import android.os.IBinder;
53import android.os.Message;
54import android.os.Process;
55import android.os.RemoteException;
56import android.os.ServiceManager;
57import android.os.UserHandle;
58import android.os.UserManager;
59import android.os.Vibrator;
60import android.provider.Settings;
61import android.service.notification.INotificationListener;
62import android.service.notification.NotificationListenerService;
63import android.service.notification.StatusBarNotification;
64import android.telephony.TelephonyManager;
65import android.text.TextUtils;
66import android.util.AtomicFile;
67import android.util.EventLog;
68import android.util.Log;
69import android.util.Slog;
70import android.util.Xml;
71import android.view.accessibility.AccessibilityEvent;
72import android.view.accessibility.AccessibilityManager;
73import android.widget.Toast;
74
75import org.xmlpull.v1.XmlPullParser;
76import org.xmlpull.v1.XmlPullParserException;
77
78import java.io.File;
79import java.io.FileDescriptor;
80import java.io.FileInputStream;
81import java.io.FileNotFoundException;
82import java.io.IOException;
83import java.io.PrintWriter;
84import java.util.ArrayDeque;
85import java.util.ArrayList;
86import java.util.Arrays;
87import java.util.HashSet;
88import java.util.Iterator;
89import java.util.NoSuchElementException;
90
91import libcore.io.IoUtils;
92
93
94/** {@hide} */
95public class NotificationManagerService extends INotificationManager.Stub
96{
97    private static final String TAG = "NotificationService";
98    private static final boolean DBG = false;
99
100    private static final int MAX_PACKAGE_NOTIFICATIONS = 50;
101
102    // message codes
103    private static final int MESSAGE_TIMEOUT = 2;
104
105    private static final int LONG_DELAY = 3500; // 3.5 seconds
106    private static final int SHORT_DELAY = 2000; // 2 seconds
107
108    private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
109    private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
110
111    private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
112    private static final boolean SCORE_ONGOING_HIGHER = false;
113
114    private static final int JUNK_SCORE = -1000;
115    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
116    private static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
117
118    // Notifications with scores below this will not interrupt the user, either via LED or
119    // sound or vibration
120    private static final int SCORE_INTERRUPTION_THRESHOLD =
121            Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
122
123    private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
124    private static final boolean ENABLE_BLOCKED_TOASTS = true;
125
126    private static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":";
127
128    final Context mContext;
129    final IActivityManager mAm;
130    final UserManager mUserManager;
131    final IBinder mForegroundToken = new Binder();
132
133    private WorkerHandler mHandler;
134    private StatusBarManagerService mStatusBar;
135    private LightsService.Light mNotificationLight;
136    private LightsService.Light mAttentionLight;
137
138    private int mDefaultNotificationColor;
139    private int mDefaultNotificationLedOn;
140    private int mDefaultNotificationLedOff;
141
142    private long[] mDefaultVibrationPattern;
143    private long[] mFallbackVibrationPattern;
144
145    private boolean mSystemReady;
146    private int mDisabledNotifications;
147
148    private NotificationRecord mSoundNotification;
149    private NotificationRecord mVibrateNotification;
150
151    private IAudioService mAudioService;
152    private Vibrator mVibrator;
153
154    // for enabling and disabling notification pulse behavior
155    private boolean mScreenOn = true;
156    private boolean mInCall = false;
157    private boolean mNotificationPulseEnabled;
158
159    // used as a mutex for access to all active notifications & listeners
160    private final ArrayList<NotificationRecord> mNotificationList =
161            new ArrayList<NotificationRecord>();
162
163    private ArrayList<ToastRecord> mToastQueue;
164
165    private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
166    private NotificationRecord mLedNotification;
167
168    private final AppOpsManager mAppOps;
169
170    // contains connections to all connected listeners, including app services
171    // and system listeners
172    private ArrayList<NotificationListenerInfo> mListeners
173            = new ArrayList<NotificationListenerInfo>();
174    // things that will be put into mListeners as soon as they're ready
175    private ArrayList<String> mServicesBinding = new ArrayList<String>();
176    // lists the component names of all enabled (and therefore connected) listener
177    // app services for the current user only
178    private HashSet<ComponentName> mEnabledListenersForCurrentUser
179            = new HashSet<ComponentName>();
180    // Just the packages from mEnabledListenersForCurrentUser
181    private HashSet<String> mEnabledListenerPackageNames = new HashSet<String>();
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    private class NotificationListenerInfo implements DeathRecipient {
197        INotificationListener listener;
198        ComponentName component;
199        int userid;
200        boolean isSystem;
201        ServiceConnection connection;
202
203        public NotificationListenerInfo(INotificationListener listener, ComponentName component,
204                int userid, boolean isSystem) {
205            this.listener = listener;
206            this.component = component;
207            this.userid = userid;
208            this.isSystem = isSystem;
209            this.connection = null;
210        }
211
212        public NotificationListenerInfo(INotificationListener listener, ComponentName component,
213                int userid, ServiceConnection connection) {
214            this.listener = listener;
215            this.component = component;
216            this.userid = userid;
217            this.isSystem = false;
218            this.connection = connection;
219        }
220
221        boolean enabledAndUserMatches(StatusBarNotification sbn) {
222            final int nid = sbn.getUserId();
223            if (!isEnabledForCurrentUser()) {
224                return false;
225            }
226            if (this.userid == UserHandle.USER_ALL) return true;
227            return (nid == UserHandle.USER_ALL || nid == this.userid);
228        }
229
230        public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
231            if (!enabledAndUserMatches(sbn)) {
232                return;
233            }
234            try {
235                listener.onNotificationPosted(sbn);
236            } catch (RemoteException ex) {
237                // not there?
238            }
239        }
240
241        public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
242            if (!enabledAndUserMatches(sbn)) return;
243            try {
244                listener.onNotificationRemoved(sbn);
245            } catch (RemoteException ex) {
246                // not there?
247            }
248        }
249
250        @Override
251        public void binderDied() {
252            if (connection == null) {
253                // This is not a service; it won't be recreated. We can give up this connection.
254                unregisterListener(this.listener, this.userid);
255            }
256        }
257
258        /** convenience method for looking in mEnabledListenersForCurrentUser */
259        public boolean isEnabledForCurrentUser() {
260            if (this.isSystem) return true;
261            if (this.connection == null) return false;
262            return mEnabledListenersForCurrentUser.contains(this.component);
263        }
264    }
265
266    private static class Archive {
267        static final int BUFFER_SIZE = 1000;
268        ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
269
270        public Archive() {
271        }
272
273        public void record(StatusBarNotification nr) {
274            // Nuke heavy parts of notification before storing in archive
275            nr.notification.tickerView = null;
276            nr.notification.contentView = null;
277            nr.notification.bigContentView = null;
278            nr.notification.largeIcon = null;
279
280            if (mBuffer.size() == BUFFER_SIZE) {
281                mBuffer.removeFirst();
282            }
283            mBuffer.addLast(nr);
284        }
285
286        public void clear() {
287            mBuffer.clear();
288        }
289
290        public Iterator<StatusBarNotification> descendingIterator() {
291            return mBuffer.descendingIterator();
292        }
293        public Iterator<StatusBarNotification> ascendingIterator() {
294            return mBuffer.iterator();
295        }
296        public Iterator<StatusBarNotification> filter(
297                final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
298            return new Iterator<StatusBarNotification>() {
299                StatusBarNotification mNext = findNext();
300
301                private StatusBarNotification findNext() {
302                    while (iter.hasNext()) {
303                        StatusBarNotification nr = iter.next();
304                        if ((pkg == null || nr.pkg == pkg)
305                                && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
306                            return nr;
307                        }
308                    }
309                    return null;
310                }
311
312                @Override
313                public boolean hasNext() {
314                    return mNext == null;
315                }
316
317                @Override
318                public StatusBarNotification next() {
319                    StatusBarNotification next = mNext;
320                    if (next == null) {
321                        throw new NoSuchElementException();
322                    }
323                    mNext = findNext();
324                    return next;
325                }
326
327                @Override
328                public void remove() {
329                    iter.remove();
330                }
331            };
332        }
333
334        public StatusBarNotification[] getArray(int count) {
335            if (count == 0) count = Archive.BUFFER_SIZE;
336            final StatusBarNotification[] a
337                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
338            Iterator<StatusBarNotification> iter = descendingIterator();
339            int i=0;
340            while (iter.hasNext() && i < count) {
341                a[i++] = iter.next();
342            }
343            return a;
344        }
345
346        public StatusBarNotification[] getArray(int count, String pkg, int userId) {
347            if (count == 0) count = Archive.BUFFER_SIZE;
348            final StatusBarNotification[] a
349                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
350            Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
351            int i=0;
352            while (iter.hasNext() && i < count) {
353                a[i++] = iter.next();
354            }
355            return a;
356        }
357
358    }
359
360    Archive mArchive = new Archive();
361
362    private void loadBlockDb() {
363        synchronized(mBlockedPackages) {
364            if (mPolicyFile == null) {
365                File dir = new File("/data/system");
366                mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml"));
367
368                mBlockedPackages.clear();
369
370                FileInputStream infile = null;
371                try {
372                    infile = mPolicyFile.openRead();
373                    final XmlPullParser parser = Xml.newPullParser();
374                    parser.setInput(infile, null);
375
376                    int type;
377                    String tag;
378                    int version = DB_VERSION;
379                    while ((type = parser.next()) != END_DOCUMENT) {
380                        tag = parser.getName();
381                        if (type == START_TAG) {
382                            if (TAG_BODY.equals(tag)) {
383                                version = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION));
384                            } else if (TAG_BLOCKED_PKGS.equals(tag)) {
385                                while ((type = parser.next()) != END_DOCUMENT) {
386                                    tag = parser.getName();
387                                    if (TAG_PACKAGE.equals(tag)) {
388                                        mBlockedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
389                                    } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
390                                        break;
391                                    }
392                                }
393                            }
394                        }
395                    }
396                } catch (FileNotFoundException e) {
397                    // No data yet
398                } catch (IOException e) {
399                    Log.wtf(TAG, "Unable to read blocked notifications database", e);
400                } catch (NumberFormatException e) {
401                    Log.wtf(TAG, "Unable to parse blocked notifications database", e);
402                } catch (XmlPullParserException e) {
403                    Log.wtf(TAG, "Unable to parse blocked notifications database", e);
404                } finally {
405                    IoUtils.closeQuietly(infile);
406                }
407            }
408        }
409    }
410
411    /**
412     * Use this when you just want to know if notifications are OK for this package.
413     */
414    public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
415        checkCallerIsSystem();
416        return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
417                == AppOpsManager.MODE_ALLOWED);
418    }
419
420    /** Use this when you actually want to post a notification or toast.
421     *
422     * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
423     */
424    private boolean noteNotificationOp(String pkg, int uid) {
425        if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
426                != AppOpsManager.MODE_ALLOWED) {
427            Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
428            return false;
429        }
430        return true;
431    }
432
433    public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
434        checkCallerIsSystem();
435        if (true||DBG) {
436            Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
437        }
438        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
439                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
440    }
441
442
443    private static String idDebugString(Context baseContext, String packageName, int id) {
444        Context c = null;
445
446        if (packageName != null) {
447            try {
448                c = baseContext.createPackageContext(packageName, 0);
449            } catch (NameNotFoundException e) {
450                c = baseContext;
451            }
452        } else {
453            c = baseContext;
454        }
455
456        String pkg;
457        String type;
458        String name;
459
460        Resources r = c.getResources();
461        try {
462            return r.getResourceName(id);
463        } catch (Resources.NotFoundException e) {
464            return "<name unknown>";
465        }
466    }
467
468    /**
469     * System-only API for getting a list of current (i.e. not cleared) notifications.
470     *
471     * Requires ACCESS_NOTIFICATIONS which is signature|system.
472     */
473    @Override
474    public StatusBarNotification[] getActiveNotifications(String callingPkg) {
475        // enforce() will ensure the calling uid has the correct permission
476        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
477                "NotificationManagerService.getActiveNotifications");
478
479        StatusBarNotification[] tmp = null;
480        int uid = Binder.getCallingUid();
481
482        // noteOp will check to make sure the callingPkg matches the uid
483        if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
484                == AppOpsManager.MODE_ALLOWED) {
485            synchronized (mNotificationList) {
486                tmp = new StatusBarNotification[mNotificationList.size()];
487                final int N = mNotificationList.size();
488                for (int i=0; i<N; i++) {
489                    tmp[i] = mNotificationList.get(i).sbn;
490                }
491            }
492        }
493        return tmp;
494    }
495
496    /**
497     * System-only API for getting a list of recent (cleared, no longer shown) notifications.
498     *
499     * Requires ACCESS_NOTIFICATIONS which is signature|system.
500     */
501    @Override
502    public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
503        // enforce() will ensure the calling uid has the correct permission
504        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
505                "NotificationManagerService.getHistoricalNotifications");
506
507        StatusBarNotification[] tmp = null;
508        int uid = Binder.getCallingUid();
509
510        // noteOp will check to make sure the callingPkg matches the uid
511        if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
512                == AppOpsManager.MODE_ALLOWED) {
513            synchronized (mArchive) {
514                tmp = mArchive.getArray(count);
515            }
516        }
517        return tmp;
518    }
519
520    /**
521     * Called whenever packages change, the user switches, or ENABLED_NOTIFICATION_LISTENERS
522     * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
523     */
524    void rebindListenerServices() {
525        String flat = Settings.Secure.getString(
526                mContext.getContentResolver(),
527                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
528
529        NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()];
530        final ArrayList<ComponentName> toAdd;
531        final int currentUser = ActivityManager.getCurrentUser();
532
533        synchronized (mNotificationList) {
534            // unbind and remove all existing listeners
535            toRemove = mListeners.toArray(toRemove);
536
537            toAdd = new ArrayList<ComponentName>();
538            final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>();
539            final HashSet<String> newPackages = new HashSet<String>();
540
541            // decode the list of components
542            if (flat != null) {
543                String[] components = flat.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
544                for (int i=0; i<components.length; i++) {
545                    final ComponentName component
546                            = ComponentName.unflattenFromString(components[i]);
547                    if (component != null) {
548                        newEnabled.add(component);
549                        toAdd.add(component);
550                        newPackages.add(component.getPackageName());
551                    }
552                }
553
554                mEnabledListenersForCurrentUser = newEnabled;
555                mEnabledListenerPackageNames = newPackages;
556            }
557        }
558
559        for (NotificationListenerInfo info : toRemove) {
560            final ComponentName component = info.component;
561            final int oldUser = info.userid;
562            Slog.v(TAG, "disabling notification listener for user " + oldUser + ": " + component);
563            unregisterListenerService(component, info.userid);
564        }
565
566        final int N = toAdd.size();
567        for (int i=0; i<N; i++) {
568            final ComponentName component = toAdd.get(i);
569            Slog.v(TAG, "enabling notification listener for user " + currentUser + ": "
570                    + component);
571            registerListenerService(component, currentUser);
572        }
573    }
574
575    /**
576     * Register a listener binder directly with the notification manager.
577     *
578     * Only works with system callers. Apps should extend
579     * {@link android.service.notification.NotificationListenerService}.
580     */
581    @Override
582    public void registerListener(final INotificationListener listener,
583            final ComponentName component, final int userid) {
584        checkCallerIsSystem();
585
586        synchronized (mNotificationList) {
587            try {
588                NotificationListenerInfo info
589                        = new NotificationListenerInfo(listener, component, userid, true);
590                listener.asBinder().linkToDeath(info, 0);
591                mListeners.add(info);
592            } catch (RemoteException e) {
593                // already dead
594            }
595        }
596    }
597
598    /**
599     * Version of registerListener that takes the name of a
600     * {@link android.service.notification.NotificationListenerService} to bind to.
601     *
602     * This is the mechanism by which third parties may subscribe to notifications.
603     */
604    private void registerListenerService(final ComponentName name, final int userid) {
605        checkCallerIsSystem();
606
607        if (DBG) Slog.v(TAG, "registerListenerService: " + name + " u=" + userid);
608
609        synchronized (mNotificationList) {
610            final String servicesBindingTag = name.toString() + "/" + userid;
611            if (mServicesBinding.contains(servicesBindingTag)) {
612                // stop registering this thing already! we're working on it
613                return;
614            }
615            mServicesBinding.add(servicesBindingTag);
616
617            final int N = mListeners.size();
618            for (int i=N-1; i>=0; i--) {
619                final NotificationListenerInfo info = mListeners.get(i);
620                if (name.equals(info.component)
621                        && info.userid == userid) {
622                    // cut old connections
623                    if (DBG) Slog.v(TAG, "    disconnecting old listener: " + info.listener);
624                    mListeners.remove(i);
625                    if (info.connection != null) {
626                        mContext.unbindService(info.connection);
627                    }
628                }
629            }
630
631            Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
632            intent.setComponent(name);
633
634            intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
635                    com.android.internal.R.string.notification_listener_binding_label);
636            intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
637                    mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0));
638
639            try {
640                if (DBG) Slog.v(TAG, "binding: " + intent);
641                if (!mContext.bindServiceAsUser(intent,
642                        new ServiceConnection() {
643                            INotificationListener mListener;
644                            @Override
645                            public void onServiceConnected(ComponentName name, IBinder service) {
646                                synchronized (mNotificationList) {
647                                    mServicesBinding.remove(servicesBindingTag);
648                                    try {
649                                        mListener = INotificationListener.Stub.asInterface(service);
650                                        NotificationListenerInfo info = new NotificationListenerInfo(
651                                                mListener, name, userid, this);
652                                        service.linkToDeath(info, 0);
653                                        mListeners.add(info);
654                                    } catch (RemoteException e) {
655                                        // already dead
656                                    }
657                                }
658                            }
659
660                            @Override
661                            public void onServiceDisconnected(ComponentName name) {
662                                Slog.v(TAG, "notification listener connection lost: " + name);
663                            }
664                        },
665                        Context.BIND_AUTO_CREATE,
666                        new UserHandle(userid)))
667                {
668                    mServicesBinding.remove(servicesBindingTag);
669                    Slog.w(TAG, "Unable to bind listener service: " + intent);
670                    return;
671                }
672            } catch (SecurityException ex) {
673                Slog.e(TAG, "Unable to bind listener service: " + intent, ex);
674                return;
675            }
676        }
677    }
678
679    /**
680     * Remove a listener binder directly
681     */
682    @Override
683    public void unregisterListener(INotificationListener listener, int userid) {
684        // no need to check permissions; if your listener binder is in the list,
685        // that's proof that you had permission to add it in the first place
686
687        synchronized (mNotificationList) {
688            final int N = mListeners.size();
689            for (int i=N-1; i>=0; i--) {
690                final NotificationListenerInfo info = mListeners.get(i);
691                if (info.listener == listener && info.userid == userid) {
692                    mListeners.remove(i);
693                    if (info.connection != null) {
694                        mContext.unbindService(info.connection);
695                    }
696                }
697            }
698        }
699    }
700
701    /**
702     * Remove a listener service for the given user by ComponentName
703     */
704    private void unregisterListenerService(ComponentName name, int userid) {
705        checkCallerIsSystem();
706
707        synchronized (mNotificationList) {
708            final int N = mListeners.size();
709            for (int i=N-1; i>=0; i--) {
710                final NotificationListenerInfo info = mListeners.get(i);
711                if (name.equals(info.component)
712                        && info.userid == userid) {
713                    mListeners.remove(i);
714                    if (info.connection != null) {
715                        mContext.unbindService(info.connection);
716                    }
717                }
718            }
719        }
720    }
721
722    /**
723     * asynchronously notify all listeners about a new notification
724     */
725    private void notifyPostedLocked(NotificationRecord n) {
726        final StatusBarNotification sbn = n.sbn;
727        for (final NotificationListenerInfo info : mListeners) {
728            mHandler.post(new Runnable() {
729                @Override
730                public void run() {
731                    info.notifyPostedIfUserMatch(sbn);
732                }});
733        }
734    }
735
736    /**
737     * asynchronously notify all listeners about a removed notification
738     */
739    private void notifyRemovedLocked(NotificationRecord n) {
740        final StatusBarNotification sbn = n.sbn;
741        for (final NotificationListenerInfo info : mListeners) {
742            mHandler.post(new Runnable() {
743                @Override
744                public void run() {
745                    info.notifyRemovedIfUserMatch(sbn);
746                }});
747        }
748    }
749
750    // -- APIs to support listeners clicking/clearing notifications --
751
752    private NotificationListenerInfo checkListenerToken(INotificationListener listener) {
753        final IBinder token = listener.asBinder();
754        final int N = mListeners.size();
755        for (int i=0; i<N; i++) {
756            final NotificationListenerInfo info = mListeners.get(i);
757            if (info.listener.asBinder() == token) return info;
758        }
759        throw new SecurityException("Disallowed call from unknown listener: " + listener);
760    }
761
762    /**
763     * Allow an INotificationListener to simulate a "clear all" operation.
764     *
765     * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
766     *
767     * @param token The binder for the listener, to check that the caller is allowed
768     */
769    public void clearAllNotificationsFromListener(INotificationListener token) {
770        NotificationListenerInfo info = checkListenerToken(token);
771        long identity = Binder.clearCallingIdentity();
772        try {
773            cancelAll(info.userid);
774        } finally {
775            Binder.restoreCallingIdentity(identity);
776        }
777    }
778
779    /**
780     * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
781     *
782     * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
783     *
784     * @param token The binder for the listener, to check that the caller is allowed
785     */
786    public void clearNotificationFromListener(INotificationListener token, String pkg, String tag, int id) {
787        NotificationListenerInfo info = checkListenerToken(token);
788        long identity = Binder.clearCallingIdentity();
789        try {
790            cancelNotification(pkg, tag, id, 0,
791                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
792                    true,
793                    info.userid);
794        } finally {
795            Binder.restoreCallingIdentity(identity);
796        }
797    }
798
799    // -- end of listener APIs --
800
801    public static final class NotificationRecord
802    {
803        final StatusBarNotification sbn;
804        IBinder statusBarKey;
805
806        NotificationRecord(StatusBarNotification sbn)
807        {
808            this.sbn = sbn;
809        }
810
811        public Notification getNotification() { return sbn.notification; }
812        public int getFlags() { return sbn.notification.flags; }
813        public int getUserId() { return sbn.getUserId(); }
814
815        void dump(PrintWriter pw, String prefix, Context baseContext) {
816            final Notification notification = sbn.notification;
817            pw.println(prefix + this);
818            pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon)
819                    + " / " + idDebugString(baseContext, this.sbn.pkg, notification.icon));
820            pw.println(prefix + "  pri=" + notification.priority);
821            pw.println(prefix + "  score=" + this.sbn.score);
822            pw.println(prefix + "  contentIntent=" + notification.contentIntent);
823            pw.println(prefix + "  deleteIntent=" + notification.deleteIntent);
824            pw.println(prefix + "  tickerText=" + notification.tickerText);
825            pw.println(prefix + "  contentView=" + notification.contentView);
826            pw.println(prefix + "  uid=" + this.sbn.uid + " userId=" + this.sbn.getUserId());
827            pw.println(prefix + "  defaults=0x" + Integer.toHexString(notification.defaults));
828            pw.println(prefix + "  flags=0x" + Integer.toHexString(notification.flags));
829            pw.println(prefix + "  sound=" + notification.sound);
830            pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate));
831            pw.println(prefix + "  ledARGB=0x" + Integer.toHexString(notification.ledARGB)
832                    + " ledOnMS=" + notification.ledOnMS
833                    + " ledOffMS=" + notification.ledOffMS);
834        }
835
836        @Override
837        public final String toString() {
838            return String.format(
839                    "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d: %s)",
840                    System.identityHashCode(this),
841                    this.sbn.pkg, this.sbn.user, this.sbn.id, this.sbn.tag,
842                    this.sbn.score, this.sbn.notification);
843        }
844    }
845
846    private static final class ToastRecord
847    {
848        final int pid;
849        final String pkg;
850        final ITransientNotification callback;
851        int duration;
852
853        ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
854        {
855            this.pid = pid;
856            this.pkg = pkg;
857            this.callback = callback;
858            this.duration = duration;
859        }
860
861        void update(int duration) {
862            this.duration = duration;
863        }
864
865        void dump(PrintWriter pw, String prefix) {
866            pw.println(prefix + this);
867        }
868
869        @Override
870        public final String toString()
871        {
872            return "ToastRecord{"
873                + Integer.toHexString(System.identityHashCode(this))
874                + " pkg=" + pkg
875                + " callback=" + callback
876                + " duration=" + duration;
877        }
878    }
879
880    private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
881            = new StatusBarManagerService.NotificationCallbacks() {
882
883        public void onSetDisabled(int status) {
884            synchronized (mNotificationList) {
885                mDisabledNotifications = status;
886                if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
887                    // cancel whatever's going on
888                    long identity = Binder.clearCallingIdentity();
889                    try {
890                        final IRingtonePlayer player = mAudioService.getRingtonePlayer();
891                        if (player != null) {
892                            player.stopAsync();
893                        }
894                    } catch (RemoteException e) {
895                    } finally {
896                        Binder.restoreCallingIdentity(identity);
897                    }
898
899                    identity = Binder.clearCallingIdentity();
900                    try {
901                        mVibrator.cancel();
902                    } finally {
903                        Binder.restoreCallingIdentity(identity);
904                    }
905                }
906            }
907        }
908
909        public void onClearAll() {
910            // XXX to be totally correct, the caller should tell us which user
911            // this is for.
912            cancelAll(ActivityManager.getCurrentUser());
913        }
914
915        public void onNotificationClick(String pkg, String tag, int id) {
916            // XXX to be totally correct, the caller should tell us which user
917            // this is for.
918            cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
919                    Notification.FLAG_FOREGROUND_SERVICE, false,
920                    ActivityManager.getCurrentUser());
921        }
922
923        public void onNotificationClear(String pkg, String tag, int id) {
924            // XXX to be totally correct, the caller should tell us which user
925            // this is for.
926            cancelNotification(pkg, tag, id, 0,
927                Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
928                true, ActivityManager.getCurrentUser());
929        }
930
931        public void onPanelRevealed() {
932            synchronized (mNotificationList) {
933                // sound
934                mSoundNotification = null;
935
936                long identity = Binder.clearCallingIdentity();
937                try {
938                    final IRingtonePlayer player = mAudioService.getRingtonePlayer();
939                    if (player != null) {
940                        player.stopAsync();
941                    }
942                } catch (RemoteException e) {
943                } finally {
944                    Binder.restoreCallingIdentity(identity);
945                }
946
947                // vibrate
948                mVibrateNotification = null;
949                identity = Binder.clearCallingIdentity();
950                try {
951                    mVibrator.cancel();
952                } finally {
953                    Binder.restoreCallingIdentity(identity);
954                }
955
956                // light
957                mLights.clear();
958                mLedNotification = null;
959                updateLightsLocked();
960            }
961        }
962
963        public void onNotificationError(String pkg, String tag, int id,
964                int uid, int initialPid, String message) {
965            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
966                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
967            // XXX to be totally correct, the caller should tell us which user
968            // this is for.
969            cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid));
970            long ident = Binder.clearCallingIdentity();
971            try {
972                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
973                        "Bad notification posted from package " + pkg
974                        + ": " + message);
975            } catch (RemoteException e) {
976            }
977            Binder.restoreCallingIdentity(ident);
978        }
979    };
980
981    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
982        @Override
983        public void onReceive(Context context, Intent intent) {
984            String action = intent.getAction();
985
986            boolean queryRestart = false;
987            boolean packageChanged = false;
988
989            if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
990                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
991                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
992                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
993                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
994                String pkgList[] = null;
995                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
996                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
997                } else if (queryRestart) {
998                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
999                } else {
1000                    Uri uri = intent.getData();
1001                    if (uri == null) {
1002                        return;
1003                    }
1004                    String pkgName = uri.getSchemeSpecificPart();
1005                    if (pkgName == null) {
1006                        return;
1007                    }
1008                    if (packageChanged) {
1009                        // We cancel notifications for packages which have just been disabled
1010                        final int enabled = mContext.getPackageManager()
1011                                .getApplicationEnabledSetting(pkgName);
1012                        if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
1013                                || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
1014                            return;
1015                        }
1016                    }
1017                    pkgList = new String[]{pkgName};
1018                }
1019
1020                boolean anyListenersInvolved = false;
1021                if (pkgList != null && (pkgList.length > 0)) {
1022                    for (String pkgName : pkgList) {
1023                        cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
1024                                UserHandle.USER_ALL);
1025                        if (mEnabledListenerPackageNames.contains(pkgName)) {
1026                            anyListenersInvolved = true;
1027                        }
1028                    }
1029                }
1030
1031                if (anyListenersInvolved) {
1032                    // make sure we're still bound to any of our
1033                    // listeners who may have just upgraded
1034                    rebindListenerServices();
1035                }
1036            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1037                // Keep track of screen on/off state, but do not turn off the notification light
1038                // until user passes through the lock screen or views the notification.
1039                mScreenOn = true;
1040            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1041                mScreenOn = false;
1042            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
1043                mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
1044                        TelephonyManager.EXTRA_STATE_OFFHOOK));
1045                updateNotificationPulse();
1046            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
1047                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
1048                if (userHandle >= 0) {
1049                    cancelAllNotificationsInt(null, 0, 0, true, userHandle);
1050                }
1051            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
1052                // turn off LED when user passes through lock screen
1053                mNotificationLight.turnOff();
1054            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
1055                // reload per-user settings
1056                mSettingsObserver.update(null);
1057            }
1058        }
1059    };
1060
1061    class SettingsObserver extends ContentObserver {
1062        private final Uri NOTIFICATION_LIGHT_PULSE_URI
1063                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
1064
1065        private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
1066                = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
1067
1068        SettingsObserver(Handler handler) {
1069            super(handler);
1070        }
1071
1072        void observe() {
1073            ContentResolver resolver = mContext.getContentResolver();
1074            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
1075                    false, this, UserHandle.USER_ALL);
1076            resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
1077                    false, this, UserHandle.USER_ALL);
1078            update(null);
1079        }
1080
1081        @Override public void onChange(boolean selfChange, Uri uri) {
1082            update(uri);
1083        }
1084
1085        public void update(Uri uri) {
1086            ContentResolver resolver = mContext.getContentResolver();
1087            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
1088                boolean pulseEnabled = Settings.System.getInt(resolver,
1089                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
1090                if (mNotificationPulseEnabled != pulseEnabled) {
1091                    mNotificationPulseEnabled = pulseEnabled;
1092                    updateNotificationPulse();
1093                }
1094            }
1095            if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
1096                rebindListenerServices();
1097            }
1098        }
1099    }
1100
1101    private SettingsObserver mSettingsObserver;
1102
1103    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
1104        int[] ar = r.getIntArray(resid);
1105        if (ar == null) {
1106            return def;
1107        }
1108        final int len = ar.length > maxlen ? maxlen : ar.length;
1109        long[] out = new long[len];
1110        for (int i=0; i<len; i++) {
1111            out[i] = ar[i];
1112        }
1113        return out;
1114    }
1115
1116    NotificationManagerService(Context context, StatusBarManagerService statusBar,
1117            LightsService lights)
1118    {
1119        super();
1120        mContext = context;
1121        mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
1122        mAm = ActivityManagerNative.getDefault();
1123        mUserManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
1124        mToastQueue = new ArrayList<ToastRecord>();
1125        mHandler = new WorkerHandler();
1126
1127        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
1128
1129        importOldBlockDb();
1130
1131        mStatusBar = statusBar;
1132        statusBar.setNotificationCallbacks(mNotificationCallbacks);
1133
1134        mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
1135        mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
1136
1137        Resources resources = mContext.getResources();
1138        mDefaultNotificationColor = resources.getColor(
1139                com.android.internal.R.color.config_defaultNotificationColor);
1140        mDefaultNotificationLedOn = resources.getInteger(
1141                com.android.internal.R.integer.config_defaultNotificationLedOn);
1142        mDefaultNotificationLedOff = resources.getInteger(
1143                com.android.internal.R.integer.config_defaultNotificationLedOff);
1144
1145        mDefaultVibrationPattern = getLongArray(resources,
1146                com.android.internal.R.array.config_defaultNotificationVibePattern,
1147                VIBRATE_PATTERN_MAXLEN,
1148                DEFAULT_VIBRATE_PATTERN);
1149
1150        mFallbackVibrationPattern = getLongArray(resources,
1151                com.android.internal.R.array.config_notificationFallbackVibePattern,
1152                VIBRATE_PATTERN_MAXLEN,
1153                DEFAULT_VIBRATE_PATTERN);
1154
1155        // Don't start allowing notifications until the setup wizard has run once.
1156        // After that, including subsequent boots, init with notifications turned on.
1157        // This works on the first boot because the setup wizard will toggle this
1158        // flag at least once and we'll go back to 0 after that.
1159        if (0 == Settings.Global.getInt(mContext.getContentResolver(),
1160                    Settings.Global.DEVICE_PROVISIONED, 0)) {
1161            mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
1162        }
1163
1164        // register for various Intents
1165        IntentFilter filter = new IntentFilter();
1166        filter.addAction(Intent.ACTION_SCREEN_ON);
1167        filter.addAction(Intent.ACTION_SCREEN_OFF);
1168        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1169        filter.addAction(Intent.ACTION_USER_PRESENT);
1170        filter.addAction(Intent.ACTION_USER_STOPPED);
1171        filter.addAction(Intent.ACTION_USER_SWITCHED);
1172        mContext.registerReceiver(mIntentReceiver, filter);
1173        IntentFilter pkgFilter = new IntentFilter();
1174        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1175        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1176        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1177        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1178        pkgFilter.addDataScheme("package");
1179        mContext.registerReceiver(mIntentReceiver, pkgFilter);
1180        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1181        mContext.registerReceiver(mIntentReceiver, sdFilter);
1182
1183        mSettingsObserver = new SettingsObserver(mHandler);
1184        mSettingsObserver.observe();
1185    }
1186
1187    /**
1188     * Read the old XML-based app block database and import those blockages into the AppOps system.
1189     */
1190    private void importOldBlockDb() {
1191        loadBlockDb();
1192
1193        PackageManager pm = mContext.getPackageManager();
1194        for (String pkg : mBlockedPackages) {
1195            PackageInfo info = null;
1196            try {
1197                info = pm.getPackageInfo(pkg, 0);
1198                setNotificationsEnabledForPackage(pkg, info.applicationInfo.uid, false);
1199            } catch (NameNotFoundException e) {
1200                // forget you
1201            }
1202        }
1203        mBlockedPackages.clear();
1204        if (mPolicyFile != null) {
1205            mPolicyFile.delete();
1206        }
1207    }
1208
1209    void systemReady() {
1210        mAudioService = IAudioService.Stub.asInterface(
1211                ServiceManager.getService(Context.AUDIO_SERVICE));
1212
1213        // no beeping until we're basically done booting
1214        mSystemReady = true;
1215
1216        // make sure our listener services are properly bound
1217        rebindListenerServices();
1218    }
1219
1220    // Toasts
1221    // ============================================================================
1222    public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1223    {
1224        if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);
1225
1226        if (pkg == null || callback == null) {
1227            Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1228            return ;
1229        }
1230
1231        final boolean isSystemToast = ("android".equals(pkg));
1232
1233        if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1234            if (!isSystemToast) {
1235                Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1236                return;
1237            }
1238        }
1239
1240        synchronized (mToastQueue) {
1241            int callingPid = Binder.getCallingPid();
1242            long callingId = Binder.clearCallingIdentity();
1243            try {
1244                ToastRecord record;
1245                int index = indexOfToastLocked(pkg, callback);
1246                // If it's already in the queue, we update it in place, we don't
1247                // move it to the end of the queue.
1248                if (index >= 0) {
1249                    record = mToastQueue.get(index);
1250                    record.update(duration);
1251                } else {
1252                    // Limit the number of toasts that any given package except the android
1253                    // package can enqueue.  Prevents DOS attacks and deals with leaks.
1254                    if (!isSystemToast) {
1255                        int count = 0;
1256                        final int N = mToastQueue.size();
1257                        for (int i=0; i<N; i++) {
1258                             final ToastRecord r = mToastQueue.get(i);
1259                             if (r.pkg.equals(pkg)) {
1260                                 count++;
1261                                 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1262                                     Slog.e(TAG, "Package has already posted " + count
1263                                            + " toasts. Not showing more. Package=" + pkg);
1264                                     return;
1265                                 }
1266                             }
1267                        }
1268                    }
1269
1270                    record = new ToastRecord(callingPid, pkg, callback, duration);
1271                    mToastQueue.add(record);
1272                    index = mToastQueue.size() - 1;
1273                    keepProcessAliveLocked(callingPid);
1274                }
1275                // If it's at index 0, it's the current toast.  It doesn't matter if it's
1276                // new or just been updated.  Call back and tell it to show itself.
1277                // If the callback fails, this will remove it from the list, so don't
1278                // assume that it's valid after this.
1279                if (index == 0) {
1280                    showNextToastLocked();
1281                }
1282            } finally {
1283                Binder.restoreCallingIdentity(callingId);
1284            }
1285        }
1286    }
1287
1288    public void cancelToast(String pkg, ITransientNotification callback) {
1289        Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1290
1291        if (pkg == null || callback == null) {
1292            Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1293            return ;
1294        }
1295
1296        synchronized (mToastQueue) {
1297            long callingId = Binder.clearCallingIdentity();
1298            try {
1299                int index = indexOfToastLocked(pkg, callback);
1300                if (index >= 0) {
1301                    cancelToastLocked(index);
1302                } else {
1303                    Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback);
1304                }
1305            } finally {
1306                Binder.restoreCallingIdentity(callingId);
1307            }
1308        }
1309    }
1310
1311    private void showNextToastLocked() {
1312        ToastRecord record = mToastQueue.get(0);
1313        while (record != null) {
1314            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1315            try {
1316                record.callback.show();
1317                scheduleTimeoutLocked(record, false);
1318                return;
1319            } catch (RemoteException e) {
1320                Slog.w(TAG, "Object died trying to show notification " + record.callback
1321                        + " in package " + record.pkg);
1322                // remove it from the list and let the process die
1323                int index = mToastQueue.indexOf(record);
1324                if (index >= 0) {
1325                    mToastQueue.remove(index);
1326                }
1327                keepProcessAliveLocked(record.pid);
1328                if (mToastQueue.size() > 0) {
1329                    record = mToastQueue.get(0);
1330                } else {
1331                    record = null;
1332                }
1333            }
1334        }
1335    }
1336
1337    private void cancelToastLocked(int index) {
1338        ToastRecord record = mToastQueue.get(index);
1339        try {
1340            record.callback.hide();
1341        } catch (RemoteException e) {
1342            Slog.w(TAG, "Object died trying to hide notification " + record.callback
1343                    + " in package " + record.pkg);
1344            // don't worry about this, we're about to remove it from
1345            // the list anyway
1346        }
1347        mToastQueue.remove(index);
1348        keepProcessAliveLocked(record.pid);
1349        if (mToastQueue.size() > 0) {
1350            // Show the next one. If the callback fails, this will remove
1351            // it from the list, so don't assume that the list hasn't changed
1352            // after this point.
1353            showNextToastLocked();
1354        }
1355    }
1356
1357    private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
1358    {
1359        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1360        long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
1361        mHandler.removeCallbacksAndMessages(r);
1362        mHandler.sendMessageDelayed(m, delay);
1363    }
1364
1365    private void handleTimeout(ToastRecord record)
1366    {
1367        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1368        synchronized (mToastQueue) {
1369            int index = indexOfToastLocked(record.pkg, record.callback);
1370            if (index >= 0) {
1371                cancelToastLocked(index);
1372            }
1373        }
1374    }
1375
1376    // lock on mToastQueue
1377    private int indexOfToastLocked(String pkg, ITransientNotification callback)
1378    {
1379        IBinder cbak = callback.asBinder();
1380        ArrayList<ToastRecord> list = mToastQueue;
1381        int len = list.size();
1382        for (int i=0; i<len; i++) {
1383            ToastRecord r = list.get(i);
1384            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1385                return i;
1386            }
1387        }
1388        return -1;
1389    }
1390
1391    // lock on mToastQueue
1392    private void keepProcessAliveLocked(int pid)
1393    {
1394        int toastCount = 0; // toasts from this pid
1395        ArrayList<ToastRecord> list = mToastQueue;
1396        int N = list.size();
1397        for (int i=0; i<N; i++) {
1398            ToastRecord r = list.get(i);
1399            if (r.pid == pid) {
1400                toastCount++;
1401            }
1402        }
1403        try {
1404            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1405        } catch (RemoteException e) {
1406            // Shouldn't happen.
1407        }
1408    }
1409
1410    private final class WorkerHandler extends Handler
1411    {
1412        @Override
1413        public void handleMessage(Message msg)
1414        {
1415            switch (msg.what)
1416            {
1417                case MESSAGE_TIMEOUT:
1418                    handleTimeout((ToastRecord)msg.obj);
1419                    break;
1420            }
1421        }
1422    }
1423
1424
1425    // Notifications
1426    // ============================================================================
1427    public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id,
1428            Notification notification, int[] idOut, int userId)
1429    {
1430        enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(), Binder.getCallingPid(),
1431                tag, id, notification, idOut, userId);
1432    }
1433
1434    private final static int clamp(int x, int low, int high) {
1435        return (x < low) ? low : ((x > high) ? high : x);
1436    }
1437
1438    // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
1439    // uid/pid of another application)
1440    public void enqueueNotificationInternal(String pkg, String basePkg, int callingUid,
1441            int callingPid, String tag, int id, Notification notification, int[] idOut, int userId)
1442    {
1443        if (DBG) {
1444            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
1445        }
1446        checkCallerIsSystemOrSameApp(pkg);
1447        final boolean isSystemNotification = ("android".equals(pkg));
1448
1449        userId = ActivityManager.handleIncomingUser(callingPid,
1450                callingUid, userId, true, false, "enqueueNotification", pkg);
1451        final UserHandle user = new UserHandle(userId);
1452
1453        // Limit the number of notifications that any given package except the android
1454        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1455        if (!isSystemNotification) {
1456            synchronized (mNotificationList) {
1457                int count = 0;
1458                final int N = mNotificationList.size();
1459                for (int i=0; i<N; i++) {
1460                    final NotificationRecord r = mNotificationList.get(i);
1461                    if (r.sbn.pkg.equals(pkg) && r.sbn.getUserId() == userId) {
1462                        count++;
1463                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1464                            Slog.e(TAG, "Package has already posted " + count
1465                                    + " notifications.  Not showing more.  package=" + pkg);
1466                            return;
1467                        }
1468                    }
1469                }
1470            }
1471        }
1472
1473        // This conditional is a dirty hack to limit the logging done on
1474        //     behalf of the download manager without affecting other apps.
1475        if (!pkg.equals("com.android.providers.downloads")
1476                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1477            EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId,
1478                    notification.toString());
1479        }
1480
1481        if (pkg == null || notification == null) {
1482            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1483                    + " id=" + id + " notification=" + notification);
1484        }
1485        if (notification.icon != 0) {
1486            if (notification.contentView == null) {
1487                throw new IllegalArgumentException("contentView required: pkg=" + pkg
1488                        + " id=" + id + " notification=" + notification);
1489            }
1490        }
1491
1492        // === Scoring ===
1493
1494        // 0. Sanitize inputs
1495        notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX);
1496        // Migrate notification flags to scores
1497        if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1498            if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX;
1499        } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1500            if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH;
1501        }
1502
1503        // 1. initial score: buckets of 10, around the app
1504        int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1505
1506        // 2. Consult external heuristics (TBD)
1507
1508        // 3. Apply local rules
1509
1510        // blocked apps
1511        if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1512            if (!isSystemNotification) {
1513                score = JUNK_SCORE;
1514                Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request.");
1515            }
1516        }
1517
1518        if (DBG) {
1519            Slog.v(TAG, "Assigned score=" + score + " to " + notification);
1520        }
1521
1522        if (score < SCORE_DISPLAY_THRESHOLD) {
1523            // Notification will be blocked because the score is too low.
1524            return;
1525        }
1526
1527        // Should this notification make noise, vibe, or use the LED?
1528        final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
1529
1530        synchronized (mNotificationList) {
1531            final StatusBarNotification n = new StatusBarNotification(
1532                    pkg, id, tag, callingUid, callingPid, score, notification, user);
1533            NotificationRecord r = new NotificationRecord(n);
1534            NotificationRecord old = null;
1535
1536            int index = indexOfNotificationLocked(pkg, tag, id, userId);
1537            if (index < 0) {
1538                mNotificationList.add(r);
1539            } else {
1540                old = mNotificationList.remove(index);
1541                mNotificationList.add(index, r);
1542                // Make sure we don't lose the foreground service state.
1543                if (old != null) {
1544                    notification.flags |=
1545                        old.getNotification().flags&Notification.FLAG_FOREGROUND_SERVICE;
1546                }
1547            }
1548
1549            // Ensure if this is a foreground service that the proper additional
1550            // flags are set.
1551            if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1552                notification.flags |= Notification.FLAG_ONGOING_EVENT
1553                        | Notification.FLAG_NO_CLEAR;
1554            }
1555
1556            final int currentUser;
1557            final long token = Binder.clearCallingIdentity();
1558            try {
1559                currentUser = ActivityManager.getCurrentUser();
1560            } finally {
1561                Binder.restoreCallingIdentity(token);
1562            }
1563
1564            if (notification.icon != 0) {
1565                if (old != null && old.statusBarKey != null) {
1566                    r.statusBarKey = old.statusBarKey;
1567                    long identity = Binder.clearCallingIdentity();
1568                    try {
1569                        mStatusBar.updateNotification(r.statusBarKey, n);
1570                    }
1571                    finally {
1572                        Binder.restoreCallingIdentity(identity);
1573                    }
1574                } else {
1575                    long identity = Binder.clearCallingIdentity();
1576                    try {
1577                        r.statusBarKey = mStatusBar.addNotification(n);
1578                        if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1579                                && canInterrupt) {
1580                            mAttentionLight.pulse();
1581                        }
1582                    }
1583                    finally {
1584                        Binder.restoreCallingIdentity(identity);
1585                    }
1586                }
1587                // Send accessibility events only for the current user.
1588                if (currentUser == userId) {
1589                    sendAccessibilityEvent(notification, pkg);
1590                }
1591
1592                notifyPostedLocked(r);
1593            } else {
1594                Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
1595                if (old != null && old.statusBarKey != null) {
1596                    long identity = Binder.clearCallingIdentity();
1597                    try {
1598                        mStatusBar.removeNotification(old.statusBarKey);
1599                    }
1600                    finally {
1601                        Binder.restoreCallingIdentity(identity);
1602                    }
1603
1604                    notifyRemovedLocked(r);
1605                }
1606                return; // do not play sounds, show lights, etc. for invalid notifications
1607            }
1608
1609            // If we're not supposed to beep, vibrate, etc. then don't.
1610            if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
1611                    && (!(old != null
1612                        && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1613                    && (r.getUserId() == UserHandle.USER_ALL ||
1614                        (r.getUserId() == userId && r.getUserId() == currentUser))
1615                    && canInterrupt
1616                    && mSystemReady) {
1617
1618                final AudioManager audioManager = (AudioManager) mContext
1619                .getSystemService(Context.AUDIO_SERVICE);
1620
1621                // sound
1622                final boolean useDefaultSound =
1623                    (notification.defaults & Notification.DEFAULT_SOUND) != 0;
1624
1625                Uri soundUri = null;
1626                boolean hasValidSound = false;
1627
1628                if (useDefaultSound) {
1629                    soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1630
1631                    // check to see if the default notification sound is silent
1632                    ContentResolver resolver = mContext.getContentResolver();
1633                    hasValidSound = Settings.System.getString(resolver,
1634                           Settings.System.NOTIFICATION_SOUND) != null;
1635                } else if (notification.sound != null) {
1636                    soundUri = notification.sound;
1637                    hasValidSound = (soundUri != null);
1638                }
1639
1640                if (hasValidSound) {
1641                    boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
1642                    int audioStreamType;
1643                    if (notification.audioStreamType >= 0) {
1644                        audioStreamType = notification.audioStreamType;
1645                    } else {
1646                        audioStreamType = DEFAULT_STREAM_TYPE;
1647                    }
1648                    mSoundNotification = r;
1649                    // do not play notifications if stream volume is 0
1650                    // (typically because ringer mode is silent) or if speech recognition is active.
1651                    if ((audioManager.getStreamVolume(audioStreamType) != 0)
1652                            && !audioManager.isSpeechRecognitionActive()) {
1653                        final long identity = Binder.clearCallingIdentity();
1654                        try {
1655                            final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1656                            if (player != null) {
1657                                player.playAsync(soundUri, user, looping, audioStreamType);
1658                            }
1659                        } catch (RemoteException e) {
1660                        } finally {
1661                            Binder.restoreCallingIdentity(identity);
1662                        }
1663                    }
1664                }
1665
1666                // vibrate
1667                // Does the notification want to specify its own vibration?
1668                final boolean hasCustomVibrate = notification.vibrate != null;
1669
1670                // new in 4.2: if there was supposed to be a sound and we're in vibrate mode,
1671                // and no other vibration is specified, we fall back to vibration
1672                final boolean convertSoundToVibration =
1673                           !hasCustomVibrate
1674                        && hasValidSound
1675                        && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
1676
1677                // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1678                final boolean useDefaultVibrate =
1679                        (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1680
1681                if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1682                        && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {
1683                    mVibrateNotification = r;
1684
1685                    if (useDefaultVibrate || convertSoundToVibration) {
1686                        // Escalate privileges so we can use the vibrator even if the notifying app
1687                        // does not have the VIBRATE permission.
1688                        long identity = Binder.clearCallingIdentity();
1689                        try {
1690                            mVibrator.vibrate(r.sbn.uid, r.sbn.basePkg,
1691                                useDefaultVibrate ? mDefaultVibrationPattern
1692                                    : mFallbackVibrationPattern,
1693                                ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
1694                        } finally {
1695                            Binder.restoreCallingIdentity(identity);
1696                        }
1697                    } else if (notification.vibrate.length > 1) {
1698                        // If you want your own vibration pattern, you need the VIBRATE permission
1699                        mVibrator.vibrate(r.sbn.uid, r.sbn.basePkg, notification.vibrate,
1700                            ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
1701                    }
1702                }
1703            }
1704
1705            // light
1706            // the most recent thing gets the light
1707            mLights.remove(old);
1708            if (mLedNotification == old) {
1709                mLedNotification = null;
1710            }
1711            //Slog.i(TAG, "notification.lights="
1712            //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
1713            if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1714                    && canInterrupt) {
1715                mLights.add(r);
1716                updateLightsLocked();
1717            } else {
1718                if (old != null
1719                        && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
1720                    updateLightsLocked();
1721                }
1722            }
1723        }
1724
1725        idOut[0] = id;
1726    }
1727
1728    private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
1729        AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
1730        if (!manager.isEnabled()) {
1731            return;
1732        }
1733
1734        AccessibilityEvent event =
1735            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1736        event.setPackageName(packageName);
1737        event.setClassName(Notification.class.getName());
1738        event.setParcelableData(notification);
1739        CharSequence tickerText = notification.tickerText;
1740        if (!TextUtils.isEmpty(tickerText)) {
1741            event.getText().add(tickerText);
1742        }
1743
1744        manager.sendAccessibilityEvent(event);
1745    }
1746
1747    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
1748        // tell the app
1749        if (sendDelete) {
1750            if (r.getNotification().deleteIntent != null) {
1751                try {
1752                    r.getNotification().deleteIntent.send();
1753                } catch (PendingIntent.CanceledException ex) {
1754                    // do nothing - there's no relevant way to recover, and
1755                    //     no reason to let this propagate
1756                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.pkg, ex);
1757                }
1758            }
1759        }
1760
1761        // status bar
1762        if (r.getNotification().icon != 0) {
1763            long identity = Binder.clearCallingIdentity();
1764            try {
1765                mStatusBar.removeNotification(r.statusBarKey);
1766            }
1767            finally {
1768                Binder.restoreCallingIdentity(identity);
1769            }
1770            r.statusBarKey = null;
1771            notifyRemovedLocked(r);
1772        }
1773
1774        // sound
1775        if (mSoundNotification == r) {
1776            mSoundNotification = null;
1777            final long identity = Binder.clearCallingIdentity();
1778            try {
1779                final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1780                if (player != null) {
1781                    player.stopAsync();
1782                }
1783            } catch (RemoteException e) {
1784            } finally {
1785                Binder.restoreCallingIdentity(identity);
1786            }
1787        }
1788
1789        // vibrate
1790        if (mVibrateNotification == r) {
1791            mVibrateNotification = null;
1792            long identity = Binder.clearCallingIdentity();
1793            try {
1794                mVibrator.cancel();
1795            }
1796            finally {
1797                Binder.restoreCallingIdentity(identity);
1798            }
1799        }
1800
1801        // light
1802        mLights.remove(r);
1803        if (mLedNotification == r) {
1804            mLedNotification = null;
1805        }
1806
1807        // Save it for users of getHistoricalNotifications()
1808        mArchive.record(r.sbn);
1809    }
1810
1811    /**
1812     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
1813     * and none of the {@code mustNotHaveFlags}.
1814     */
1815    private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
1816            int mustNotHaveFlags, boolean sendDelete, int userId) {
1817        EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId,
1818                mustHaveFlags, mustNotHaveFlags);
1819
1820        synchronized (mNotificationList) {
1821            int index = indexOfNotificationLocked(pkg, tag, id, userId);
1822            if (index >= 0) {
1823                NotificationRecord r = mNotificationList.get(index);
1824
1825                if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
1826                    return;
1827                }
1828                if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
1829                    return;
1830                }
1831
1832                mNotificationList.remove(index);
1833
1834                cancelNotificationLocked(r, sendDelete);
1835                updateLightsLocked();
1836            }
1837        }
1838    }
1839
1840    /**
1841     * Determine whether the userId applies to the notification in question, either because
1842     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
1843     */
1844    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
1845        return
1846                // looking for USER_ALL notifications? match everything
1847                   userId == UserHandle.USER_ALL
1848                // a notification sent to USER_ALL matches any query
1849                || r.getUserId() == UserHandle.USER_ALL
1850                // an exact user match
1851                || r.getUserId() == userId;
1852    }
1853
1854    /**
1855     * Cancels all notifications from a given package that have all of the
1856     * {@code mustHaveFlags}.
1857     */
1858    boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
1859            int mustNotHaveFlags, boolean doit, int userId) {
1860        EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, userId,
1861                mustHaveFlags, mustNotHaveFlags);
1862
1863        synchronized (mNotificationList) {
1864            final int N = mNotificationList.size();
1865            boolean canceledSomething = false;
1866            for (int i = N-1; i >= 0; --i) {
1867                NotificationRecord r = mNotificationList.get(i);
1868                if (!notificationMatchesUserId(r, userId)) {
1869                    continue;
1870                }
1871                // Don't remove notifications to all, if there's no package name specified
1872                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
1873                    continue;
1874                }
1875                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
1876                    continue;
1877                }
1878                if ((r.getFlags() & mustNotHaveFlags) != 0) {
1879                    continue;
1880                }
1881                if (pkg != null && !r.sbn.pkg.equals(pkg)) {
1882                    continue;
1883                }
1884                canceledSomething = true;
1885                if (!doit) {
1886                    return true;
1887                }
1888                mNotificationList.remove(i);
1889                cancelNotificationLocked(r, false);
1890            }
1891            if (canceledSomething) {
1892                updateLightsLocked();
1893            }
1894            return canceledSomething;
1895        }
1896    }
1897
1898    public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1899        checkCallerIsSystemOrSameApp(pkg);
1900        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1901                Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1902        // Don't allow client applications to cancel foreground service notis.
1903        cancelNotification(pkg, tag, id, 0,
1904                Binder.getCallingUid() == Process.SYSTEM_UID
1905                ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
1906    }
1907
1908    public void cancelAllNotifications(String pkg, int userId) {
1909        checkCallerIsSystemOrSameApp(pkg);
1910
1911        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1912                Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1913
1914        // Calling from user space, don't allow the canceling of actively
1915        // running foreground services.
1916        cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
1917    }
1918
1919    void checkCallerIsSystem() {
1920        int uid = Binder.getCallingUid();
1921        if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
1922            return;
1923        }
1924        throw new SecurityException("Disallowed call for uid " + uid);
1925    }
1926
1927    void checkCallerIsSystemOrSameApp(String pkg) {
1928        int uid = Binder.getCallingUid();
1929        if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
1930            return;
1931        }
1932        try {
1933            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
1934                    pkg, 0, UserHandle.getCallingUserId());
1935            if (!UserHandle.isSameApp(ai.uid, uid)) {
1936                throw new SecurityException("Calling uid " + uid + " gave package"
1937                        + pkg + " which is owned by uid " + ai.uid);
1938            }
1939        } catch (RemoteException re) {
1940            throw new SecurityException("Unknown package " + pkg + "\n" + re);
1941        }
1942    }
1943
1944    void cancelAll(int userId) {
1945        synchronized (mNotificationList) {
1946            final int N = mNotificationList.size();
1947            for (int i=N-1; i>=0; i--) {
1948                NotificationRecord r = mNotificationList.get(i);
1949
1950                if (!notificationMatchesUserId(r, userId)) {
1951                    continue;
1952                }
1953
1954                if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
1955                                | Notification.FLAG_NO_CLEAR)) == 0) {
1956                    mNotificationList.remove(i);
1957                    cancelNotificationLocked(r, true);
1958                }
1959            }
1960
1961            updateLightsLocked();
1962        }
1963    }
1964
1965    // lock on mNotificationList
1966    private void updateLightsLocked()
1967    {
1968        // handle notification lights
1969        if (mLedNotification == null) {
1970            // get next notification, if any
1971            int n = mLights.size();
1972            if (n > 0) {
1973                mLedNotification = mLights.get(n-1);
1974            }
1975        }
1976
1977        // Don't flash while we are in a call or screen is on
1978        if (mLedNotification == null || mInCall || mScreenOn) {
1979            mNotificationLight.turnOff();
1980        } else {
1981            final Notification ledno = mLedNotification.sbn.notification;
1982            int ledARGB = ledno.ledARGB;
1983            int ledOnMS = ledno.ledOnMS;
1984            int ledOffMS = ledno.ledOffMS;
1985            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
1986                ledARGB = mDefaultNotificationColor;
1987                ledOnMS = mDefaultNotificationLedOn;
1988                ledOffMS = mDefaultNotificationLedOff;
1989            }
1990            if (mNotificationPulseEnabled) {
1991                // pulse repeatedly
1992                mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
1993                        ledOnMS, ledOffMS);
1994            }
1995        }
1996    }
1997
1998    // lock on mNotificationList
1999    private int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2000    {
2001        ArrayList<NotificationRecord> list = mNotificationList;
2002        final int len = list.size();
2003        for (int i=0; i<len; i++) {
2004            NotificationRecord r = list.get(i);
2005            if (!notificationMatchesUserId(r, userId) || r.sbn.id != id) {
2006                continue;
2007            }
2008            if (tag == null) {
2009                if (r.sbn.tag != null) {
2010                    continue;
2011                }
2012            } else {
2013                if (!tag.equals(r.sbn.tag)) {
2014                    continue;
2015                }
2016            }
2017            if (r.sbn.pkg.equals(pkg)) {
2018                return i;
2019            }
2020        }
2021        return -1;
2022    }
2023
2024    private void updateNotificationPulse() {
2025        synchronized (mNotificationList) {
2026            updateLightsLocked();
2027        }
2028    }
2029
2030    // ======================================================================
2031    @Override
2032    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2033        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2034                != PackageManager.PERMISSION_GRANTED) {
2035            pw.println("Permission Denial: can't dump NotificationManager from from pid="
2036                    + Binder.getCallingPid()
2037                    + ", uid=" + Binder.getCallingUid());
2038            return;
2039        }
2040
2041        pw.println("Current Notification Manager state:");
2042
2043        pw.println("  Listeners (" + mEnabledListenersForCurrentUser.size()
2044                + ") enabled for current user:");
2045        for (ComponentName cmpt : mEnabledListenersForCurrentUser) {
2046            pw.println("    " + cmpt);
2047        }
2048
2049        pw.println("  Live listeners (" + mListeners.size() + "):");
2050        for (NotificationListenerInfo info : mListeners) {
2051            pw.println("    " + info.component
2052                    + " (user " + info.userid + "): " + info.listener
2053                    + (info.isSystem?" SYSTEM":""));
2054        }
2055
2056        int N;
2057
2058        synchronized (mToastQueue) {
2059            N = mToastQueue.size();
2060            if (N > 0) {
2061                pw.println("  Toast Queue:");
2062                for (int i=0; i<N; i++) {
2063                    mToastQueue.get(i).dump(pw, "    ");
2064                }
2065                pw.println("  ");
2066            }
2067
2068        }
2069
2070        synchronized (mNotificationList) {
2071            N = mNotificationList.size();
2072            if (N > 0) {
2073                pw.println("  Notification List:");
2074                for (int i=0; i<N; i++) {
2075                    mNotificationList.get(i).dump(pw, "    ", mContext);
2076                }
2077                pw.println("  ");
2078            }
2079
2080            N = mLights.size();
2081            if (N > 0) {
2082                pw.println("  Lights List:");
2083                for (int i=0; i<N; i++) {
2084                    mLights.get(i).dump(pw, "    ", mContext);
2085                }
2086                pw.println("  ");
2087            }
2088
2089            pw.println("  mSoundNotification=" + mSoundNotification);
2090            pw.println("  mVibrateNotification=" + mVibrateNotification);
2091            pw.println("  mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
2092            pw.println("  mSystemReady=" + mSystemReady);
2093        }
2094    }
2095}
2096