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