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