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