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.IActivityManager;
     27 import android.app.INotificationManager;
     28 import android.app.ITransientNotification;
     29 import android.app.Notification;
     30 import android.app.PendingIntent;
     31 import android.app.StatusBarManager;
     32 import android.content.BroadcastReceiver;
     33 import android.content.ContentResolver;
     34 import android.content.Context;
     35 import android.content.Intent;
     36 import android.content.IntentFilter;
     37 import android.content.pm.ApplicationInfo;
     38 import android.content.pm.PackageManager;
     39 import android.content.pm.PackageManager.NameNotFoundException;
     40 import android.content.res.Resources;
     41 import android.database.ContentObserver;
     42 import android.media.AudioManager;
     43 import android.media.IAudioService;
     44 import android.media.IRingtonePlayer;
     45 import android.net.Uri;
     46 import android.os.Binder;
     47 import android.os.Handler;
     48 import android.os.IBinder;
     49 import android.os.Message;
     50 import android.os.Process;
     51 import android.os.RemoteException;
     52 import android.os.ServiceManager;
     53 import android.os.UserHandle;
     54 import android.os.Vibrator;
     55 import android.provider.Settings;
     56 import android.telephony.TelephonyManager;
     57 import android.text.TextUtils;
     58 import android.util.AtomicFile;
     59 import android.util.EventLog;
     60 import android.util.Log;
     61 import android.util.Slog;
     62 import android.util.Xml;
     63 import android.view.accessibility.AccessibilityEvent;
     64 import android.view.accessibility.AccessibilityManager;
     65 import android.widget.RemoteViews;
     66 import android.widget.Toast;
     67 
     68 import com.android.internal.statusbar.StatusBarNotification;
     69 import com.android.internal.util.FastXmlSerializer;
     70 
     71 import org.xmlpull.v1.XmlPullParser;
     72 import org.xmlpull.v1.XmlPullParserException;
     73 import org.xmlpull.v1.XmlSerializer;
     74 
     75 import java.io.File;
     76 import java.io.FileDescriptor;
     77 import java.io.FileInputStream;
     78 import java.io.FileNotFoundException;
     79 import java.io.FileOutputStream;
     80 import java.io.IOException;
     81 import java.io.PrintWriter;
     82 import java.util.ArrayList;
     83 import java.util.Arrays;
     84 import java.util.HashSet;
     85 
     86 import libcore.io.IoUtils;
     87 
     88 
     89 /** {@hide} */
     90 public class NotificationManagerService extends INotificationManager.Stub
     91 {
     92     private static final String TAG = "NotificationService";
     93     private static final boolean DBG = false;
     94 
     95     private static final int MAX_PACKAGE_NOTIFICATIONS = 50;
     96 
     97     // message codes
     98     private static final int MESSAGE_TIMEOUT = 2;
     99 
    100     private static final int LONG_DELAY = 3500; // 3.5 seconds
    101     private static final int SHORT_DELAY = 2000; // 2 seconds
    102 
    103     private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
    104 
    105     private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
    106     private static final boolean SCORE_ONGOING_HIGHER = false;
    107 
    108     private static final int JUNK_SCORE = -1000;
    109     private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
    110     private static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
    111 
    112     private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
    113     private static final boolean ENABLE_BLOCKED_TOASTS = true;
    114 
    115     final Context mContext;
    116     final IActivityManager mAm;
    117     final IBinder mForegroundToken = new Binder();
    118 
    119     private WorkerHandler mHandler;
    120     private StatusBarManagerService mStatusBar;
    121     private LightsService.Light mNotificationLight;
    122     private LightsService.Light mAttentionLight;
    123 
    124     private int mDefaultNotificationColor;
    125     private int mDefaultNotificationLedOn;
    126     private int mDefaultNotificationLedOff;
    127 
    128     private boolean mSystemReady;
    129     private int mDisabledNotifications;
    130 
    131     private NotificationRecord mSoundNotification;
    132     private NotificationRecord mVibrateNotification;
    133 
    134     private IAudioService mAudioService;
    135     private Vibrator mVibrator;
    136 
    137     // for enabling and disabling notification pulse behavior
    138     private boolean mScreenOn = true;
    139     private boolean mInCall = false;
    140     private boolean mNotificationPulseEnabled;
    141 
    142     private final ArrayList<NotificationRecord> mNotificationList =
    143             new ArrayList<NotificationRecord>();
    144 
    145     private ArrayList<ToastRecord> mToastQueue;
    146 
    147     private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
    148     private NotificationRecord mLedNotification;
    149 
    150     // Notification control database. For now just contains disabled packages.
    151     private AtomicFile mPolicyFile;
    152     private HashSet<String> mBlockedPackages = new HashSet<String>();
    153 
    154     private static final int DB_VERSION = 1;
    155 
    156     private static final String TAG_BODY = "notification-policy";
    157     private static final String ATTR_VERSION = "version";
    158 
    159     private static final String TAG_BLOCKED_PKGS = "blocked-packages";
    160     private static final String TAG_PACKAGE = "package";
    161     private static final String ATTR_NAME = "name";
    162 
    163     private void loadBlockDb() {
    164         synchronized(mBlockedPackages) {
    165             if (mPolicyFile == null) {
    166                 File dir = new File("/data/system");
    167                 mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml"));
    168 
    169                 mBlockedPackages.clear();
    170 
    171                 FileInputStream infile = null;
    172                 try {
    173                     infile = mPolicyFile.openRead();
    174                     final XmlPullParser parser = Xml.newPullParser();
    175                     parser.setInput(infile, null);
    176 
    177                     int type;
    178                     String tag;
    179                     int version = DB_VERSION;
    180                     while ((type = parser.next()) != END_DOCUMENT) {
    181                         tag = parser.getName();
    182                         if (type == START_TAG) {
    183                             if (TAG_BODY.equals(tag)) {
    184                                 version = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION));
    185                             } else if (TAG_BLOCKED_PKGS.equals(tag)) {
    186                                 while ((type = parser.next()) != END_DOCUMENT) {
    187                                     tag = parser.getName();
    188                                     if (TAG_PACKAGE.equals(tag)) {
    189                                         mBlockedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
    190                                     } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
    191                                         break;
    192                                     }
    193                                 }
    194                             }
    195                         }
    196                     }
    197                 } catch (FileNotFoundException e) {
    198                     // No data yet
    199                 } catch (IOException e) {
    200                     Log.wtf(TAG, "Unable to read blocked notifications database", e);
    201                 } catch (NumberFormatException e) {
    202                     Log.wtf(TAG, "Unable to parse blocked notifications database", e);
    203                 } catch (XmlPullParserException e) {
    204                     Log.wtf(TAG, "Unable to parse blocked notifications database", e);
    205                 } finally {
    206                     IoUtils.closeQuietly(infile);
    207                 }
    208             }
    209         }
    210     }
    211 
    212     private void writeBlockDb() {
    213         synchronized(mBlockedPackages) {
    214             FileOutputStream outfile = null;
    215             try {
    216                 outfile = mPolicyFile.startWrite();
    217 
    218                 XmlSerializer out = new FastXmlSerializer();
    219                 out.setOutput(outfile, "utf-8");
    220 
    221                 out.startDocument(null, true);
    222 
    223                 out.startTag(null, TAG_BODY); {
    224                     out.attribute(null, ATTR_VERSION, String.valueOf(DB_VERSION));
    225                     out.startTag(null, TAG_BLOCKED_PKGS); {
    226                         // write all known network policies
    227                         for (String pkg : mBlockedPackages) {
    228                             out.startTag(null, TAG_PACKAGE); {
    229                                 out.attribute(null, ATTR_NAME, pkg);
    230                             } out.endTag(null, TAG_PACKAGE);
    231                         }
    232                     } out.endTag(null, TAG_BLOCKED_PKGS);
    233                 } out.endTag(null, TAG_BODY);
    234 
    235                 out.endDocument();
    236 
    237                 mPolicyFile.finishWrite(outfile);
    238             } catch (IOException e) {
    239                 if (outfile != null) {
    240                     mPolicyFile.failWrite(outfile);
    241                 }
    242             }
    243         }
    244     }
    245 
    246     public boolean areNotificationsEnabledForPackage(String pkg) {
    247         checkCallerIsSystem();
    248         return areNotificationsEnabledForPackageInt(pkg);
    249     }
    250 
    251     // Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
    252     private boolean areNotificationsEnabledForPackageInt(String pkg) {
    253         final boolean enabled = !mBlockedPackages.contains(pkg);
    254         if (DBG) {
    255             Slog.v(TAG, "notifications are " + (enabled?"en":"dis") + "abled for " + pkg);
    256         }
    257         return enabled;
    258     }
    259 
    260     public void setNotificationsEnabledForPackage(String pkg, boolean enabled) {
    261         checkCallerIsSystem();
    262         if (DBG) {
    263             Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
    264         }
    265         if (enabled) {
    266             mBlockedPackages.remove(pkg);
    267         } else {
    268             mBlockedPackages.add(pkg);
    269 
    270             // Now, cancel any outstanding notifications that are part of a just-disabled app
    271             if (ENABLE_BLOCKED_NOTIFICATIONS) {
    272                 synchronized (mNotificationList) {
    273                     final int N = mNotificationList.size();
    274                     for (int i=0; i<N; i++) {
    275                         final NotificationRecord r = mNotificationList.get(i);
    276                         if (r.pkg.equals(pkg)) {
    277                             cancelNotificationLocked(r, false);
    278                         }
    279                     }
    280                 }
    281             }
    282             // Don't bother canceling toasts, they'll go away soon enough.
    283         }
    284         writeBlockDb();
    285     }
    286 
    287 
    288     private static String idDebugString(Context baseContext, String packageName, int id) {
    289         Context c = null;
    290 
    291         if (packageName != null) {
    292             try {
    293                 c = baseContext.createPackageContext(packageName, 0);
    294             } catch (NameNotFoundException e) {
    295                 c = baseContext;
    296             }
    297         } else {
    298             c = baseContext;
    299         }
    300 
    301         String pkg;
    302         String type;
    303         String name;
    304 
    305         Resources r = c.getResources();
    306         try {
    307             return r.getResourceName(id);
    308         } catch (Resources.NotFoundException e) {
    309             return "<name unknown>";
    310         }
    311     }
    312 
    313     private static final class NotificationRecord
    314     {
    315         final String pkg;
    316         final String tag;
    317         final int id;
    318         final int uid;
    319         final int initialPid;
    320         final int userId;
    321         final Notification notification;
    322         final int score;
    323         IBinder statusBarKey;
    324 
    325         NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,
    326                 int userId, int score, Notification notification)
    327         {
    328             this.pkg = pkg;
    329             this.tag = tag;
    330             this.id = id;
    331             this.uid = uid;
    332             this.initialPid = initialPid;
    333             this.userId = userId;
    334             this.score = score;
    335             this.notification = notification;
    336         }
    337 
    338         void dump(PrintWriter pw, String prefix, Context baseContext) {
    339             pw.println(prefix + this);
    340             pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon)
    341                     + " / " + idDebugString(baseContext, this.pkg, notification.icon));
    342             pw.println(prefix + "  pri=" + notification.priority);
    343             pw.println(prefix + "  score=" + this.score);
    344             pw.println(prefix + "  contentIntent=" + notification.contentIntent);
    345             pw.println(prefix + "  deleteIntent=" + notification.deleteIntent);
    346             pw.println(prefix + "  tickerText=" + notification.tickerText);
    347             pw.println(prefix + "  contentView=" + notification.contentView);
    348             pw.println(prefix + "  uid=" + uid + " userId=" + userId);
    349             pw.println(prefix + "  defaults=0x" + Integer.toHexString(notification.defaults));
    350             pw.println(prefix + "  flags=0x" + Integer.toHexString(notification.flags));
    351             pw.println(prefix + "  sound=" + notification.sound);
    352             pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate));
    353             pw.println(prefix + "  ledARGB=0x" + Integer.toHexString(notification.ledARGB)
    354                     + " ledOnMS=" + notification.ledOnMS
    355                     + " ledOffMS=" + notification.ledOffMS);
    356         }
    357 
    358         @Override
    359         public final String toString()
    360         {
    361             return "NotificationRecord{"
    362                 + Integer.toHexString(System.identityHashCode(this))
    363                 + " pkg=" + pkg
    364                 + " id=" + Integer.toHexString(id)
    365                 + " tag=" + tag
    366                 + " score=" + score
    367                 + "}";
    368         }
    369     }
    370 
    371     private static final class ToastRecord
    372     {
    373         final int pid;
    374         final String pkg;
    375         final ITransientNotification callback;
    376         int duration;
    377 
    378         ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
    379         {
    380             this.pid = pid;
    381             this.pkg = pkg;
    382             this.callback = callback;
    383             this.duration = duration;
    384         }
    385 
    386         void update(int duration) {
    387             this.duration = duration;
    388         }
    389 
    390         void dump(PrintWriter pw, String prefix) {
    391             pw.println(prefix + this);
    392         }
    393 
    394         @Override
    395         public final String toString()
    396         {
    397             return "ToastRecord{"
    398                 + Integer.toHexString(System.identityHashCode(this))
    399                 + " pkg=" + pkg
    400                 + " callback=" + callback
    401                 + " duration=" + duration;
    402         }
    403     }
    404 
    405     private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
    406             = new StatusBarManagerService.NotificationCallbacks() {
    407 
    408         public void onSetDisabled(int status) {
    409             synchronized (mNotificationList) {
    410                 mDisabledNotifications = status;
    411                 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
    412                     // cancel whatever's going on
    413                     long identity = Binder.clearCallingIdentity();
    414                     try {
    415                         final IRingtonePlayer player = mAudioService.getRingtonePlayer();
    416                         if (player != null) {
    417                             player.stopAsync();
    418                         }
    419                     } catch (RemoteException e) {
    420                     } finally {
    421                         Binder.restoreCallingIdentity(identity);
    422                     }
    423 
    424                     identity = Binder.clearCallingIdentity();
    425                     try {
    426                         mVibrator.cancel();
    427                     } finally {
    428                         Binder.restoreCallingIdentity(identity);
    429                     }
    430                 }
    431             }
    432         }
    433 
    434         public void onClearAll() {
    435             // XXX to be totally correct, the caller should tell us which user
    436             // this is for.
    437             cancelAll(ActivityManager.getCurrentUser());
    438         }
    439 
    440         public void onNotificationClick(String pkg, String tag, int id) {
    441             // XXX to be totally correct, the caller should tell us which user
    442             // this is for.
    443             cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
    444                     Notification.FLAG_FOREGROUND_SERVICE, false,
    445                     ActivityManager.getCurrentUser());
    446         }
    447 
    448         public void onNotificationClear(String pkg, String tag, int id) {
    449             // XXX to be totally correct, the caller should tell us which user
    450             // this is for.
    451             cancelNotification(pkg, tag, id, 0,
    452                 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
    453                 true, ActivityManager.getCurrentUser());
    454         }
    455 
    456         public void onPanelRevealed() {
    457             synchronized (mNotificationList) {
    458                 // sound
    459                 mSoundNotification = null;
    460 
    461                 long identity = Binder.clearCallingIdentity();
    462                 try {
    463                     final IRingtonePlayer player = mAudioService.getRingtonePlayer();
    464                     if (player != null) {
    465                         player.stopAsync();
    466                     }
    467                 } catch (RemoteException e) {
    468                 } finally {
    469                     Binder.restoreCallingIdentity(identity);
    470                 }
    471 
    472                 // vibrate
    473                 mVibrateNotification = null;
    474                 identity = Binder.clearCallingIdentity();
    475                 try {
    476                     mVibrator.cancel();
    477                 } finally {
    478                     Binder.restoreCallingIdentity(identity);
    479                 }
    480 
    481                 // light
    482                 mLights.clear();
    483                 mLedNotification = null;
    484                 updateLightsLocked();
    485             }
    486         }
    487 
    488         public void onNotificationError(String pkg, String tag, int id,
    489                 int uid, int initialPid, String message) {
    490             Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
    491                     + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
    492             // XXX to be totally correct, the caller should tell us which user
    493             // this is for.
    494             cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid));
    495             long ident = Binder.clearCallingIdentity();
    496             try {
    497                 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
    498                         "Bad notification posted from package " + pkg
    499                         + ": " + message);
    500             } catch (RemoteException e) {
    501             }
    502             Binder.restoreCallingIdentity(ident);
    503         }
    504     };
    505 
    506     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
    507         @Override
    508         public void onReceive(Context context, Intent intent) {
    509             String action = intent.getAction();
    510 
    511             boolean queryRestart = false;
    512             boolean packageChanged = false;
    513 
    514             if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
    515                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
    516                     || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
    517                     || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
    518                     || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
    519                 String pkgList[] = null;
    520                 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
    521                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
    522                 } else if (queryRestart) {
    523                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
    524                 } else {
    525                     Uri uri = intent.getData();
    526                     if (uri == null) {
    527                         return;
    528                     }
    529                     String pkgName = uri.getSchemeSpecificPart();
    530                     if (pkgName == null) {
    531                         return;
    532                     }
    533                     if (packageChanged) {
    534                         // We cancel notifications for packages which have just been disabled
    535                         final int enabled = mContext.getPackageManager()
    536                                 .getApplicationEnabledSetting(pkgName);
    537                         if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
    538                                 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
    539                             return;
    540                         }
    541                     }
    542                     pkgList = new String[]{pkgName};
    543                 }
    544                 if (pkgList != null && (pkgList.length > 0)) {
    545                     for (String pkgName : pkgList) {
    546                         cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
    547                                 UserHandle.USER_ALL);
    548                     }
    549                 }
    550             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
    551                 // Keep track of screen on/off state, but do not turn off the notification light
    552                 // until user passes through the lock screen or views the notification.
    553                 mScreenOn = true;
    554             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
    555                 mScreenOn = false;
    556             } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
    557                 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
    558                         TelephonyManager.EXTRA_STATE_OFFHOOK));
    559                 updateNotificationPulse();
    560             } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
    561                 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
    562                 if (userHandle >= 0) {
    563                     cancelAllNotificationsInt(null, 0, 0, true, userHandle);
    564                 }
    565             } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
    566                 // turn off LED when user passes through lock screen
    567                 mNotificationLight.turnOff();
    568             }
    569         }
    570     };
    571 
    572     class SettingsObserver extends ContentObserver {
    573         SettingsObserver(Handler handler) {
    574             super(handler);
    575         }
    576 
    577         void observe() {
    578             ContentResolver resolver = mContext.getContentResolver();
    579             resolver.registerContentObserver(Settings.System.getUriFor(
    580                     Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
    581             update();
    582         }
    583 
    584         @Override public void onChange(boolean selfChange) {
    585             update();
    586         }
    587 
    588         public void update() {
    589             ContentResolver resolver = mContext.getContentResolver();
    590             boolean pulseEnabled = Settings.System.getInt(resolver,
    591                         Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
    592             if (mNotificationPulseEnabled != pulseEnabled) {
    593                 mNotificationPulseEnabled = pulseEnabled;
    594                 updateNotificationPulse();
    595             }
    596         }
    597     }
    598 
    599     NotificationManagerService(Context context, StatusBarManagerService statusBar,
    600             LightsService lights)
    601     {
    602         super();
    603         mContext = context;
    604         mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
    605         mAm = ActivityManagerNative.getDefault();
    606         mToastQueue = new ArrayList<ToastRecord>();
    607         mHandler = new WorkerHandler();
    608 
    609         loadBlockDb();
    610 
    611         mStatusBar = statusBar;
    612         statusBar.setNotificationCallbacks(mNotificationCallbacks);
    613 
    614         mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
    615         mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
    616 
    617         Resources resources = mContext.getResources();
    618         mDefaultNotificationColor = resources.getColor(
    619                 com.android.internal.R.color.config_defaultNotificationColor);
    620         mDefaultNotificationLedOn = resources.getInteger(
    621                 com.android.internal.R.integer.config_defaultNotificationLedOn);
    622         mDefaultNotificationLedOff = resources.getInteger(
    623                 com.android.internal.R.integer.config_defaultNotificationLedOff);
    624 
    625         // Don't start allowing notifications until the setup wizard has run once.
    626         // After that, including subsequent boots, init with notifications turned on.
    627         // This works on the first boot because the setup wizard will toggle this
    628         // flag at least once and we'll go back to 0 after that.
    629         if (0 == Settings.Global.getInt(mContext.getContentResolver(),
    630                     Settings.Global.DEVICE_PROVISIONED, 0)) {
    631             mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
    632         }
    633 
    634         // register for various Intents
    635         IntentFilter filter = new IntentFilter();
    636         filter.addAction(Intent.ACTION_SCREEN_ON);
    637         filter.addAction(Intent.ACTION_SCREEN_OFF);
    638         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
    639         filter.addAction(Intent.ACTION_USER_PRESENT);
    640         filter.addAction(Intent.ACTION_USER_STOPPED);
    641         mContext.registerReceiver(mIntentReceiver, filter);
    642         IntentFilter pkgFilter = new IntentFilter();
    643         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    644         pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    645         pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
    646         pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
    647         pkgFilter.addDataScheme("package");
    648         mContext.registerReceiver(mIntentReceiver, pkgFilter);
    649         IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
    650         mContext.registerReceiver(mIntentReceiver, sdFilter);
    651 
    652         SettingsObserver observer = new SettingsObserver(mHandler);
    653         observer.observe();
    654     }
    655 
    656     void systemReady() {
    657         mAudioService = IAudioService.Stub.asInterface(
    658                 ServiceManager.getService(Context.AUDIO_SERVICE));
    659 
    660         // no beeping until we're basically done booting
    661         mSystemReady = true;
    662     }
    663 
    664     // Toasts
    665     // ============================================================================
    666     public void enqueueToast(String pkg, ITransientNotification callback, int duration)
    667     {
    668         if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);
    669 
    670         if (pkg == null || callback == null) {
    671             Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
    672             return ;
    673         }
    674 
    675         final boolean isSystemToast = ("android".equals(pkg));
    676 
    677         if (ENABLE_BLOCKED_TOASTS && !isSystemToast && !areNotificationsEnabledForPackageInt(pkg)) {
    678             Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
    679             return;
    680         }
    681 
    682         synchronized (mToastQueue) {
    683             int callingPid = Binder.getCallingPid();
    684             long callingId = Binder.clearCallingIdentity();
    685             try {
    686                 ToastRecord record;
    687                 int index = indexOfToastLocked(pkg, callback);
    688                 // If it's already in the queue, we update it in place, we don't
    689                 // move it to the end of the queue.
    690                 if (index >= 0) {
    691                     record = mToastQueue.get(index);
    692                     record.update(duration);
    693                 } else {
    694                     // Limit the number of toasts that any given package except the android
    695                     // package can enqueue.  Prevents DOS attacks and deals with leaks.
    696                     if (!isSystemToast) {
    697                         int count = 0;
    698                         final int N = mToastQueue.size();
    699                         for (int i=0; i<N; i++) {
    700                              final ToastRecord r = mToastQueue.get(i);
    701                              if (r.pkg.equals(pkg)) {
    702                                  count++;
    703                                  if (count >= MAX_PACKAGE_NOTIFICATIONS) {
    704                                      Slog.e(TAG, "Package has already posted " + count
    705                                             + " toasts. Not showing more. Package=" + pkg);
    706                                      return;
    707                                  }
    708                              }
    709                         }
    710                     }
    711 
    712                     record = new ToastRecord(callingPid, pkg, callback, duration);
    713                     mToastQueue.add(record);
    714                     index = mToastQueue.size() - 1;
    715                     keepProcessAliveLocked(callingPid);
    716                 }
    717                 // If it's at index 0, it's the current toast.  It doesn't matter if it's
    718                 // new or just been updated.  Call back and tell it to show itself.
    719                 // If the callback fails, this will remove it from the list, so don't
    720                 // assume that it's valid after this.
    721                 if (index == 0) {
    722                     showNextToastLocked();
    723                 }
    724             } finally {
    725                 Binder.restoreCallingIdentity(callingId);
    726             }
    727         }
    728     }
    729 
    730     public void cancelToast(String pkg, ITransientNotification callback) {
    731         Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
    732 
    733         if (pkg == null || callback == null) {
    734             Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
    735             return ;
    736         }
    737 
    738         synchronized (mToastQueue) {
    739             long callingId = Binder.clearCallingIdentity();
    740             try {
    741                 int index = indexOfToastLocked(pkg, callback);
    742                 if (index >= 0) {
    743                     cancelToastLocked(index);
    744                 } else {
    745                     Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback);
    746                 }
    747             } finally {
    748                 Binder.restoreCallingIdentity(callingId);
    749             }
    750         }
    751     }
    752 
    753     private void showNextToastLocked() {
    754         ToastRecord record = mToastQueue.get(0);
    755         while (record != null) {
    756             if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
    757             try {
    758                 record.callback.show();
    759                 scheduleTimeoutLocked(record, false);
    760                 return;
    761             } catch (RemoteException e) {
    762                 Slog.w(TAG, "Object died trying to show notification " + record.callback
    763                         + " in package " + record.pkg);
    764                 // remove it from the list and let the process die
    765                 int index = mToastQueue.indexOf(record);
    766                 if (index >= 0) {
    767                     mToastQueue.remove(index);
    768                 }
    769                 keepProcessAliveLocked(record.pid);
    770                 if (mToastQueue.size() > 0) {
    771                     record = mToastQueue.get(0);
    772                 } else {
    773                     record = null;
    774                 }
    775             }
    776         }
    777     }
    778 
    779     private void cancelToastLocked(int index) {
    780         ToastRecord record = mToastQueue.get(index);
    781         try {
    782             record.callback.hide();
    783         } catch (RemoteException e) {
    784             Slog.w(TAG, "Object died trying to hide notification " + record.callback
    785                     + " in package " + record.pkg);
    786             // don't worry about this, we're about to remove it from
    787             // the list anyway
    788         }
    789         mToastQueue.remove(index);
    790         keepProcessAliveLocked(record.pid);
    791         if (mToastQueue.size() > 0) {
    792             // Show the next one. If the callback fails, this will remove
    793             // it from the list, so don't assume that the list hasn't changed
    794             // after this point.
    795             showNextToastLocked();
    796         }
    797     }
    798 
    799     private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
    800     {
    801         Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
    802         long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
    803         mHandler.removeCallbacksAndMessages(r);
    804         mHandler.sendMessageDelayed(m, delay);
    805     }
    806 
    807     private void handleTimeout(ToastRecord record)
    808     {
    809         if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
    810         synchronized (mToastQueue) {
    811             int index = indexOfToastLocked(record.pkg, record.callback);
    812             if (index >= 0) {
    813                 cancelToastLocked(index);
    814             }
    815         }
    816     }
    817 
    818     // lock on mToastQueue
    819     private int indexOfToastLocked(String pkg, ITransientNotification callback)
    820     {
    821         IBinder cbak = callback.asBinder();
    822         ArrayList<ToastRecord> list = mToastQueue;
    823         int len = list.size();
    824         for (int i=0; i<len; i++) {
    825             ToastRecord r = list.get(i);
    826             if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
    827                 return i;
    828             }
    829         }
    830         return -1;
    831     }
    832 
    833     // lock on mToastQueue
    834     private void keepProcessAliveLocked(int pid)
    835     {
    836         int toastCount = 0; // toasts from this pid
    837         ArrayList<ToastRecord> list = mToastQueue;
    838         int N = list.size();
    839         for (int i=0; i<N; i++) {
    840             ToastRecord r = list.get(i);
    841             if (r.pid == pid) {
    842                 toastCount++;
    843             }
    844         }
    845         try {
    846             mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
    847         } catch (RemoteException e) {
    848             // Shouldn't happen.
    849         }
    850     }
    851 
    852     private final class WorkerHandler extends Handler
    853     {
    854         @Override
    855         public void handleMessage(Message msg)
    856         {
    857             switch (msg.what)
    858             {
    859                 case MESSAGE_TIMEOUT:
    860                     handleTimeout((ToastRecord)msg.obj);
    861                     break;
    862             }
    863         }
    864     }
    865 
    866 
    867     // Notifications
    868     // ============================================================================
    869     public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
    870             int[] idOut, int userId)
    871     {
    872         enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
    873                 tag, id, notification, idOut, userId);
    874     }
    875 
    876     private final static int clamp(int x, int low, int high) {
    877         return (x < low) ? low : ((x > high) ? high : x);
    878     }
    879 
    880     // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
    881     // uid/pid of another application)
    882     public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
    883             String tag, int id, Notification notification, int[] idOut, int userId)
    884     {
    885         if (DBG) {
    886             Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
    887         }
    888         checkCallerIsSystemOrSameApp(pkg);
    889         final boolean isSystemNotification = ("android".equals(pkg));
    890 
    891         userId = ActivityManager.handleIncomingUser(callingPid,
    892                 callingUid, userId, true, false, "enqueueNotification", pkg);
    893         final UserHandle user = new UserHandle(userId);
    894 
    895         // Limit the number of notifications that any given package except the android
    896         // package can enqueue.  Prevents DOS attacks and deals with leaks.
    897         if (!isSystemNotification) {
    898             synchronized (mNotificationList) {
    899                 int count = 0;
    900                 final int N = mNotificationList.size();
    901                 for (int i=0; i<N; i++) {
    902                     final NotificationRecord r = mNotificationList.get(i);
    903                     if (r.pkg.equals(pkg) && r.userId == userId) {
    904                         count++;
    905                         if (count >= MAX_PACKAGE_NOTIFICATIONS) {
    906                             Slog.e(TAG, "Package has already posted " + count
    907                                     + " notifications.  Not showing more.  package=" + pkg);
    908                             return;
    909                         }
    910                     }
    911                 }
    912             }
    913         }
    914 
    915         // This conditional is a dirty hack to limit the logging done on
    916         //     behalf of the download manager without affecting other apps.
    917         if (!pkg.equals("com.android.providers.downloads")
    918                 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
    919             EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId,
    920                     notification.toString());
    921         }
    922 
    923         if (pkg == null || notification == null) {
    924             throw new IllegalArgumentException("null not allowed: pkg=" + pkg
    925                     + " id=" + id + " notification=" + notification);
    926         }
    927         if (notification.icon != 0) {
    928             if (notification.contentView == null) {
    929                 throw new IllegalArgumentException("contentView required: pkg=" + pkg
    930                         + " id=" + id + " notification=" + notification);
    931             }
    932         }
    933 
    934         // === Scoring ===
    935 
    936         // 0. Sanitize inputs
    937         notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX);
    938         // Migrate notification flags to scores
    939         if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
    940             if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX;
    941         } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
    942             if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH;
    943         }
    944 
    945         // 1. initial score: buckets of 10, around the app
    946         int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
    947 
    948         // 2. Consult external heuristics (TBD)
    949 
    950         // 3. Apply local rules
    951 
    952         // blocked apps
    953         if (ENABLE_BLOCKED_NOTIFICATIONS && !isSystemNotification && !areNotificationsEnabledForPackageInt(pkg)) {
    954             score = JUNK_SCORE;
    955             Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request.");
    956         }
    957 
    958         if (DBG) {
    959             Slog.v(TAG, "Assigned score=" + score + " to " + notification);
    960         }
    961 
    962         if (score < SCORE_DISPLAY_THRESHOLD) {
    963             // Notification will be blocked because the score is too low.
    964             return;
    965         }
    966 
    967         synchronized (mNotificationList) {
    968             NotificationRecord r = new NotificationRecord(pkg, tag, id,
    969                     callingUid, callingPid, userId,
    970                     score,
    971                     notification);
    972             NotificationRecord old = null;
    973 
    974             int index = indexOfNotificationLocked(pkg, tag, id, userId);
    975             if (index < 0) {
    976                 mNotificationList.add(r);
    977             } else {
    978                 old = mNotificationList.remove(index);
    979                 mNotificationList.add(index, r);
    980                 // Make sure we don't lose the foreground service state.
    981                 if (old != null) {
    982                     notification.flags |=
    983                         old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
    984                 }
    985             }
    986 
    987             // Ensure if this is a foreground service that the proper additional
    988             // flags are set.
    989             if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
    990                 notification.flags |= Notification.FLAG_ONGOING_EVENT
    991                         | Notification.FLAG_NO_CLEAR;
    992             }
    993 
    994             final int currentUser;
    995             final long token = Binder.clearCallingIdentity();
    996             try {
    997                 currentUser = ActivityManager.getCurrentUser();
    998             } finally {
    999                 Binder.restoreCallingIdentity(token);
   1000             }
   1001 
   1002             if (notification.icon != 0) {
   1003                 final StatusBarNotification n = new StatusBarNotification(
   1004                         pkg, id, tag, r.uid, r.initialPid, score, notification, user);
   1005                 if (old != null && old.statusBarKey != null) {
   1006                     r.statusBarKey = old.statusBarKey;
   1007                     long identity = Binder.clearCallingIdentity();
   1008                     try {
   1009                         mStatusBar.updateNotification(r.statusBarKey, n);
   1010                     }
   1011                     finally {
   1012                         Binder.restoreCallingIdentity(identity);
   1013                     }
   1014                 } else {
   1015                     long identity = Binder.clearCallingIdentity();
   1016                     try {
   1017                         r.statusBarKey = mStatusBar.addNotification(n);
   1018                         if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
   1019                             mAttentionLight.pulse();
   1020                         }
   1021                     }
   1022                     finally {
   1023                         Binder.restoreCallingIdentity(identity);
   1024                     }
   1025                 }
   1026                 // Send accessibility events only for the current user.
   1027                 if (currentUser == userId) {
   1028                     sendAccessibilityEvent(notification, pkg);
   1029                 }
   1030             } else {
   1031                 Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
   1032                 if (old != null && old.statusBarKey != null) {
   1033                     long identity = Binder.clearCallingIdentity();
   1034                     try {
   1035                         mStatusBar.removeNotification(old.statusBarKey);
   1036                     }
   1037                     finally {
   1038                         Binder.restoreCallingIdentity(identity);
   1039                     }
   1040                 }
   1041             }
   1042 
   1043             // If we're not supposed to beep, vibrate, etc. then don't.
   1044             if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
   1045                     && (!(old != null
   1046                         && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
   1047                     && (r.userId == UserHandle.USER_ALL ||
   1048                         (r.userId == userId && r.userId == currentUser))
   1049                     && mSystemReady) {
   1050 
   1051                 final AudioManager audioManager = (AudioManager) mContext
   1052                 .getSystemService(Context.AUDIO_SERVICE);
   1053                 // sound
   1054                 final boolean useDefaultSound =
   1055                     (notification.defaults & Notification.DEFAULT_SOUND) != 0;
   1056                 if (useDefaultSound || notification.sound != null) {
   1057                     Uri uri;
   1058                     if (useDefaultSound) {
   1059                         uri = Settings.System.DEFAULT_NOTIFICATION_URI;
   1060                     } else {
   1061                         uri = notification.sound;
   1062                     }
   1063                     boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
   1064                     int audioStreamType;
   1065                     if (notification.audioStreamType >= 0) {
   1066                         audioStreamType = notification.audioStreamType;
   1067                     } else {
   1068                         audioStreamType = DEFAULT_STREAM_TYPE;
   1069                     }
   1070                     mSoundNotification = r;
   1071                     // do not play notifications if stream volume is 0
   1072                     // (typically because ringer mode is silent) or if speech recognition is active.
   1073                     if ((audioManager.getStreamVolume(audioStreamType) != 0)
   1074                             && !audioManager.isSpeechRecognitionActive()) {
   1075                         final long identity = Binder.clearCallingIdentity();
   1076                         try {
   1077                             final IRingtonePlayer player = mAudioService.getRingtonePlayer();
   1078                             if (player != null) {
   1079                                 player.playAsync(uri, user, looping, audioStreamType);
   1080                             }
   1081                         } catch (RemoteException e) {
   1082                         } finally {
   1083                             Binder.restoreCallingIdentity(identity);
   1084                         }
   1085                     }
   1086                 }
   1087 
   1088                 // vibrate
   1089                 // new in 4.2: if there was supposed to be a sound and we're in vibrate mode,
   1090                 // we always vibrate, even if no vibration was specified
   1091                 final boolean convertSoundToVibration =
   1092                            notification.vibrate == null
   1093                         && (useDefaultSound || notification.sound != null)
   1094                         && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
   1095 
   1096                 final boolean useDefaultVibrate =
   1097                     (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
   1098                     || convertSoundToVibration;
   1099 
   1100                 if ((useDefaultVibrate || notification.vibrate != null)
   1101                         && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {
   1102                     mVibrateNotification = r;
   1103 
   1104                     mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN
   1105                                                         : notification.vibrate,
   1106                               ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
   1107                 }
   1108             }
   1109 
   1110             // this option doesn't shut off the lights
   1111 
   1112             // light
   1113             // the most recent thing gets the light
   1114             mLights.remove(old);
   1115             if (mLedNotification == old) {
   1116                 mLedNotification = null;
   1117             }
   1118             //Slog.i(TAG, "notification.lights="
   1119             //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
   1120             if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
   1121                 mLights.add(r);
   1122                 updateLightsLocked();
   1123             } else {
   1124                 if (old != null
   1125                         && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
   1126                     updateLightsLocked();
   1127                 }
   1128             }
   1129         }
   1130 
   1131         idOut[0] = id;
   1132     }
   1133 
   1134     private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
   1135         AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
   1136         if (!manager.isEnabled()) {
   1137             return;
   1138         }
   1139 
   1140         AccessibilityEvent event =
   1141             AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
   1142         event.setPackageName(packageName);
   1143         event.setClassName(Notification.class.getName());
   1144         event.setParcelableData(notification);
   1145         CharSequence tickerText = notification.tickerText;
   1146         if (!TextUtils.isEmpty(tickerText)) {
   1147             event.getText().add(tickerText);
   1148         }
   1149 
   1150         manager.sendAccessibilityEvent(event);
   1151     }
   1152 
   1153     private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
   1154         // tell the app
   1155         if (sendDelete) {
   1156             if (r.notification.deleteIntent != null) {
   1157                 try {
   1158                     r.notification.deleteIntent.send();
   1159                 } catch (PendingIntent.CanceledException ex) {
   1160                     // do nothing - there's no relevant way to recover, and
   1161                     //     no reason to let this propagate
   1162                     Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
   1163                 }
   1164             }
   1165         }
   1166 
   1167         // status bar
   1168         if (r.notification.icon != 0) {
   1169             long identity = Binder.clearCallingIdentity();
   1170             try {
   1171                 mStatusBar.removeNotification(r.statusBarKey);
   1172             }
   1173             finally {
   1174                 Binder.restoreCallingIdentity(identity);
   1175             }
   1176             r.statusBarKey = null;
   1177         }
   1178 
   1179         // sound
   1180         if (mSoundNotification == r) {
   1181             mSoundNotification = null;
   1182             final long identity = Binder.clearCallingIdentity();
   1183             try {
   1184                 final IRingtonePlayer player = mAudioService.getRingtonePlayer();
   1185                 if (player != null) {
   1186                     player.stopAsync();
   1187                 }
   1188             } catch (RemoteException e) {
   1189             } finally {
   1190                 Binder.restoreCallingIdentity(identity);
   1191             }
   1192         }
   1193 
   1194         // vibrate
   1195         if (mVibrateNotification == r) {
   1196             mVibrateNotification = null;
   1197             long identity = Binder.clearCallingIdentity();
   1198             try {
   1199                 mVibrator.cancel();
   1200             }
   1201             finally {
   1202                 Binder.restoreCallingIdentity(identity);
   1203             }
   1204         }
   1205 
   1206         // light
   1207         mLights.remove(r);
   1208         if (mLedNotification == r) {
   1209             mLedNotification = null;
   1210         }
   1211     }
   1212 
   1213     /**
   1214      * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
   1215      * and none of the {@code mustNotHaveFlags}.
   1216      */
   1217     private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
   1218             int mustNotHaveFlags, boolean sendDelete, int userId) {
   1219         EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId,
   1220                 mustHaveFlags, mustNotHaveFlags);
   1221 
   1222         synchronized (mNotificationList) {
   1223             int index = indexOfNotificationLocked(pkg, tag, id, userId);
   1224             if (index >= 0) {
   1225                 NotificationRecord r = mNotificationList.get(index);
   1226 
   1227                 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
   1228                     return;
   1229                 }
   1230                 if ((r.notification.flags & mustNotHaveFlags) != 0) {
   1231                     return;
   1232                 }
   1233 
   1234                 mNotificationList.remove(index);
   1235 
   1236                 cancelNotificationLocked(r, sendDelete);
   1237                 updateLightsLocked();
   1238             }
   1239         }
   1240     }
   1241 
   1242     /**
   1243      * Determine whether the userId applies to the notification in question, either because
   1244      * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
   1245      */
   1246     private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
   1247         return
   1248                 // looking for USER_ALL notifications? match everything
   1249                    userId == UserHandle.USER_ALL
   1250                 // a notification sent to USER_ALL matches any query
   1251                 || r.userId == UserHandle.USER_ALL
   1252                 // an exact user match
   1253                 || r.userId == userId;
   1254     }
   1255 
   1256     /**
   1257      * Cancels all notifications from a given package that have all of the
   1258      * {@code mustHaveFlags}.
   1259      */
   1260     boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
   1261             int mustNotHaveFlags, boolean doit, int userId) {
   1262         EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, userId,
   1263                 mustHaveFlags, mustNotHaveFlags);
   1264 
   1265         synchronized (mNotificationList) {
   1266             final int N = mNotificationList.size();
   1267             boolean canceledSomething = false;
   1268             for (int i = N-1; i >= 0; --i) {
   1269                 NotificationRecord r = mNotificationList.get(i);
   1270                 if (!notificationMatchesUserId(r, userId)) {
   1271                     continue;
   1272                 }
   1273                 // Don't remove notifications to all, if there's no package name specified
   1274                 if (r.userId == UserHandle.USER_ALL && pkg == null) {
   1275                     continue;
   1276                 }
   1277                 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
   1278                     continue;
   1279                 }
   1280                 if ((r.notification.flags & mustNotHaveFlags) != 0) {
   1281                     continue;
   1282                 }
   1283                 if (pkg != null && !r.pkg.equals(pkg)) {
   1284                     continue;
   1285                 }
   1286                 canceledSomething = true;
   1287                 if (!doit) {
   1288                     return true;
   1289                 }
   1290                 mNotificationList.remove(i);
   1291                 cancelNotificationLocked(r, false);
   1292             }
   1293             if (canceledSomething) {
   1294                 updateLightsLocked();
   1295             }
   1296             return canceledSomething;
   1297         }
   1298     }
   1299 
   1300     public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
   1301         checkCallerIsSystemOrSameApp(pkg);
   1302         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
   1303                 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
   1304         // Don't allow client applications to cancel foreground service notis.
   1305         cancelNotification(pkg, tag, id, 0,
   1306                 Binder.getCallingUid() == Process.SYSTEM_UID
   1307                 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
   1308     }
   1309 
   1310     public void cancelAllNotifications(String pkg, int userId) {
   1311         checkCallerIsSystemOrSameApp(pkg);
   1312 
   1313         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
   1314                 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
   1315 
   1316         // Calling from user space, don't allow the canceling of actively
   1317         // running foreground services.
   1318         cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
   1319     }
   1320 
   1321     void checkCallerIsSystem() {
   1322         int uid = Binder.getCallingUid();
   1323         if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
   1324             return;
   1325         }
   1326         throw new SecurityException("Disallowed call for uid " + uid);
   1327     }
   1328 
   1329     void checkCallerIsSystemOrSameApp(String pkg) {
   1330         int uid = Binder.getCallingUid();
   1331         if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
   1332             return;
   1333         }
   1334         try {
   1335             ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
   1336                     pkg, 0, UserHandle.getCallingUserId());
   1337             if (!UserHandle.isSameApp(ai.uid, uid)) {
   1338                 throw new SecurityException("Calling uid " + uid + " gave package"
   1339                         + pkg + " which is owned by uid " + ai.uid);
   1340             }
   1341         } catch (RemoteException re) {
   1342             throw new SecurityException("Unknown package " + pkg + "\n" + re);
   1343         }
   1344     }
   1345 
   1346     void cancelAll(int userId) {
   1347         synchronized (mNotificationList) {
   1348             final int N = mNotificationList.size();
   1349             for (int i=N-1; i>=0; i--) {
   1350                 NotificationRecord r = mNotificationList.get(i);
   1351 
   1352                 if (!notificationMatchesUserId(r, userId)) {
   1353                     continue;
   1354                 }
   1355 
   1356                 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
   1357                                 | Notification.FLAG_NO_CLEAR)) == 0) {
   1358                     mNotificationList.remove(i);
   1359                     cancelNotificationLocked(r, true);
   1360                 }
   1361             }
   1362 
   1363             updateLightsLocked();
   1364         }
   1365     }
   1366 
   1367     // lock on mNotificationList
   1368     private void updateLightsLocked()
   1369     {
   1370         // handle notification lights
   1371         if (mLedNotification == null) {
   1372             // get next notification, if any
   1373             int n = mLights.size();
   1374             if (n > 0) {
   1375                 mLedNotification = mLights.get(n-1);
   1376             }
   1377         }
   1378 
   1379         // Don't flash while we are in a call or screen is on
   1380         if (mLedNotification == null || mInCall || mScreenOn) {
   1381             mNotificationLight.turnOff();
   1382         } else {
   1383             int ledARGB = mLedNotification.notification.ledARGB;
   1384             int ledOnMS = mLedNotification.notification.ledOnMS;
   1385             int ledOffMS = mLedNotification.notification.ledOffMS;
   1386             if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
   1387                 ledARGB = mDefaultNotificationColor;
   1388                 ledOnMS = mDefaultNotificationLedOn;
   1389                 ledOffMS = mDefaultNotificationLedOff;
   1390             }
   1391             if (mNotificationPulseEnabled) {
   1392                 // pulse repeatedly
   1393                 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
   1394                         ledOnMS, ledOffMS);
   1395             }
   1396         }
   1397     }
   1398 
   1399     // lock on mNotificationList
   1400     private int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
   1401     {
   1402         ArrayList<NotificationRecord> list = mNotificationList;
   1403         final int len = list.size();
   1404         for (int i=0; i<len; i++) {
   1405             NotificationRecord r = list.get(i);
   1406             if (!notificationMatchesUserId(r, userId) || r.id != id) {
   1407                 continue;
   1408             }
   1409             if (tag == null) {
   1410                 if (r.tag != null) {
   1411                     continue;
   1412                 }
   1413             } else {
   1414                 if (!tag.equals(r.tag)) {
   1415                     continue;
   1416                 }
   1417             }
   1418             if (r.pkg.equals(pkg)) {
   1419                 return i;
   1420             }
   1421         }
   1422         return -1;
   1423     }
   1424 
   1425     private void updateNotificationPulse() {
   1426         synchronized (mNotificationList) {
   1427             updateLightsLocked();
   1428         }
   1429     }
   1430 
   1431     // ======================================================================
   1432     @Override
   1433     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1434         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
   1435                 != PackageManager.PERMISSION_GRANTED) {
   1436             pw.println("Permission Denial: can't dump NotificationManager from from pid="
   1437                     + Binder.getCallingPid()
   1438                     + ", uid=" + Binder.getCallingUid());
   1439             return;
   1440         }
   1441 
   1442         pw.println("Current Notification Manager state:");
   1443 
   1444         int N;
   1445 
   1446         synchronized (mToastQueue) {
   1447             N = mToastQueue.size();
   1448             if (N > 0) {
   1449                 pw.println("  Toast Queue:");
   1450                 for (int i=0; i<N; i++) {
   1451                     mToastQueue.get(i).dump(pw, "    ");
   1452                 }
   1453                 pw.println("  ");
   1454             }
   1455 
   1456         }
   1457 
   1458         synchronized (mNotificationList) {
   1459             N = mNotificationList.size();
   1460             if (N > 0) {
   1461                 pw.println("  Notification List:");
   1462                 for (int i=0; i<N; i++) {
   1463                     mNotificationList.get(i).dump(pw, "    ", mContext);
   1464                 }
   1465                 pw.println("  ");
   1466             }
   1467 
   1468             N = mLights.size();
   1469             if (N > 0) {
   1470                 pw.println("  Lights List:");
   1471                 for (int i=0; i<N; i++) {
   1472                     mLights.get(i).dump(pw, "    ", mContext);
   1473                 }
   1474                 pw.println("  ");
   1475             }
   1476 
   1477             pw.println("  mSoundNotification=" + mSoundNotification);
   1478             pw.println("  mVibrateNotification=" + mVibrateNotification);
   1479             pw.println("  mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
   1480             pw.println("  mSystemReady=" + mSystemReady);
   1481         }
   1482     }
   1483 }
   1484