1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
20import static org.xmlpull.v1.XmlPullParser.END_TAG;
21import static org.xmlpull.v1.XmlPullParser.START_TAG;
22
23import android.app.ActivityManager;
24import android.app.ActivityManagerNative;
25import android.app.AppGlobals;
26import android.app.IActivityManager;
27import android.app.INotificationManager;
28import android.app.ITransientNotification;
29import android.app.Notification;
30import android.app.PendingIntent;
31import android.app.StatusBarManager;
32import android.content.BroadcastReceiver;
33import android.content.ContentResolver;
34import android.content.Context;
35import android.content.Intent;
36import android.content.IntentFilter;
37import android.content.pm.ApplicationInfo;
38import android.content.pm.PackageManager;
39import android.content.pm.PackageManager.NameNotFoundException;
40import android.content.res.Resources;
41import android.database.ContentObserver;
42import android.media.AudioManager;
43import android.media.IAudioService;
44import android.media.IRingtonePlayer;
45import android.net.Uri;
46import android.os.Binder;
47import android.os.Handler;
48import android.os.IBinder;
49import android.os.Message;
50import android.os.Process;
51import android.os.RemoteException;
52import android.os.ServiceManager;
53import android.os.UserHandle;
54import android.os.Vibrator;
55import android.provider.Settings;
56import android.telephony.TelephonyManager;
57import android.text.TextUtils;
58import android.util.AtomicFile;
59import android.util.EventLog;
60import android.util.Log;
61import android.util.Slog;
62import android.util.Xml;
63import android.view.accessibility.AccessibilityEvent;
64import android.view.accessibility.AccessibilityManager;
65import android.widget.RemoteViews;
66import android.widget.Toast;
67
68import com.android.internal.statusbar.StatusBarNotification;
69import com.android.internal.util.FastXmlSerializer;
70
71import org.xmlpull.v1.XmlPullParser;
72import org.xmlpull.v1.XmlPullParserException;
73import org.xmlpull.v1.XmlSerializer;
74
75import java.io.File;
76import java.io.FileDescriptor;
77import java.io.FileInputStream;
78import java.io.FileNotFoundException;
79import java.io.FileOutputStream;
80import java.io.IOException;
81import java.io.PrintWriter;
82import java.util.ArrayList;
83import java.util.Arrays;
84import java.util.HashSet;
85
86import libcore.io.IoUtils;
87
88
89/** {@hide} */
90public 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