NotificationManagerService.java revision 9b05c61a87972dce6982e5b016bbcd0e0dff74c3
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);
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)
1520    {
1521        mHandler.removeCallbacksAndMessages(r);
1522        if (r.duration != Toast.LENGTH_INFINITE) {
1523            Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1524            long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1525            mHandler.sendMessageDelayed(m, delay);
1526        }
1527    }
1528
1529    private void handleTimeout(ToastRecord record)
1530    {
1531        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1532        synchronized (mToastQueue) {
1533            int index = indexOfToastLocked(record.pkg, record.callback);
1534            if (index >= 0) {
1535                cancelToastLocked(index);
1536            }
1537        }
1538    }
1539
1540    // lock on mToastQueue
1541    private int indexOfToastLocked(String pkg, ITransientNotification callback)
1542    {
1543        IBinder cbak = callback.asBinder();
1544        ArrayList<ToastRecord> list = mToastQueue;
1545        int len = list.size();
1546        for (int i=0; i<len; i++) {
1547            ToastRecord r = list.get(i);
1548            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1549                return i;
1550            }
1551        }
1552        return -1;
1553    }
1554
1555    // lock on mToastQueue
1556    private void keepProcessAliveLocked(int pid)
1557    {
1558        int toastCount = 0; // toasts from this pid
1559        ArrayList<ToastRecord> list = mToastQueue;
1560        int N = list.size();
1561        for (int i=0; i<N; i++) {
1562            ToastRecord r = list.get(i);
1563            if (r.pid == pid) {
1564                toastCount++;
1565            }
1566        }
1567        try {
1568            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1569        } catch (RemoteException e) {
1570            // Shouldn't happen.
1571        }
1572    }
1573
1574    private final class WorkerHandler extends Handler
1575    {
1576        @Override
1577        public void handleMessage(Message msg)
1578        {
1579            switch (msg.what)
1580            {
1581                case MESSAGE_TIMEOUT:
1582                    handleTimeout((ToastRecord)msg.obj);
1583                    break;
1584            }
1585        }
1586    }
1587
1588
1589    // Notifications
1590    // ============================================================================
1591    public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id,
1592            Notification notification, int[] idOut, int userId)
1593    {
1594        enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(), Binder.getCallingPid(),
1595                tag, id, notification, idOut, userId);
1596    }
1597
1598    private final static int clamp(int x, int low, int high) {
1599        return (x < low) ? low : ((x > high) ? high : x);
1600    }
1601
1602    // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
1603    // uid/pid of another application)
1604
1605    public void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid,
1606            final int callingPid, final String tag, final int id, final Notification notification,
1607            int[] idOut, int incomingUserId)
1608    {
1609        if (DBG) {
1610            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
1611        }
1612        checkCallerIsSystemOrSameApp(pkg);
1613        final boolean isSystemNotification = isCallerSystem() || ("android".equals(pkg));
1614
1615        final int userId = ActivityManager.handleIncomingUser(callingPid,
1616                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1617        final UserHandle user = new UserHandle(userId);
1618
1619        // Limit the number of notifications that any given package except the android
1620        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1621        if (!isSystemNotification) {
1622            synchronized (mNotificationList) {
1623                int count = 0;
1624                final int N = mNotificationList.size();
1625                for (int i=0; i<N; i++) {
1626                    final NotificationRecord r = mNotificationList.get(i);
1627                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1628                        count++;
1629                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1630                            Slog.e(TAG, "Package has already posted " + count
1631                                    + " notifications.  Not showing more.  package=" + pkg);
1632                            return;
1633                        }
1634                    }
1635                }
1636            }
1637        }
1638
1639        // This conditional is a dirty hack to limit the logging done on
1640        //     behalf of the download manager without affecting other apps.
1641        if (!pkg.equals("com.android.providers.downloads")
1642                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1643            EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId,
1644                    notification.toString());
1645        }
1646
1647        if (pkg == null || notification == null) {
1648            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1649                    + " id=" + id + " notification=" + notification);
1650        }
1651        if (notification.icon != 0) {
1652            if (notification.contentView == null) {
1653                throw new IllegalArgumentException("contentView required: pkg=" + pkg
1654                        + " id=" + id + " notification=" + notification);
1655            }
1656        }
1657
1658        mHandler.post(new Runnable() {
1659            @Override
1660            public void run() {
1661
1662                // === Scoring ===
1663
1664                // 0. Sanitize inputs
1665                notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1666                        Notification.PRIORITY_MAX);
1667                // Migrate notification flags to scores
1668                if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1669                    if (notification.priority < Notification.PRIORITY_MAX) {
1670                        notification.priority = Notification.PRIORITY_MAX;
1671                    }
1672                } else if (SCORE_ONGOING_HIGHER &&
1673                        0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1674                    if (notification.priority < Notification.PRIORITY_HIGH) {
1675                        notification.priority = Notification.PRIORITY_HIGH;
1676                    }
1677                }
1678
1679                // 1. initial score: buckets of 10, around the app
1680                int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1681
1682                // 2. Consult external heuristics (TBD)
1683
1684                // 3. Apply local rules
1685
1686                // blocked apps
1687                if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1688                    if (!isSystemNotification) {
1689                        score = JUNK_SCORE;
1690                        Slog.e(TAG, "Suppressing notification from package " + pkg
1691                                + " by user request.");
1692                    }
1693                }
1694
1695                if (DBG) {
1696                    Slog.v(TAG, "Assigned score=" + score + " to " + notification);
1697                }
1698
1699                if (score < SCORE_DISPLAY_THRESHOLD) {
1700                    // Notification will be blocked because the score is too low.
1701                    return;
1702                }
1703
1704                // Should this notification make noise, vibe, or use the LED?
1705                final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
1706
1707                synchronized (mNotificationList) {
1708                    final StatusBarNotification n = new StatusBarNotification(
1709                            pkg, id, tag, callingUid, callingPid, score, notification, user);
1710                    NotificationRecord r = new NotificationRecord(n);
1711                    NotificationRecord old = null;
1712
1713                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
1714                    if (index < 0) {
1715                        mNotificationList.add(r);
1716                    } else {
1717                        old = mNotificationList.remove(index);
1718                        mNotificationList.add(index, r);
1719                        // Make sure we don't lose the foreground service state.
1720                        if (old != null) {
1721                            notification.flags |=
1722                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1723                        }
1724                    }
1725
1726                    // Ensure if this is a foreground service that the proper additional
1727                    // flags are set.
1728                    if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1729                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1730                                | Notification.FLAG_NO_CLEAR;
1731                    }
1732
1733                    final int currentUser;
1734                    final long token = Binder.clearCallingIdentity();
1735                    try {
1736                        currentUser = ActivityManager.getCurrentUser();
1737                    } finally {
1738                        Binder.restoreCallingIdentity(token);
1739                    }
1740
1741                    if (notification.icon != 0) {
1742                        if (old != null && old.statusBarKey != null) {
1743                            r.statusBarKey = old.statusBarKey;
1744                            long identity = Binder.clearCallingIdentity();
1745                            try {
1746                                mStatusBar.updateNotification(r.statusBarKey, n);
1747                            }
1748                            finally {
1749                                Binder.restoreCallingIdentity(identity);
1750                            }
1751                        } else {
1752                            long identity = Binder.clearCallingIdentity();
1753                            try {
1754                                r.statusBarKey = mStatusBar.addNotification(n);
1755                                if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
1756                                        && canInterrupt) {
1757                                    mAttentionLight.pulse();
1758                                }
1759                            }
1760                            finally {
1761                                Binder.restoreCallingIdentity(identity);
1762                            }
1763                        }
1764                        // Send accessibility events only for the current user.
1765                        if (currentUser == userId) {
1766                            sendAccessibilityEvent(notification, pkg);
1767                        }
1768
1769                        notifyPostedLocked(r);
1770                    } else {
1771                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1772                        if (old != null && old.statusBarKey != null) {
1773                            long identity = Binder.clearCallingIdentity();
1774                            try {
1775                                mStatusBar.removeNotification(old.statusBarKey);
1776                            }
1777                            finally {
1778                                Binder.restoreCallingIdentity(identity);
1779                            }
1780
1781                            notifyRemovedLocked(r);
1782                        }
1783                        // ATTENTION: in a future release we will bail out here
1784                        // so that we do not play sounds, show lights, etc. for invalid notifications
1785                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1786                                + n.getPackageName());
1787                    }
1788
1789                    // If we're not supposed to beep, vibrate, etc. then don't.
1790                    if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
1791                            && (!(old != null
1792                                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1793                            && (r.getUserId() == UserHandle.USER_ALL ||
1794                                (r.getUserId() == userId && r.getUserId() == currentUser))
1795                            && canInterrupt
1796                            && mSystemReady) {
1797
1798                        final AudioManager audioManager = (AudioManager) mContext
1799                        .getSystemService(Context.AUDIO_SERVICE);
1800
1801                        // sound
1802
1803                        // should we use the default notification sound? (indicated either by
1804                        // DEFAULT_SOUND or because notification.sound is pointing at
1805                        // Settings.System.NOTIFICATION_SOUND)
1806                        final boolean useDefaultSound =
1807                               (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1808                                       Settings.System.DEFAULT_NOTIFICATION_URI
1809                                               .equals(notification.sound);
1810
1811                        Uri soundUri = null;
1812                        boolean hasValidSound = false;
1813
1814                        if (useDefaultSound) {
1815                            soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1816
1817                            // check to see if the default notification sound is silent
1818                            ContentResolver resolver = mContext.getContentResolver();
1819                            hasValidSound = Settings.System.getString(resolver,
1820                                   Settings.System.NOTIFICATION_SOUND) != null;
1821                        } else if (notification.sound != null) {
1822                            soundUri = notification.sound;
1823                            hasValidSound = (soundUri != null);
1824                        }
1825
1826                        if (hasValidSound) {
1827                            boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
1828                            int audioStreamType;
1829                            if (notification.audioStreamType >= 0) {
1830                                audioStreamType = notification.audioStreamType;
1831                            } else {
1832                                audioStreamType = DEFAULT_STREAM_TYPE;
1833                            }
1834                            mSoundNotification = r;
1835                            // do not play notifications if stream volume is 0 (typically because
1836                            // ringer mode is silent) or if speech recognition is active.
1837                            if ((audioManager.getStreamVolume(audioStreamType) != 0)
1838                                    && !audioManager.isSpeechRecognitionActive()) {
1839                                final long identity = Binder.clearCallingIdentity();
1840                                try {
1841                                    final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1842                                    if (player != null) {
1843                                        player.playAsync(soundUri, user, looping, audioStreamType);
1844                                    }
1845                                } catch (RemoteException e) {
1846                                } finally {
1847                                    Binder.restoreCallingIdentity(identity);
1848                                }
1849                            }
1850                        }
1851
1852                        // vibrate
1853                        // Does the notification want to specify its own vibration?
1854                        final boolean hasCustomVibrate = notification.vibrate != null;
1855
1856                        // new in 4.2: if there was supposed to be a sound and we're in vibrate
1857                        // mode, and no other vibration is specified, we fall back to vibration
1858                        final boolean convertSoundToVibration =
1859                                   !hasCustomVibrate
1860                                && hasValidSound
1861                                && (audioManager.getRingerMode()
1862                                           == AudioManager.RINGER_MODE_VIBRATE);
1863
1864                        // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1865                        final boolean useDefaultVibrate =
1866                                (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1867
1868                        if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1869                                && !(audioManager.getRingerMode()
1870                                        == AudioManager.RINGER_MODE_SILENT)) {
1871                            mVibrateNotification = r;
1872
1873                            if (useDefaultVibrate || convertSoundToVibration) {
1874                                // Escalate privileges so we can use the vibrator even if the
1875                                // notifying app does not have the VIBRATE permission.
1876                                long identity = Binder.clearCallingIdentity();
1877                                try {
1878                                    mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
1879                                        useDefaultVibrate ? mDefaultVibrationPattern
1880                                            : mFallbackVibrationPattern,
1881                                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1882                                                ? 0: -1);
1883                                } finally {
1884                                    Binder.restoreCallingIdentity(identity);
1885                                }
1886                            } else if (notification.vibrate.length > 1) {
1887                                // If you want your own vibration pattern, you need the VIBRATE
1888                                // permission
1889                                mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
1890                                        notification.vibrate,
1891                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1892                                            ? 0: -1);
1893                            }
1894                        }
1895                    }
1896
1897                    // light
1898                    // the most recent thing gets the light
1899                    mLights.remove(old);
1900                    if (mLedNotification == old) {
1901                        mLedNotification = null;
1902                    }
1903                    //Slog.i(TAG, "notification.lights="
1904                    //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
1905                    //                  != 0));
1906                    if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1907                            && canInterrupt) {
1908                        mLights.add(r);
1909                        updateLightsLocked();
1910                    } else {
1911                        if (old != null
1912                                && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
1913                            updateLightsLocked();
1914                        }
1915                    }
1916                }
1917            }
1918        });
1919
1920        idOut[0] = id;
1921    }
1922
1923    private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
1924        AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
1925        if (!manager.isEnabled()) {
1926            return;
1927        }
1928
1929        AccessibilityEvent event =
1930            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1931        event.setPackageName(packageName);
1932        event.setClassName(Notification.class.getName());
1933        event.setParcelableData(notification);
1934        CharSequence tickerText = notification.tickerText;
1935        if (!TextUtils.isEmpty(tickerText)) {
1936            event.getText().add(tickerText);
1937        }
1938
1939        manager.sendAccessibilityEvent(event);
1940    }
1941
1942    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
1943        // tell the app
1944        if (sendDelete) {
1945            if (r.getNotification().deleteIntent != null) {
1946                try {
1947                    r.getNotification().deleteIntent.send();
1948                } catch (PendingIntent.CanceledException ex) {
1949                    // do nothing - there's no relevant way to recover, and
1950                    //     no reason to let this propagate
1951                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
1952                }
1953            }
1954        }
1955
1956        // status bar
1957        if (r.getNotification().icon != 0) {
1958            long identity = Binder.clearCallingIdentity();
1959            try {
1960                mStatusBar.removeNotification(r.statusBarKey);
1961            }
1962            finally {
1963                Binder.restoreCallingIdentity(identity);
1964            }
1965            r.statusBarKey = null;
1966            notifyRemovedLocked(r);
1967        }
1968
1969        // sound
1970        if (mSoundNotification == r) {
1971            mSoundNotification = null;
1972            final long identity = Binder.clearCallingIdentity();
1973            try {
1974                final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1975                if (player != null) {
1976                    player.stopAsync();
1977                }
1978            } catch (RemoteException e) {
1979            } finally {
1980                Binder.restoreCallingIdentity(identity);
1981            }
1982        }
1983
1984        // vibrate
1985        if (mVibrateNotification == r) {
1986            mVibrateNotification = null;
1987            long identity = Binder.clearCallingIdentity();
1988            try {
1989                mVibrator.cancel();
1990            }
1991            finally {
1992                Binder.restoreCallingIdentity(identity);
1993            }
1994        }
1995
1996        // light
1997        mLights.remove(r);
1998        if (mLedNotification == r) {
1999            mLedNotification = null;
2000        }
2001
2002        // Save it for users of getHistoricalNotifications()
2003        mArchive.record(r.sbn);
2004    }
2005
2006    /**
2007     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2008     * and none of the {@code mustNotHaveFlags}.
2009     */
2010    private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
2011            int mustNotHaveFlags, boolean sendDelete, int userId) {
2012        EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId,
2013                mustHaveFlags, mustNotHaveFlags);
2014
2015        synchronized (mNotificationList) {
2016            int index = indexOfNotificationLocked(pkg, tag, id, userId);
2017            if (index >= 0) {
2018                NotificationRecord r = mNotificationList.get(index);
2019
2020                if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2021                    return;
2022                }
2023                if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2024                    return;
2025                }
2026
2027                mNotificationList.remove(index);
2028
2029                cancelNotificationLocked(r, sendDelete);
2030                updateLightsLocked();
2031            }
2032        }
2033    }
2034
2035    /**
2036     * Determine whether the userId applies to the notification in question, either because
2037     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2038     */
2039    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2040        return
2041                // looking for USER_ALL notifications? match everything
2042                   userId == UserHandle.USER_ALL
2043                // a notification sent to USER_ALL matches any query
2044                || r.getUserId() == UserHandle.USER_ALL
2045                // an exact user match
2046                || r.getUserId() == userId;
2047    }
2048
2049    /**
2050     * Cancels all notifications from a given package that have all of the
2051     * {@code mustHaveFlags}.
2052     */
2053    boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
2054            int mustNotHaveFlags, boolean doit, int userId) {
2055        EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, userId,
2056                mustHaveFlags, mustNotHaveFlags);
2057
2058        synchronized (mNotificationList) {
2059            final int N = mNotificationList.size();
2060            boolean canceledSomething = false;
2061            for (int i = N-1; i >= 0; --i) {
2062                NotificationRecord r = mNotificationList.get(i);
2063                if (!notificationMatchesUserId(r, userId)) {
2064                    continue;
2065                }
2066                // Don't remove notifications to all, if there's no package name specified
2067                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2068                    continue;
2069                }
2070                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2071                    continue;
2072                }
2073                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2074                    continue;
2075                }
2076                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2077                    continue;
2078                }
2079                canceledSomething = true;
2080                if (!doit) {
2081                    return true;
2082                }
2083                mNotificationList.remove(i);
2084                cancelNotificationLocked(r, false);
2085            }
2086            if (canceledSomething) {
2087                updateLightsLocked();
2088            }
2089            return canceledSomething;
2090        }
2091    }
2092
2093    public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
2094        checkCallerIsSystemOrSameApp(pkg);
2095        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2096                Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
2097        // Don't allow client applications to cancel foreground service notis.
2098        cancelNotification(pkg, tag, id, 0,
2099                Binder.getCallingUid() == Process.SYSTEM_UID
2100                ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
2101    }
2102
2103    public void cancelAllNotifications(String pkg, int userId) {
2104        checkCallerIsSystemOrSameApp(pkg);
2105
2106        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2107                Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
2108
2109        // Calling from user space, don't allow the canceling of actively
2110        // running foreground services.
2111        cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
2112    }
2113
2114    // Return true if the caller is a system or phone UID and therefore should not have
2115    // any notifications or toasts blocked.
2116    boolean isCallerSystem() {
2117        final int uid = Binder.getCallingUid();
2118        final int appid = UserHandle.getAppId(uid);
2119        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2120    }
2121
2122    void checkCallerIsSystem() {
2123        if (isCallerSystem()) {
2124            return;
2125        }
2126        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2127    }
2128
2129    void checkCallerIsSystemOrSameApp(String pkg) {
2130        if (isCallerSystem()) {
2131            return;
2132        }
2133        final int uid = Binder.getCallingUid();
2134        try {
2135            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2136                    pkg, 0, UserHandle.getCallingUserId());
2137            if (!UserHandle.isSameApp(ai.uid, uid)) {
2138                throw new SecurityException("Calling uid " + uid + " gave package"
2139                        + pkg + " which is owned by uid " + ai.uid);
2140            }
2141        } catch (RemoteException re) {
2142            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2143        }
2144    }
2145
2146    void cancelAll(int userId) {
2147        synchronized (mNotificationList) {
2148            final int N = mNotificationList.size();
2149            for (int i=N-1; i>=0; i--) {
2150                NotificationRecord r = mNotificationList.get(i);
2151
2152                if (!notificationMatchesUserId(r, userId)) {
2153                    continue;
2154                }
2155
2156                if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2157                                | Notification.FLAG_NO_CLEAR)) == 0) {
2158                    mNotificationList.remove(i);
2159                    cancelNotificationLocked(r, true);
2160                }
2161            }
2162
2163            updateLightsLocked();
2164        }
2165    }
2166
2167    // lock on mNotificationList
2168    private void updateLightsLocked()
2169    {
2170        // handle notification lights
2171        if (mLedNotification == null) {
2172            // get next notification, if any
2173            int n = mLights.size();
2174            if (n > 0) {
2175                mLedNotification = mLights.get(n-1);
2176            }
2177        }
2178
2179        // Don't flash while we are in a call or screen is on
2180        if (mLedNotification == null || mInCall || mScreenOn) {
2181            mNotificationLight.turnOff();
2182        } else {
2183            final Notification ledno = mLedNotification.sbn.getNotification();
2184            int ledARGB = ledno.ledARGB;
2185            int ledOnMS = ledno.ledOnMS;
2186            int ledOffMS = ledno.ledOffMS;
2187            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2188                ledARGB = mDefaultNotificationColor;
2189                ledOnMS = mDefaultNotificationLedOn;
2190                ledOffMS = mDefaultNotificationLedOff;
2191            }
2192            if (mNotificationPulseEnabled) {
2193                // pulse repeatedly
2194                mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
2195                        ledOnMS, ledOffMS);
2196            }
2197        }
2198    }
2199
2200    // lock on mNotificationList
2201    private int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2202    {
2203        ArrayList<NotificationRecord> list = mNotificationList;
2204        final int len = list.size();
2205        for (int i=0; i<len; i++) {
2206            NotificationRecord r = list.get(i);
2207            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2208                continue;
2209            }
2210            if (tag == null) {
2211                if (r.sbn.getTag() != null) {
2212                    continue;
2213                }
2214            } else {
2215                if (!tag.equals(r.sbn.getTag())) {
2216                    continue;
2217                }
2218            }
2219            if (r.sbn.getPackageName().equals(pkg)) {
2220                return i;
2221            }
2222        }
2223        return -1;
2224    }
2225
2226    private void updateNotificationPulse() {
2227        synchronized (mNotificationList) {
2228            updateLightsLocked();
2229        }
2230    }
2231
2232    // ======================================================================
2233    @Override
2234    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2235        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2236                != PackageManager.PERMISSION_GRANTED) {
2237            pw.println("Permission Denial: can't dump NotificationManager from from pid="
2238                    + Binder.getCallingPid()
2239                    + ", uid=" + Binder.getCallingUid());
2240            return;
2241        }
2242
2243        pw.println("Current Notification Manager state:");
2244
2245        pw.println("  Listeners (" + mEnabledListenersForCurrentUser.size()
2246                + ") enabled for current user:");
2247        for (ComponentName cmpt : mEnabledListenersForCurrentUser) {
2248            pw.println("    " + cmpt);
2249        }
2250
2251        pw.println("  Live listeners (" + mListeners.size() + "):");
2252        for (NotificationListenerInfo info : mListeners) {
2253            pw.println("    " + info.component
2254                    + " (user " + info.userid + "): " + info.listener
2255                    + (info.isSystem?" SYSTEM":""));
2256        }
2257
2258        int N;
2259
2260        synchronized (mToastQueue) {
2261            N = mToastQueue.size();
2262            if (N > 0) {
2263                pw.println("  Toast Queue:");
2264                for (int i=0; i<N; i++) {
2265                    mToastQueue.get(i).dump(pw, "    ");
2266                }
2267                pw.println("  ");
2268            }
2269
2270        }
2271
2272        synchronized (mNotificationList) {
2273            N = mNotificationList.size();
2274            if (N > 0) {
2275                pw.println("  Notification List:");
2276                for (int i=0; i<N; i++) {
2277                    mNotificationList.get(i).dump(pw, "    ", mContext);
2278                }
2279                pw.println("  ");
2280            }
2281
2282            N = mLights.size();
2283            if (N > 0) {
2284                pw.println("  Lights List:");
2285                for (int i=0; i<N; i++) {
2286                    pw.println("    " + mLights.get(i));
2287                }
2288                pw.println("  ");
2289            }
2290
2291            pw.println("  mSoundNotification=" + mSoundNotification);
2292            pw.println("  mVibrateNotification=" + mVibrateNotification);
2293            pw.println("  mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
2294            pw.println("  mSystemReady=" + mSystemReady);
2295            pw.println("  mArchive=" + mArchive.toString());
2296            Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
2297            int i=0;
2298            while (iter.hasNext()) {
2299                pw.println("    " + iter.next());
2300                if (++i >= 5) {
2301                    if (iter.hasNext()) pw.println("    ...");
2302                    break;
2303                }
2304            }
2305
2306        }
2307    }
2308}
2309