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