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