blob: 6833a57100c793193e085e5ef445438e95dab092 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Joe Onorato18e69df2010-05-17 22:26:12 -070019import com.android.internal.statusbar.StatusBarNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -070020
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.app.ActivityManagerNative;
22import android.app.IActivityManager;
23import android.app.INotificationManager;
24import android.app.ITransientNotification;
25import android.app.Notification;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070026import android.app.NotificationManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.app.PendingIntent;
28import android.app.StatusBarManager;
29import android.content.BroadcastReceiver;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070030import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070034import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.content.pm.PackageManager;
36import android.content.pm.PackageManager.NameNotFoundException;
37import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070038import android.database.ContentObserver;
svetoslavganov75986cf2009-05-14 22:28:01 -070039import android.media.AudioManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.os.Binder;
Andy Stadler110988c2010-12-03 14:29:16 -080042import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.os.IBinder;
45import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070046import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070047import android.os.RemoteException;
Amith Yamasani742a6712011-05-04 14:49:28 -070048import android.os.UserId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Vibrator;
50import android.provider.Settings;
Daniel Sandlere96ffb12010-03-11 13:38:06 -050051import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070052import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.util.EventLog;
54import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -080055import android.util.Slog;
svetoslavganov75986cf2009-05-14 22:28:01 -070056import android.view.accessibility.AccessibilityEvent;
57import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.widget.Toast;
59
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import java.io.FileDescriptor;
61import java.io.PrintWriter;
62import java.util.ArrayList;
63import java.util.Arrays;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
Daniel Sandlerd0a2f862010-08-03 15:29:31 -040065/** {@hide} */
66public class NotificationManagerService extends INotificationManager.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067{
68 private static final String TAG = "NotificationService";
69 private static final boolean DBG = false;
70
Joe Onoratobd73d012010-06-04 11:44:54 -070071 private static final int MAX_PACKAGE_NOTIFICATIONS = 50;
72
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 // message codes
74 private static final int MESSAGE_TIMEOUT = 2;
75
76 private static final int LONG_DELAY = 3500; // 3.5 seconds
77 private static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -080078
79 private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080
81 private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
Daniel Sandler49a2ad12012-03-28 15:46:39 -040082 private static final boolean SCORE_ONGOING_HIGHER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083
84 final Context mContext;
85 final IActivityManager mAm;
86 final IBinder mForegroundToken = new Binder();
87
88 private WorkerHandler mHandler;
Joe Onorato089de882010-04-12 08:18:45 -070089 private StatusBarManagerService mStatusBar;
Mike Lockwood3cb67a32009-11-27 14:25:58 -050090 private LightsService.Light mNotificationLight;
91 private LightsService.Light mAttentionLight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092
Mike Lockwood670f9322010-01-20 12:13:36 -050093 private int mDefaultNotificationColor;
94 private int mDefaultNotificationLedOn;
95 private int mDefaultNotificationLedOff;
96
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 private NotificationRecord mSoundNotification;
Jean-Michel Trivi211957f2010-03-26 18:19:33 -070098 private NotificationPlayer mSound;
Joe Onorato30275482009-07-08 17:09:14 -070099 private boolean mSystemReady;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400100 private int mDisabledNotifications;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101
102 private NotificationRecord mVibrateNotification;
103 private Vibrator mVibrator = new Vibrator();
104
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500105 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400106 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500107 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500108 private boolean mNotificationPulseEnabled;
109
Fred Quintana6ecaff12009-09-25 14:23:13 -0700110 private final ArrayList<NotificationRecord> mNotificationList =
111 new ArrayList<NotificationRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
113 private ArrayList<ToastRecord> mToastQueue;
114
115 private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 private NotificationRecord mLedNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -0700117
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 private static String idDebugString(Context baseContext, String packageName, int id) {
119 Context c = null;
120
121 if (packageName != null) {
122 try {
123 c = baseContext.createPackageContext(packageName, 0);
124 } catch (NameNotFoundException e) {
125 c = baseContext;
126 }
127 } else {
128 c = baseContext;
129 }
130
131 String pkg;
132 String type;
133 String name;
134
135 Resources r = c.getResources();
136 try {
137 return r.getResourceName(id);
138 } catch (Resources.NotFoundException e) {
139 return "<name unknown>";
140 }
141 }
142
143 private static final class NotificationRecord
144 {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700145 final String pkg;
146 final String tag;
147 final int id;
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700148 final int uid;
149 final int initialPid;
Fred Quintana6ecaff12009-09-25 14:23:13 -0700150 final Notification notification;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500151 final int score;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 IBinder statusBarKey;
153
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500154 NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int score, Notification notification)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 {
156 this.pkg = pkg;
Fred Quintana6ecaff12009-09-25 14:23:13 -0700157 this.tag = tag;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158 this.id = id;
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700159 this.uid = uid;
160 this.initialPid = initialPid;
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500161 this.score = score;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 this.notification = notification;
163 }
Fred Quintana6ecaff12009-09-25 14:23:13 -0700164
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 void dump(PrintWriter pw, String prefix, Context baseContext) {
166 pw.println(prefix + this);
167 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
168 + " / " + idDebugString(baseContext, this.pkg, notification.icon));
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500169 pw.println(prefix + " pri=" + notification.priority);
170 pw.println(prefix + " score=" + this.score);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 pw.println(prefix + " contentIntent=" + notification.contentIntent);
172 pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
173 pw.println(prefix + " tickerText=" + notification.tickerText);
174 pw.println(prefix + " contentView=" + notification.contentView);
175 pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults));
176 pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags));
177 pw.println(prefix + " sound=" + notification.sound);
178 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
179 pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB)
180 + " ledOnMS=" + notification.ledOnMS
181 + " ledOffMS=" + notification.ledOffMS);
182 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800183
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 @Override
185 public final String toString()
186 {
187 return "NotificationRecord{"
188 + Integer.toHexString(System.identityHashCode(this))
189 + " pkg=" + pkg
Fred Quintana6ecaff12009-09-25 14:23:13 -0700190 + " id=" + Integer.toHexString(id)
Daniel Sandlere40451a2011-02-03 14:51:35 -0500191 + " tag=" + tag
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500192 + " score=" + score
Daniel Sandlere40451a2011-02-03 14:51:35 -0500193 + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 }
195 }
196
197 private static final class ToastRecord
198 {
199 final int pid;
200 final String pkg;
201 final ITransientNotification callback;
202 int duration;
203
204 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
205 {
206 this.pid = pid;
207 this.pkg = pkg;
208 this.callback = callback;
209 this.duration = duration;
210 }
211
212 void update(int duration) {
213 this.duration = duration;
214 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800215
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 void dump(PrintWriter pw, String prefix) {
217 pw.println(prefix + this);
218 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800219
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 @Override
221 public final String toString()
222 {
223 return "ToastRecord{"
224 + Integer.toHexString(System.identityHashCode(this))
225 + " pkg=" + pkg
226 + " callback=" + callback
227 + " duration=" + duration;
228 }
229 }
230
Joe Onorato089de882010-04-12 08:18:45 -0700231 private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
232 = new StatusBarManagerService.NotificationCallbacks() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233
234 public void onSetDisabled(int status) {
235 synchronized (mNotificationList) {
236 mDisabledNotifications = status;
237 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
238 // cancel whatever's going on
239 long identity = Binder.clearCallingIdentity();
240 try {
241 mSound.stop();
242 }
243 finally {
244 Binder.restoreCallingIdentity(identity);
245 }
246
247 identity = Binder.clearCallingIdentity();
248 try {
249 mVibrator.cancel();
250 }
251 finally {
252 Binder.restoreCallingIdentity(identity);
253 }
254 }
255 }
256 }
257
258 public void onClearAll() {
259 cancelAll();
260 }
261
Fred Quintana6ecaff12009-09-25 14:23:13 -0700262 public void onNotificationClick(String pkg, String tag, int id) {
263 cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
jhtop.kim2e448f72011-07-13 17:15:32 +0900264 Notification.FLAG_FOREGROUND_SERVICE, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 }
266
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400267 public void onNotificationClear(String pkg, String tag, int id) {
Joe Onorato46439ce2010-11-19 13:56:21 -0800268 cancelNotification(pkg, tag, id, 0,
269 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
270 true);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400271 }
272
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 public void onPanelRevealed() {
274 synchronized (mNotificationList) {
275 // sound
276 mSoundNotification = null;
277 long identity = Binder.clearCallingIdentity();
278 try {
279 mSound.stop();
280 }
281 finally {
282 Binder.restoreCallingIdentity(identity);
283 }
284
285 // vibrate
286 mVibrateNotification = null;
287 identity = Binder.clearCallingIdentity();
288 try {
289 mVibrator.cancel();
290 }
291 finally {
292 Binder.restoreCallingIdentity(identity);
293 }
294
295 // light
296 mLights.clear();
297 mLedNotification = null;
298 updateLightsLocked();
299 }
300 }
Joe Onorato005847b2010-06-04 16:08:02 -0400301
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700302 public void onNotificationError(String pkg, String tag, int id,
303 int uid, int initialPid, String message) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400304 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
305 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
Joe Onorato46439ce2010-11-19 13:56:21 -0800306 cancelNotification(pkg, tag, id, 0, 0, false);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700307 long ident = Binder.clearCallingIdentity();
308 try {
309 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
310 "Bad notification posted from package " + pkg
311 + ": " + message);
312 } catch (RemoteException e) {
313 }
314 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400315 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 };
317
318 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
319 @Override
320 public void onReceive(Context context, Intent intent) {
321 String action = intent.getAction();
322
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800323 boolean queryRestart = false;
324
Mike Lockwood541c9942011-06-12 19:35:45 -0400325 if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800326 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandleraac0eb02011-08-06 22:51:56 -0400327 || action.equals(Intent.ACTION_PACKAGE_CHANGED)
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800328 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800329 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800330 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800331 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800332 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800333 } else if (queryRestart) {
334 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800335 } else {
336 Uri uri = intent.getData();
337 if (uri == null) {
338 return;
339 }
340 String pkgName = uri.getSchemeSpecificPart();
341 if (pkgName == null) {
342 return;
343 }
344 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800346 if (pkgList != null && (pkgList.length > 0)) {
347 for (String pkgName : pkgList) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800348 cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800349 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400351 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
352 // Keep track of screen on/off state, but do not turn off the notification light
353 // until user passes through the lock screen or views the notification.
354 mScreenOn = true;
355 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
356 mScreenOn = false;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500357 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400358 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
359 TelephonyManager.EXTRA_STATE_OFFHOOK));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500360 updateNotificationPulse();
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400361 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
362 // turn off LED when user passes through lock screen
363 mNotificationLight.turnOff();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 }
365 }
366 };
367
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700368 class SettingsObserver extends ContentObserver {
369 SettingsObserver(Handler handler) {
370 super(handler);
371 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800372
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700373 void observe() {
374 ContentResolver resolver = mContext.getContentResolver();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500375 resolver.registerContentObserver(Settings.System.getUriFor(
376 Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700377 update();
378 }
379
380 @Override public void onChange(boolean selfChange) {
381 update();
382 }
383
384 public void update() {
385 ContentResolver resolver = mContext.getContentResolver();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500386 boolean pulseEnabled = Settings.System.getInt(resolver,
387 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
388 if (mNotificationPulseEnabled != pulseEnabled) {
389 mNotificationPulseEnabled = pulseEnabled;
390 updateNotificationPulse();
391 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700392 }
393 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500394
Joe Onorato089de882010-04-12 08:18:45 -0700395 NotificationManagerService(Context context, StatusBarManagerService statusBar,
Mike Lockwood3a322132009-11-24 00:30:52 -0500396 LightsService lights)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 {
398 super();
399 mContext = context;
400 mAm = ActivityManagerNative.getDefault();
Jean-Michel Trivi211957f2010-03-26 18:19:33 -0700401 mSound = new NotificationPlayer(TAG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 mSound.setUsesWakeLock(context);
403 mToastQueue = new ArrayList<ToastRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 mHandler = new WorkerHandler();
San Mehat3ee13172010-02-04 20:54:43 -0800405
Joe Onorato089de882010-04-12 08:18:45 -0700406 mStatusBar = statusBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 statusBar.setNotificationCallbacks(mNotificationCallbacks);
408
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500409 mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
410 mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
411
Mike Lockwood670f9322010-01-20 12:13:36 -0500412 Resources resources = mContext.getResources();
413 mDefaultNotificationColor = resources.getColor(
414 com.android.internal.R.color.config_defaultNotificationColor);
415 mDefaultNotificationLedOn = resources.getInteger(
416 com.android.internal.R.integer.config_defaultNotificationLedOn);
417 mDefaultNotificationLedOff = resources.getInteger(
418 com.android.internal.R.integer.config_defaultNotificationLedOff);
419
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400420 // Don't start allowing notifications until the setup wizard has run once.
421 // After that, including subsequent boots, init with notifications turned on.
422 // This works on the first boot because the setup wizard will toggle this
423 // flag at least once and we'll go back to 0 after that.
424 if (0 == Settings.Secure.getInt(mContext.getContentResolver(),
425 Settings.Secure.DEVICE_PROVISIONED, 0)) {
426 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
427 }
428
Mike Lockwood35e16bf2010-11-30 19:53:36 -0500429 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500431 filter.addAction(Intent.ACTION_SCREEN_ON);
432 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500433 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400434 filter.addAction(Intent.ACTION_USER_PRESENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 mContext.registerReceiver(mIntentReceiver, filter);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800436 IntentFilter pkgFilter = new IntentFilter();
437 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -0400438 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800439 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
440 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
441 pkgFilter.addDataScheme("package");
442 mContext.registerReceiver(mIntentReceiver, pkgFilter);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800443 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800444 mContext.registerReceiver(mIntentReceiver, sdFilter);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800445
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500446 SettingsObserver observer = new SettingsObserver(mHandler);
447 observer.observe();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 }
449
Joe Onorato30275482009-07-08 17:09:14 -0700450 void systemReady() {
451 // no beeping until we're basically done booting
452 mSystemReady = true;
453 }
454
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 // Toasts
456 // ============================================================================
457 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
458 {
Daniel Sandlera7035902010-03-30 15:45:31 -0400459 if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460
461 if (pkg == null || callback == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800462 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 return ;
464 }
465
466 synchronized (mToastQueue) {
467 int callingPid = Binder.getCallingPid();
468 long callingId = Binder.clearCallingIdentity();
469 try {
470 ToastRecord record;
471 int index = indexOfToastLocked(pkg, callback);
472 // If it's already in the queue, we update it in place, we don't
473 // move it to the end of the queue.
474 if (index >= 0) {
475 record = mToastQueue.get(index);
476 record.update(duration);
477 } else {
Vairavan Srinivasanf9eb06c2011-01-21 18:08:36 -0800478 // Limit the number of toasts that any given package except the android
479 // package can enqueue. Prevents DOS attacks and deals with leaks.
480 if (!"android".equals(pkg)) {
481 int count = 0;
482 final int N = mToastQueue.size();
483 for (int i=0; i<N; i++) {
484 final ToastRecord r = mToastQueue.get(i);
485 if (r.pkg.equals(pkg)) {
486 count++;
487 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
488 Slog.e(TAG, "Package has already posted " + count
489 + " toasts. Not showing more. Package=" + pkg);
490 return;
491 }
492 }
493 }
494 }
495
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 record = new ToastRecord(callingPid, pkg, callback, duration);
497 mToastQueue.add(record);
498 index = mToastQueue.size() - 1;
499 keepProcessAliveLocked(callingPid);
500 }
501 // If it's at index 0, it's the current toast. It doesn't matter if it's
502 // new or just been updated. Call back and tell it to show itself.
503 // If the callback fails, this will remove it from the list, so don't
504 // assume that it's valid after this.
505 if (index == 0) {
506 showNextToastLocked();
507 }
508 } finally {
509 Binder.restoreCallingIdentity(callingId);
510 }
511 }
512 }
513
514 public void cancelToast(String pkg, ITransientNotification callback) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800515 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516
517 if (pkg == null || callback == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800518 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 return ;
520 }
521
522 synchronized (mToastQueue) {
523 long callingId = Binder.clearCallingIdentity();
524 try {
525 int index = indexOfToastLocked(pkg, callback);
526 if (index >= 0) {
527 cancelToastLocked(index);
528 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800529 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 }
531 } finally {
532 Binder.restoreCallingIdentity(callingId);
533 }
534 }
535 }
536
537 private void showNextToastLocked() {
538 ToastRecord record = mToastQueue.get(0);
539 while (record != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800540 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 try {
542 record.callback.show();
543 scheduleTimeoutLocked(record, false);
544 return;
545 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800546 Slog.w(TAG, "Object died trying to show notification " + record.callback
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 + " in package " + record.pkg);
548 // remove it from the list and let the process die
549 int index = mToastQueue.indexOf(record);
550 if (index >= 0) {
551 mToastQueue.remove(index);
552 }
553 keepProcessAliveLocked(record.pid);
554 if (mToastQueue.size() > 0) {
555 record = mToastQueue.get(0);
556 } else {
557 record = null;
558 }
559 }
560 }
561 }
562
563 private void cancelToastLocked(int index) {
564 ToastRecord record = mToastQueue.get(index);
565 try {
566 record.callback.hide();
567 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800568 Slog.w(TAG, "Object died trying to hide notification " + record.callback
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 + " in package " + record.pkg);
570 // don't worry about this, we're about to remove it from
571 // the list anyway
572 }
573 mToastQueue.remove(index);
574 keepProcessAliveLocked(record.pid);
575 if (mToastQueue.size() > 0) {
576 // Show the next one. If the callback fails, this will remove
577 // it from the list, so don't assume that the list hasn't changed
578 // after this point.
579 showNextToastLocked();
580 }
581 }
582
583 private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
584 {
585 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
586 long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
587 mHandler.removeCallbacksAndMessages(r);
588 mHandler.sendMessageDelayed(m, delay);
589 }
590
591 private void handleTimeout(ToastRecord record)
592 {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800593 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 synchronized (mToastQueue) {
595 int index = indexOfToastLocked(record.pkg, record.callback);
596 if (index >= 0) {
597 cancelToastLocked(index);
598 }
599 }
600 }
601
602 // lock on mToastQueue
603 private int indexOfToastLocked(String pkg, ITransientNotification callback)
604 {
605 IBinder cbak = callback.asBinder();
606 ArrayList<ToastRecord> list = mToastQueue;
607 int len = list.size();
608 for (int i=0; i<len; i++) {
609 ToastRecord r = list.get(i);
610 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
611 return i;
612 }
613 }
614 return -1;
615 }
616
617 // lock on mToastQueue
618 private void keepProcessAliveLocked(int pid)
619 {
620 int toastCount = 0; // toasts from this pid
621 ArrayList<ToastRecord> list = mToastQueue;
622 int N = list.size();
623 for (int i=0; i<N; i++) {
624 ToastRecord r = list.get(i);
625 if (r.pid == pid) {
626 toastCount++;
627 }
628 }
629 try {
630 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
631 } catch (RemoteException e) {
632 // Shouldn't happen.
633 }
634 }
635
636 private final class WorkerHandler extends Handler
637 {
638 @Override
639 public void handleMessage(Message msg)
640 {
641 switch (msg.what)
642 {
643 case MESSAGE_TIMEOUT:
644 handleTimeout((ToastRecord)msg.obj);
645 break;
646 }
647 }
648 }
649
650
651 // Notifications
652 // ============================================================================
Andy Stadler110988c2010-12-03 14:29:16 -0800653 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
655 {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700656 enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
657 }
658
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400659 public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
660 int[] idOut)
Fred Quintana6ecaff12009-09-25 14:23:13 -0700661 {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400662 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
663 tag, id, notification, idOut);
664 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500665
666 private final static int clamp(int x, int low, int high) {
667 return (x < low) ? low : ((x > high) ? high : x);
Daniel Sandlere40451a2011-02-03 14:51:35 -0500668 }
669
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500670
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400671 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
672 // uid/pid of another application)
673 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
674 String tag, int id, Notification notification, int[] idOut)
675 {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700676 checkIncomingCall(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800677
Joe Onoratobd73d012010-06-04 11:44:54 -0700678 // Limit the number of notifications that any given package except the android
679 // package can enqueue. Prevents DOS attacks and deals with leaks.
680 if (!"android".equals(pkg)) {
681 synchronized (mNotificationList) {
682 int count = 0;
683 final int N = mNotificationList.size();
684 for (int i=0; i<N; i++) {
685 final NotificationRecord r = mNotificationList.get(i);
686 if (r.pkg.equals(pkg)) {
687 count++;
688 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
689 Slog.e(TAG, "Package has already posted " + count
690 + " notifications. Not showing more. package=" + pkg);
691 return;
692 }
693 }
694 }
695 }
696 }
697
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 // This conditional is a dirty hack to limit the logging done on
699 // behalf of the download manager without affecting other apps.
700 if (!pkg.equals("com.android.providers.downloads")
701 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
Daniel Sandlerb64cb882011-11-29 23:48:29 -0500702 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag,
703 notification.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 }
705
706 if (pkg == null || notification == null) {
707 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
708 + " id=" + id + " notification=" + notification);
709 }
710 if (notification.icon != 0) {
711 if (notification.contentView == null) {
712 throw new IllegalArgumentException("contentView required: pkg=" + pkg
713 + " id=" + id + " notification=" + notification);
714 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 }
716
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500717 // === Scoring ===
718
719 // 0. Sanitize inputs
720 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX);
721 // Migrate notification flags to scores
722 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
723 if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX;
Daniel Sandler49a2ad12012-03-28 15:46:39 -0400724 } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500725 if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH;
726 }
727
728 // 1. initial score: buckets of 10, around the app
729 int score = notification.priority * 10; //[-20..20]
730
731 // 2. Consult oracles (external heuristics)
732 // TODO(dsandler): oracles
733
734 // 3. Apply local heuristics & overrides
735
736 // blocked apps
737 // TODO(dsandler): add block db
738 if (pkg.startsWith("com.test.spammer.")) {
739 score = -1000;
740 }
741
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 synchronized (mNotificationList) {
Daniel Sandlere40451a2011-02-03 14:51:35 -0500743 NotificationRecord r = new NotificationRecord(pkg, tag, id,
744 callingUid, callingPid,
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500745 score,
Daniel Sandlere40451a2011-02-03 14:51:35 -0500746 notification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 NotificationRecord old = null;
748
Fred Quintana6ecaff12009-09-25 14:23:13 -0700749 int index = indexOfNotificationLocked(pkg, tag, id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 if (index < 0) {
751 mNotificationList.add(r);
752 } else {
753 old = mNotificationList.remove(index);
754 mNotificationList.add(index, r);
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700755 // Make sure we don't lose the foreground service state.
756 if (old != null) {
757 notification.flags |=
758 old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
759 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800761
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700762 // Ensure if this is a foreground service that the proper additional
763 // flags are set.
764 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
765 notification.flags |= Notification.FLAG_ONGOING_EVENT
766 | Notification.FLAG_NO_CLEAR;
767 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800768
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 if (notification.icon != 0) {
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700770 StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500771 r.uid, r.initialPid, score, notification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 if (old != null && old.statusBarKey != null) {
773 r.statusBarKey = old.statusBarKey;
774 long identity = Binder.clearCallingIdentity();
775 try {
Joe Onorato18e69df2010-05-17 22:26:12 -0700776 mStatusBar.updateNotification(r.statusBarKey, n);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 }
778 finally {
779 Binder.restoreCallingIdentity(identity);
780 }
781 } else {
782 long identity = Binder.clearCallingIdentity();
783 try {
Joe Onorato18e69df2010-05-17 22:26:12 -0700784 r.statusBarKey = mStatusBar.addNotification(n);
Mike Lockwoodece18ef2012-02-13 20:42:19 -0800785 if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
786 mAttentionLight.pulse();
787 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 }
789 finally {
790 Binder.restoreCallingIdentity(identity);
791 }
792 }
Joe Onorato30275482009-07-08 17:09:14 -0700793 sendAccessibilityEvent(notification, pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 } else {
Daniel Sandlere40451a2011-02-03 14:51:35 -0500795 Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 if (old != null && old.statusBarKey != null) {
797 long identity = Binder.clearCallingIdentity();
798 try {
Joe Onorato0cbda992010-05-02 16:28:15 -0700799 mStatusBar.removeNotification(old.statusBarKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800 }
801 finally {
802 Binder.restoreCallingIdentity(identity);
803 }
804 }
805 }
806
807 // If we're not supposed to beep, vibrate, etc. then don't.
808 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
809 && (!(old != null
Joe Onorato30275482009-07-08 17:09:14 -0700810 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
811 && mSystemReady) {
Eric Laurent524dc042009-11-27 05:07:55 -0800812
813 final AudioManager audioManager = (AudioManager) mContext
814 .getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 // sound
816 final boolean useDefaultSound =
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800817 (notification.defaults & Notification.DEFAULT_SOUND) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 if (useDefaultSound || notification.sound != null) {
819 Uri uri;
820 if (useDefaultSound) {
821 uri = Settings.System.DEFAULT_NOTIFICATION_URI;
822 } else {
823 uri = notification.sound;
824 }
825 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
826 int audioStreamType;
827 if (notification.audioStreamType >= 0) {
828 audioStreamType = notification.audioStreamType;
829 } else {
830 audioStreamType = DEFAULT_STREAM_TYPE;
831 }
832 mSoundNotification = r;
Eric Laurent524dc042009-11-27 05:07:55 -0800833 // do not play notifications if stream volume is 0
834 // (typically because ringer mode is silent).
835 if (audioManager.getStreamVolume(audioStreamType) != 0) {
836 long identity = Binder.clearCallingIdentity();
837 try {
838 mSound.play(mContext, uri, looping, audioStreamType);
839 }
840 finally {
841 Binder.restoreCallingIdentity(identity);
842 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 }
844 }
845
846 // vibrate
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 final boolean useDefaultVibrate =
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800848 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 if ((useDefaultVibrate || notification.vibrate != null)
850 && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) {
851 mVibrateNotification = r;
852
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800853 mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 : notification.vibrate,
855 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
856 }
857 }
858
859 // this option doesn't shut off the lights
860
861 // light
862 // the most recent thing gets the light
863 mLights.remove(old);
864 if (mLedNotification == old) {
865 mLedNotification = null;
866 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800867 //Slog.i(TAG, "notification.lights="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
869 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
870 mLights.add(r);
871 updateLightsLocked();
872 } else {
873 if (old != null
874 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
875 updateLightsLocked();
876 }
877 }
878 }
879
880 idOut[0] = id;
881 }
882
Joe Onorato30275482009-07-08 17:09:14 -0700883 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700884 AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
885 if (!manager.isEnabled()) {
886 return;
887 }
888
889 AccessibilityEvent event =
890 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
891 event.setPackageName(packageName);
892 event.setClassName(Notification.class.getName());
893 event.setParcelableData(notification);
894 CharSequence tickerText = notification.tickerText;
895 if (!TextUtils.isEmpty(tickerText)) {
896 event.getText().add(tickerText);
897 }
898
899 manager.sendAccessibilityEvent(event);
900 }
901
Joe Onorato46439ce2010-11-19 13:56:21 -0800902 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
903 // tell the app
904 if (sendDelete) {
905 if (r.notification.deleteIntent != null) {
906 try {
907 r.notification.deleteIntent.send();
908 } catch (PendingIntent.CanceledException ex) {
909 // do nothing - there's no relevant way to recover, and
910 // no reason to let this propagate
911 Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
912 }
913 }
914 }
915
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 // status bar
917 if (r.notification.icon != 0) {
918 long identity = Binder.clearCallingIdentity();
919 try {
Joe Onorato0cbda992010-05-02 16:28:15 -0700920 mStatusBar.removeNotification(r.statusBarKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 }
922 finally {
923 Binder.restoreCallingIdentity(identity);
924 }
925 r.statusBarKey = null;
926 }
927
928 // sound
929 if (mSoundNotification == r) {
930 mSoundNotification = null;
931 long identity = Binder.clearCallingIdentity();
932 try {
933 mSound.stop();
934 }
935 finally {
936 Binder.restoreCallingIdentity(identity);
937 }
938 }
939
940 // vibrate
941 if (mVibrateNotification == r) {
942 mVibrateNotification = null;
943 long identity = Binder.clearCallingIdentity();
944 try {
945 mVibrator.cancel();
946 }
947 finally {
948 Binder.restoreCallingIdentity(identity);
949 }
950 }
951
952 // light
953 mLights.remove(r);
954 if (mLedNotification == r) {
955 mLedNotification = null;
956 }
957 }
958
959 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700960 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800961 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 */
Fred Quintana6ecaff12009-09-25 14:23:13 -0700963 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
Joe Onorato46439ce2010-11-19 13:56:21 -0800964 int mustNotHaveFlags, boolean sendDelete) {
Daniel Sandlerb64cb882011-11-29 23:48:29 -0500965 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag,
966 mustHaveFlags, mustNotHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967
968 synchronized (mNotificationList) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700969 int index = indexOfNotificationLocked(pkg, tag, id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 if (index >= 0) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700971 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800972
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
974 return;
975 }
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700976 if ((r.notification.flags & mustNotHaveFlags) != 0) {
977 return;
978 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800979
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 mNotificationList.remove(index);
981
Joe Onorato46439ce2010-11-19 13:56:21 -0800982 cancelNotificationLocked(r, sendDelete);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983 updateLightsLocked();
984 }
985 }
986 }
987
988 /**
989 * Cancels all notifications from a given package that have all of the
990 * {@code mustHaveFlags}.
991 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800992 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
993 int mustNotHaveFlags, boolean doit) {
Daniel Sandlerb64cb882011-11-29 23:48:29 -0500994 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags,
995 mustNotHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996
997 synchronized (mNotificationList) {
998 final int N = mNotificationList.size();
999 boolean canceledSomething = false;
1000 for (int i = N-1; i >= 0; --i) {
1001 NotificationRecord r = mNotificationList.get(i);
1002 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
1003 continue;
1004 }
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001005 if ((r.notification.flags & mustNotHaveFlags) != 0) {
1006 continue;
1007 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 if (!r.pkg.equals(pkg)) {
1009 continue;
1010 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001011 canceledSomething = true;
1012 if (!doit) {
1013 return true;
1014 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 mNotificationList.remove(i);
Joe Onorato46439ce2010-11-19 13:56:21 -08001016 cancelNotificationLocked(r, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 }
1018 if (canceledSomething) {
1019 updateLightsLocked();
1020 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001021 return canceledSomething;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 }
1023 }
1024
Andy Stadler110988c2010-12-03 14:29:16 -08001025 @Deprecated
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001026 public void cancelNotification(String pkg, int id) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07001027 cancelNotificationWithTag(pkg, null /* tag */, id);
1028 }
1029
1030 public void cancelNotificationWithTag(String pkg, String tag, int id) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001031 checkIncomingCall(pkg);
1032 // Don't allow client applications to cancel foreground service notis.
Fred Quintana6ecaff12009-09-25 14:23:13 -07001033 cancelNotification(pkg, tag, id, 0,
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001034 Binder.getCallingUid() == Process.SYSTEM_UID
Joe Onorato46439ce2010-11-19 13:56:21 -08001035 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 }
1037
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001038 public void cancelAllNotifications(String pkg) {
1039 checkIncomingCall(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001040
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001041 // Calling from user space, don't allow the canceling of actively
1042 // running foreground services.
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001043 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 }
1045
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001046 void checkIncomingCall(String pkg) {
1047 int uid = Binder.getCallingUid();
1048 if (uid == Process.SYSTEM_UID || uid == 0) {
1049 return;
1050 }
1051 try {
1052 ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
1053 pkg, 0);
Amith Yamasani742a6712011-05-04 14:49:28 -07001054 if (!UserId.isSameApp(ai.uid, uid)) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001055 throw new SecurityException("Calling uid " + uid + " gave package"
1056 + pkg + " which is owned by uid " + ai.uid);
1057 }
1058 } catch (PackageManager.NameNotFoundException e) {
1059 throw new SecurityException("Unknown package " + pkg);
1060 }
1061 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001062
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001063 void cancelAll() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 synchronized (mNotificationList) {
1065 final int N = mNotificationList.size();
1066 for (int i=N-1; i>=0; i--) {
1067 NotificationRecord r = mNotificationList.get(i);
1068
1069 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
1070 | Notification.FLAG_NO_CLEAR)) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 mNotificationList.remove(i);
Joe Onorato46439ce2010-11-19 13:56:21 -08001072 cancelNotificationLocked(r, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073 }
1074 }
1075
1076 updateLightsLocked();
1077 }
1078 }
1079
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 // lock on mNotificationList
1081 private void updateLightsLocked()
1082 {
The Android Open Source Project10592532009-03-18 17:39:46 -07001083 // handle notification lights
1084 if (mLedNotification == null) {
1085 // get next notification, if any
1086 int n = mLights.size();
1087 if (n > 0) {
1088 mLedNotification = mLights.get(n-1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 }
1090 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001091
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001092 // Don't flash while we are in a call or screen is on
1093 if (mLedNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001094 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07001095 } else {
Mike Lockwood670f9322010-01-20 12:13:36 -05001096 int ledARGB = mLedNotification.notification.ledARGB;
1097 int ledOnMS = mLedNotification.notification.ledOnMS;
1098 int ledOffMS = mLedNotification.notification.ledOffMS;
1099 if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
1100 ledARGB = mDefaultNotificationColor;
1101 ledOnMS = mDefaultNotificationLedOn;
1102 ledOffMS = mDefaultNotificationLedOff;
1103 }
1104 if (mNotificationPulseEnabled) {
1105 // pulse repeatedly
1106 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
1107 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05001108 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001109 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 }
1111
1112 // lock on mNotificationList
Fred Quintana6ecaff12009-09-25 14:23:13 -07001113 private int indexOfNotificationLocked(String pkg, String tag, int id)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 {
1115 ArrayList<NotificationRecord> list = mNotificationList;
1116 final int len = list.size();
1117 for (int i=0; i<len; i++) {
1118 NotificationRecord r = list.get(i);
Fred Quintana6ecaff12009-09-25 14:23:13 -07001119 if (tag == null) {
1120 if (r.tag != null) {
1121 continue;
1122 }
1123 } else {
1124 if (!tag.equals(r.tag)) {
1125 continue;
1126 }
1127 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128 if (r.id == id && r.pkg.equals(pkg)) {
1129 return i;
1130 }
1131 }
1132 return -1;
1133 }
1134
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001135 private void updateNotificationPulse() {
1136 synchronized (mNotificationList) {
1137 updateLightsLocked();
1138 }
1139 }
1140
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141 // ======================================================================
1142 @Override
1143 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1144 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1145 != PackageManager.PERMISSION_GRANTED) {
1146 pw.println("Permission Denial: can't dump NotificationManager from from pid="
1147 + Binder.getCallingPid()
1148 + ", uid=" + Binder.getCallingUid());
1149 return;
1150 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 pw.println("Current Notification Manager state:");
1153
1154 int N;
1155
1156 synchronized (mToastQueue) {
1157 N = mToastQueue.size();
1158 if (N > 0) {
1159 pw.println(" Toast Queue:");
1160 for (int i=0; i<N; i++) {
1161 mToastQueue.get(i).dump(pw, " ");
1162 }
1163 pw.println(" ");
1164 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001165
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 }
1167
1168 synchronized (mNotificationList) {
1169 N = mNotificationList.size();
1170 if (N > 0) {
1171 pw.println(" Notification List:");
1172 for (int i=0; i<N; i++) {
1173 mNotificationList.get(i).dump(pw, " ", mContext);
1174 }
1175 pw.println(" ");
1176 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001178 N = mLights.size();
1179 if (N > 0) {
1180 pw.println(" Lights List:");
1181 for (int i=0; i<N; i++) {
1182 mLights.get(i).dump(pw, " ", mContext);
1183 }
1184 pw.println(" ");
1185 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001186
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 pw.println(" mSoundNotification=" + mSoundNotification);
1188 pw.println(" mSound=" + mSound);
1189 pw.println(" mVibrateNotification=" + mVibrateNotification);
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001190 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
1191 pw.println(" mSystemReady=" + mSystemReady);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001192 }
1193 }
1194}