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