blob: e73814511224d65b4b63897f83fd2c2a3e01ff8d [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.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070031import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070035import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.content.pm.PackageManager;
37import android.content.pm.PackageManager.NameNotFoundException;
38import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070039import android.database.ContentObserver;
Mike Lockwoodc4308f02011-03-01 08:04:54 -080040import android.hardware.usb.UsbManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070041import android.media.AudioManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.Binder;
Andy Stadler110988c2010-12-03 14:29:16 -080044import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.os.IBinder;
47import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070048import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070049import android.os.RemoteException;
Mike Lockwooded760372009-07-09 07:07:27 -040050import android.os.SystemProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.Vibrator;
52import android.provider.Settings;
Daniel Sandlere96ffb12010-03-11 13:38:06 -050053import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070054import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.util.EventLog;
56import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -080057import android.util.Slog;
svetoslavganov75986cf2009-05-14 22:28:01 -070058import android.view.accessibility.AccessibilityEvent;
59import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.widget.Toast;
61
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import java.io.FileDescriptor;
63import java.io.PrintWriter;
64import java.util.ArrayList;
65import java.util.Arrays;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066
Daniel Sandlerd0a2f862010-08-03 15:29:31 -040067/** {@hide} */
68public class NotificationManagerService extends INotificationManager.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069{
70 private static final String TAG = "NotificationService";
71 private static final boolean DBG = false;
72
Joe Onoratobd73d012010-06-04 11:44:54 -070073 private static final int MAX_PACKAGE_NOTIFICATIONS = 50;
74
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 // message codes
76 private static final int MESSAGE_TIMEOUT = 2;
77
78 private static final int LONG_DELAY = 3500; // 3.5 seconds
79 private static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -080080
81 private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
83 private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
84
85 final Context mContext;
86 final IActivityManager mAm;
87 final IBinder mForegroundToken = new Binder();
88
89 private WorkerHandler mHandler;
Joe Onorato089de882010-04-12 08:18:45 -070090 private StatusBarManagerService mStatusBar;
Mike Lockwood3cb67a32009-11-27 14:25:58 -050091 private LightsService.Light mNotificationLight;
92 private LightsService.Light mAttentionLight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093
Mike Lockwood670f9322010-01-20 12:13:36 -050094 private int mDefaultNotificationColor;
95 private int mDefaultNotificationLedOn;
96 private int mDefaultNotificationLedOff;
97
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 private NotificationRecord mSoundNotification;
Jean-Michel Trivi211957f2010-03-26 18:19:33 -070099 private NotificationPlayer mSound;
Joe Onorato30275482009-07-08 17:09:14 -0700100 private boolean mSystemReady;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400101 private int mDisabledNotifications;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102
103 private NotificationRecord mVibrateNotification;
104 private Vibrator mVibrator = new Vibrator();
105
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500106 // for enabling and disabling notification pulse behavior
107 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500108 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500109 private boolean mNotificationPulseEnabled;
Mike Lockwood2117f6f2010-09-09 09:48:08 -0400110 // This is true if we have received a new notification while the screen is off
111 // (that is, if mLedNotification was set while the screen was off)
112 // This is reset to false when the screen is turned on.
113 private boolean mPendingPulseNotification;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500114
115 // for adb connected notifications
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700116 private boolean mAdbNotificationShown = false;
117 private Notification mAdbNotification;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800118
Fred Quintana6ecaff12009-09-25 14:23:13 -0700119 private final ArrayList<NotificationRecord> mNotificationList =
120 new ArrayList<NotificationRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
122 private ArrayList<ToastRecord> mToastQueue;
123
124 private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 private NotificationRecord mLedNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -0700126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 private static String idDebugString(Context baseContext, String packageName, int id) {
128 Context c = null;
129
130 if (packageName != null) {
131 try {
132 c = baseContext.createPackageContext(packageName, 0);
133 } catch (NameNotFoundException e) {
134 c = baseContext;
135 }
136 } else {
137 c = baseContext;
138 }
139
140 String pkg;
141 String type;
142 String name;
143
144 Resources r = c.getResources();
145 try {
146 return r.getResourceName(id);
147 } catch (Resources.NotFoundException e) {
148 return "<name unknown>";
149 }
150 }
151
152 private static final class NotificationRecord
153 {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700154 final String pkg;
155 final String tag;
156 final int id;
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700157 final int uid;
158 final int initialPid;
Daniel Sandlere40451a2011-02-03 14:51:35 -0500159 final int priority;
Fred Quintana6ecaff12009-09-25 14:23:13 -0700160 final Notification notification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 IBinder statusBarKey;
162
Daniel Sandlere40451a2011-02-03 14:51:35 -0500163 NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int priority,
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700164 Notification notification)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 {
166 this.pkg = pkg;
Fred Quintana6ecaff12009-09-25 14:23:13 -0700167 this.tag = tag;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 this.id = id;
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700169 this.uid = uid;
170 this.initialPid = initialPid;
Daniel Sandlere40451a2011-02-03 14:51:35 -0500171 this.priority = priority;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 this.notification = notification;
173 }
Fred Quintana6ecaff12009-09-25 14:23:13 -0700174
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 void dump(PrintWriter pw, String prefix, Context baseContext) {
176 pw.println(prefix + this);
177 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
178 + " / " + idDebugString(baseContext, this.pkg, notification.icon));
179 pw.println(prefix + " contentIntent=" + notification.contentIntent);
180 pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
181 pw.println(prefix + " tickerText=" + notification.tickerText);
182 pw.println(prefix + " contentView=" + notification.contentView);
183 pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults));
184 pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags));
185 pw.println(prefix + " sound=" + notification.sound);
186 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
187 pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB)
188 + " ledOnMS=" + notification.ledOnMS
189 + " ledOffMS=" + notification.ledOffMS);
190 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800191
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 @Override
193 public final String toString()
194 {
195 return "NotificationRecord{"
196 + Integer.toHexString(System.identityHashCode(this))
197 + " pkg=" + pkg
Fred Quintana6ecaff12009-09-25 14:23:13 -0700198 + " id=" + Integer.toHexString(id)
Daniel Sandlere40451a2011-02-03 14:51:35 -0500199 + " tag=" + tag
200 + " pri=" + priority
201 + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
203 }
204
205 private static final class ToastRecord
206 {
207 final int pid;
208 final String pkg;
209 final ITransientNotification callback;
210 int duration;
211
212 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
213 {
214 this.pid = pid;
215 this.pkg = pkg;
216 this.callback = callback;
217 this.duration = duration;
218 }
219
220 void update(int duration) {
221 this.duration = duration;
222 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 void dump(PrintWriter pw, String prefix) {
225 pw.println(prefix + this);
226 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800227
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 @Override
229 public final String toString()
230 {
231 return "ToastRecord{"
232 + Integer.toHexString(System.identityHashCode(this))
233 + " pkg=" + pkg
234 + " callback=" + callback
235 + " duration=" + duration;
236 }
237 }
238
Joe Onorato089de882010-04-12 08:18:45 -0700239 private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
240 = new StatusBarManagerService.NotificationCallbacks() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241
242 public void onSetDisabled(int status) {
243 synchronized (mNotificationList) {
244 mDisabledNotifications = status;
245 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
246 // cancel whatever's going on
247 long identity = Binder.clearCallingIdentity();
248 try {
249 mSound.stop();
250 }
251 finally {
252 Binder.restoreCallingIdentity(identity);
253 }
254
255 identity = Binder.clearCallingIdentity();
256 try {
257 mVibrator.cancel();
258 }
259 finally {
260 Binder.restoreCallingIdentity(identity);
261 }
262 }
263 }
264 }
265
266 public void onClearAll() {
267 cancelAll();
268 }
269
Fred Quintana6ecaff12009-09-25 14:23:13 -0700270 public void onNotificationClick(String pkg, String tag, int id) {
271 cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
Joe Onorato46439ce2010-11-19 13:56:21 -0800272 Notification.FLAG_FOREGROUND_SERVICE, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 }
274
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400275 public void onNotificationClear(String pkg, String tag, int id) {
Joe Onorato46439ce2010-11-19 13:56:21 -0800276 cancelNotification(pkg, tag, id, 0,
277 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
278 true);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400279 }
280
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 public void onPanelRevealed() {
282 synchronized (mNotificationList) {
283 // sound
284 mSoundNotification = null;
285 long identity = Binder.clearCallingIdentity();
286 try {
287 mSound.stop();
288 }
289 finally {
290 Binder.restoreCallingIdentity(identity);
291 }
292
293 // vibrate
294 mVibrateNotification = null;
295 identity = Binder.clearCallingIdentity();
296 try {
297 mVibrator.cancel();
298 }
299 finally {
300 Binder.restoreCallingIdentity(identity);
301 }
302
303 // light
304 mLights.clear();
305 mLedNotification = null;
306 updateLightsLocked();
307 }
308 }
Joe Onorato005847b2010-06-04 16:08:02 -0400309
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700310 public void onNotificationError(String pkg, String tag, int id,
311 int uid, int initialPid, String message) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400312 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
313 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
Joe Onorato46439ce2010-11-19 13:56:21 -0800314 cancelNotification(pkg, tag, id, 0, 0, false);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700315 long ident = Binder.clearCallingIdentity();
316 try {
317 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
318 "Bad notification posted from package " + pkg
319 + ": " + message);
320 } catch (RemoteException e) {
321 }
322 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400323 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 };
325
326 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
327 @Override
328 public void onReceive(Context context, Intent intent) {
329 String action = intent.getAction();
330
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800331 boolean queryRestart = false;
332
Mike Lockwood770126a2010-12-09 22:30:37 -0800333 if (action.equals(UsbManager.ACTION_USB_STATE)) {
Mike Lockwoodff2544c2010-06-28 09:17:50 -0400334 Bundle extras = intent.getExtras();
Mike Lockwood770126a2010-12-09 22:30:37 -0800335 boolean usbConnected = extras.getBoolean(UsbManager.USB_CONNECTED);
336 boolean adbEnabled = (UsbManager.USB_FUNCTION_ENABLED.equals(
337 extras.getString(UsbManager.USB_FUNCTION_ADB)));
Mike Lockwoodff2544c2010-06-28 09:17:50 -0400338 updateAdbNotification(usbConnected && adbEnabled);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800340 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800341 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800342 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800343 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800344 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800345 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800346 } else if (queryRestart) {
347 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800348 } else {
349 Uri uri = intent.getData();
350 if (uri == null) {
351 return;
352 }
353 String pkgName = uri.getSchemeSpecificPart();
354 if (pkgName == null) {
355 return;
356 }
357 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800359 if (pkgList != null && (pkgList.length > 0)) {
360 for (String pkgName : pkgList) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800361 cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800362 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500364 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
365 mScreenOn = true;
366 updateNotificationPulse();
367 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
368 mScreenOn = false;
369 updateNotificationPulse();
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500370 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
371 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK));
372 updateNotificationPulse();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 }
374 }
375 };
376
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700377 class SettingsObserver extends ContentObserver {
378 SettingsObserver(Handler handler) {
379 super(handler);
380 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800381
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700382 void observe() {
383 ContentResolver resolver = mContext.getContentResolver();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500384 resolver.registerContentObserver(Settings.System.getUriFor(
385 Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700386 update();
387 }
388
389 @Override public void onChange(boolean selfChange) {
390 update();
391 }
392
393 public void update() {
394 ContentResolver resolver = mContext.getContentResolver();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500395 boolean pulseEnabled = Settings.System.getInt(resolver,
396 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
397 if (mNotificationPulseEnabled != pulseEnabled) {
398 mNotificationPulseEnabled = pulseEnabled;
399 updateNotificationPulse();
400 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700401 }
402 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500403
Joe Onorato089de882010-04-12 08:18:45 -0700404 NotificationManagerService(Context context, StatusBarManagerService statusBar,
Mike Lockwood3a322132009-11-24 00:30:52 -0500405 LightsService lights)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 {
407 super();
408 mContext = context;
409 mAm = ActivityManagerNative.getDefault();
Jean-Michel Trivi211957f2010-03-26 18:19:33 -0700410 mSound = new NotificationPlayer(TAG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 mSound.setUsesWakeLock(context);
412 mToastQueue = new ArrayList<ToastRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 mHandler = new WorkerHandler();
San Mehat3ee13172010-02-04 20:54:43 -0800414
Joe Onorato089de882010-04-12 08:18:45 -0700415 mStatusBar = statusBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 statusBar.setNotificationCallbacks(mNotificationCallbacks);
417
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500418 mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
419 mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
420
Mike Lockwood670f9322010-01-20 12:13:36 -0500421 Resources resources = mContext.getResources();
422 mDefaultNotificationColor = resources.getColor(
423 com.android.internal.R.color.config_defaultNotificationColor);
424 mDefaultNotificationLedOn = resources.getInteger(
425 com.android.internal.R.integer.config_defaultNotificationLedOn);
426 mDefaultNotificationLedOff = resources.getInteger(
427 com.android.internal.R.integer.config_defaultNotificationLedOff);
428
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400429 // Don't start allowing notifications until the setup wizard has run once.
430 // After that, including subsequent boots, init with notifications turned on.
431 // This works on the first boot because the setup wizard will toggle this
432 // flag at least once and we'll go back to 0 after that.
433 if (0 == Settings.Secure.getInt(mContext.getContentResolver(),
434 Settings.Secure.DEVICE_PROVISIONED, 0)) {
435 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
436 }
437
Mike Lockwood35e16bf2010-11-30 19:53:36 -0500438 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 IntentFilter filter = new IntentFilter();
Mike Lockwood770126a2010-12-09 22:30:37 -0800440 filter.addAction(UsbManager.ACTION_USB_STATE);
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500441 filter.addAction(Intent.ACTION_SCREEN_ON);
442 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500443 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 mContext.registerReceiver(mIntentReceiver, filter);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800445 IntentFilter pkgFilter = new IntentFilter();
446 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
447 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
448 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
449 pkgFilter.addDataScheme("package");
450 mContext.registerReceiver(mIntentReceiver, pkgFilter);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800451 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800452 mContext.registerReceiver(mIntentReceiver, sdFilter);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800453
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500454 SettingsObserver observer = new SettingsObserver(mHandler);
455 observer.observe();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 }
457
Joe Onorato30275482009-07-08 17:09:14 -0700458 void systemReady() {
459 // no beeping until we're basically done booting
460 mSystemReady = true;
461 }
462
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 // Toasts
464 // ============================================================================
465 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
466 {
Daniel Sandlera7035902010-03-30 15:45:31 -0400467 if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468
469 if (pkg == null || callback == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800470 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 return ;
472 }
473
474 synchronized (mToastQueue) {
475 int callingPid = Binder.getCallingPid();
476 long callingId = Binder.clearCallingIdentity();
477 try {
478 ToastRecord record;
479 int index = indexOfToastLocked(pkg, callback);
480 // If it's already in the queue, we update it in place, we don't
481 // move it to the end of the queue.
482 if (index >= 0) {
483 record = mToastQueue.get(index);
484 record.update(duration);
485 } else {
486 record = new ToastRecord(callingPid, pkg, callback, duration);
487 mToastQueue.add(record);
488 index = mToastQueue.size() - 1;
489 keepProcessAliveLocked(callingPid);
490 }
491 // If it's at index 0, it's the current toast. It doesn't matter if it's
492 // new or just been updated. Call back and tell it to show itself.
493 // If the callback fails, this will remove it from the list, so don't
494 // assume that it's valid after this.
495 if (index == 0) {
496 showNextToastLocked();
497 }
498 } finally {
499 Binder.restoreCallingIdentity(callingId);
500 }
501 }
502 }
503
504 public void cancelToast(String pkg, ITransientNotification callback) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800505 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506
507 if (pkg == null || callback == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800508 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 return ;
510 }
511
512 synchronized (mToastQueue) {
513 long callingId = Binder.clearCallingIdentity();
514 try {
515 int index = indexOfToastLocked(pkg, callback);
516 if (index >= 0) {
517 cancelToastLocked(index);
518 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800519 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 }
521 } finally {
522 Binder.restoreCallingIdentity(callingId);
523 }
524 }
525 }
526
527 private void showNextToastLocked() {
528 ToastRecord record = mToastQueue.get(0);
529 while (record != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800530 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 try {
532 record.callback.show();
533 scheduleTimeoutLocked(record, false);
534 return;
535 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800536 Slog.w(TAG, "Object died trying to show notification " + record.callback
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 + " in package " + record.pkg);
538 // remove it from the list and let the process die
539 int index = mToastQueue.indexOf(record);
540 if (index >= 0) {
541 mToastQueue.remove(index);
542 }
543 keepProcessAliveLocked(record.pid);
544 if (mToastQueue.size() > 0) {
545 record = mToastQueue.get(0);
546 } else {
547 record = null;
548 }
549 }
550 }
551 }
552
553 private void cancelToastLocked(int index) {
554 ToastRecord record = mToastQueue.get(index);
555 try {
556 record.callback.hide();
557 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800558 Slog.w(TAG, "Object died trying to hide notification " + record.callback
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 + " in package " + record.pkg);
560 // don't worry about this, we're about to remove it from
561 // the list anyway
562 }
563 mToastQueue.remove(index);
564 keepProcessAliveLocked(record.pid);
565 if (mToastQueue.size() > 0) {
566 // Show the next one. If the callback fails, this will remove
567 // it from the list, so don't assume that the list hasn't changed
568 // after this point.
569 showNextToastLocked();
570 }
571 }
572
573 private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
574 {
575 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
576 long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
577 mHandler.removeCallbacksAndMessages(r);
578 mHandler.sendMessageDelayed(m, delay);
579 }
580
581 private void handleTimeout(ToastRecord record)
582 {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800583 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 synchronized (mToastQueue) {
585 int index = indexOfToastLocked(record.pkg, record.callback);
586 if (index >= 0) {
587 cancelToastLocked(index);
588 }
589 }
590 }
591
592 // lock on mToastQueue
593 private int indexOfToastLocked(String pkg, ITransientNotification callback)
594 {
595 IBinder cbak = callback.asBinder();
596 ArrayList<ToastRecord> list = mToastQueue;
597 int len = list.size();
598 for (int i=0; i<len; i++) {
599 ToastRecord r = list.get(i);
600 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
601 return i;
602 }
603 }
604 return -1;
605 }
606
607 // lock on mToastQueue
608 private void keepProcessAliveLocked(int pid)
609 {
610 int toastCount = 0; // toasts from this pid
611 ArrayList<ToastRecord> list = mToastQueue;
612 int N = list.size();
613 for (int i=0; i<N; i++) {
614 ToastRecord r = list.get(i);
615 if (r.pid == pid) {
616 toastCount++;
617 }
618 }
619 try {
620 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
621 } catch (RemoteException e) {
622 // Shouldn't happen.
623 }
624 }
625
626 private final class WorkerHandler extends Handler
627 {
628 @Override
629 public void handleMessage(Message msg)
630 {
631 switch (msg.what)
632 {
633 case MESSAGE_TIMEOUT:
634 handleTimeout((ToastRecord)msg.obj);
635 break;
636 }
637 }
638 }
639
640
641 // Notifications
642 // ============================================================================
Andy Stadler110988c2010-12-03 14:29:16 -0800643 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
645 {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700646 enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
647 }
648
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400649 public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
650 int[] idOut)
Fred Quintana6ecaff12009-09-25 14:23:13 -0700651 {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400652 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
653 tag, id, notification, idOut);
654 }
655
Daniel Sandlere40451a2011-02-03 14:51:35 -0500656 public void enqueueNotificationWithTagPriority(String pkg, String tag, int id, int priority,
657 Notification notification, int[] idOut)
658 {
659 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
660 tag, id, priority, notification, idOut);
661 }
662
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400663 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
664 // uid/pid of another application)
665 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
666 String tag, int id, Notification notification, int[] idOut)
667 {
Daniel Sandlere40451a2011-02-03 14:51:35 -0500668 enqueueNotificationInternal(pkg, callingUid, callingPid, tag, id,
669 ((notification.flags & Notification.FLAG_ONGOING_EVENT) != 0)
670 ? StatusBarNotification.PRIORITY_ONGOING
671 : StatusBarNotification.PRIORITY_NORMAL,
672 notification, idOut);
673 }
674 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
675 String tag, int id, int priority, Notification notification, int[] idOut)
676 {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700677 checkIncomingCall(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800678
Joe Onoratobd73d012010-06-04 11:44:54 -0700679 // Limit the number of notifications that any given package except the android
680 // package can enqueue. Prevents DOS attacks and deals with leaks.
681 if (!"android".equals(pkg)) {
682 synchronized (mNotificationList) {
683 int count = 0;
684 final int N = mNotificationList.size();
685 for (int i=0; i<N; i++) {
686 final NotificationRecord r = mNotificationList.get(i);
687 if (r.pkg.equals(pkg)) {
688 count++;
689 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
690 Slog.e(TAG, "Package has already posted " + count
691 + " notifications. Not showing more. package=" + pkg);
692 return;
693 }
694 }
695 }
696 }
697 }
698
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 // This conditional is a dirty hack to limit the logging done on
700 // behalf of the download manager without affecting other apps.
701 if (!pkg.equals("com.android.providers.downloads")
702 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800703 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, 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
717 synchronized (mNotificationList) {
Daniel Sandlere40451a2011-02-03 14:51:35 -0500718 NotificationRecord r = new NotificationRecord(pkg, tag, id,
719 callingUid, callingPid,
720 priority,
721 notification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 NotificationRecord old = null;
723
Fred Quintana6ecaff12009-09-25 14:23:13 -0700724 int index = indexOfNotificationLocked(pkg, tag, id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 if (index < 0) {
726 mNotificationList.add(r);
727 } else {
728 old = mNotificationList.remove(index);
729 mNotificationList.add(index, r);
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700730 // Make sure we don't lose the foreground service state.
731 if (old != null) {
732 notification.flags |=
733 old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
734 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800736
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700737 // Ensure if this is a foreground service that the proper additional
738 // flags are set.
739 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
740 notification.flags |= Notification.FLAG_ONGOING_EVENT
741 | Notification.FLAG_NO_CLEAR;
742 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800743
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 if (notification.icon != 0) {
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700745 StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
746 r.uid, r.initialPid, notification);
Daniel Sandlere40451a2011-02-03 14:51:35 -0500747 n.priority = r.priority;
748
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 if (old != null && old.statusBarKey != null) {
750 r.statusBarKey = old.statusBarKey;
751 long identity = Binder.clearCallingIdentity();
752 try {
Joe Onorato18e69df2010-05-17 22:26:12 -0700753 mStatusBar.updateNotification(r.statusBarKey, n);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 }
755 finally {
756 Binder.restoreCallingIdentity(identity);
757 }
758 } else {
759 long identity = Binder.clearCallingIdentity();
760 try {
Joe Onorato18e69df2010-05-17 22:26:12 -0700761 r.statusBarKey = mStatusBar.addNotification(n);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500762 mAttentionLight.pulse();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 }
764 finally {
765 Binder.restoreCallingIdentity(identity);
766 }
767 }
Joe Onorato30275482009-07-08 17:09:14 -0700768 sendAccessibilityEvent(notification, pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 } else {
Daniel Sandlere40451a2011-02-03 14:51:35 -0500770 Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 if (old != null && old.statusBarKey != null) {
772 long identity = Binder.clearCallingIdentity();
773 try {
Joe Onorato0cbda992010-05-02 16:28:15 -0700774 mStatusBar.removeNotification(old.statusBarKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 }
776 finally {
777 Binder.restoreCallingIdentity(identity);
778 }
779 }
780 }
781
782 // If we're not supposed to beep, vibrate, etc. then don't.
783 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
784 && (!(old != null
Joe Onorato30275482009-07-08 17:09:14 -0700785 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
786 && mSystemReady) {
Eric Laurent524dc042009-11-27 05:07:55 -0800787
788 final AudioManager audioManager = (AudioManager) mContext
789 .getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 // sound
791 final boolean useDefaultSound =
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800792 (notification.defaults & Notification.DEFAULT_SOUND) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 if (useDefaultSound || notification.sound != null) {
794 Uri uri;
795 if (useDefaultSound) {
796 uri = Settings.System.DEFAULT_NOTIFICATION_URI;
797 } else {
798 uri = notification.sound;
799 }
800 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
801 int audioStreamType;
802 if (notification.audioStreamType >= 0) {
803 audioStreamType = notification.audioStreamType;
804 } else {
805 audioStreamType = DEFAULT_STREAM_TYPE;
806 }
807 mSoundNotification = r;
Eric Laurent524dc042009-11-27 05:07:55 -0800808 // do not play notifications if stream volume is 0
809 // (typically because ringer mode is silent).
810 if (audioManager.getStreamVolume(audioStreamType) != 0) {
811 long identity = Binder.clearCallingIdentity();
812 try {
813 mSound.play(mContext, uri, looping, audioStreamType);
814 }
815 finally {
816 Binder.restoreCallingIdentity(identity);
817 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 }
819 }
820
821 // vibrate
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 final boolean useDefaultVibrate =
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800823 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 if ((useDefaultVibrate || notification.vibrate != null)
825 && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) {
826 mVibrateNotification = r;
827
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800828 mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 : notification.vibrate,
830 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
831 }
832 }
833
834 // this option doesn't shut off the lights
835
836 // light
837 // the most recent thing gets the light
838 mLights.remove(old);
839 if (mLedNotification == old) {
840 mLedNotification = null;
841 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800842 //Slog.i(TAG, "notification.lights="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
844 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
845 mLights.add(r);
846 updateLightsLocked();
847 } else {
848 if (old != null
849 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
850 updateLightsLocked();
851 }
852 }
853 }
854
855 idOut[0] = id;
856 }
857
Joe Onorato30275482009-07-08 17:09:14 -0700858 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700859 AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
860 if (!manager.isEnabled()) {
861 return;
862 }
863
864 AccessibilityEvent event =
865 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
866 event.setPackageName(packageName);
867 event.setClassName(Notification.class.getName());
868 event.setParcelableData(notification);
869 CharSequence tickerText = notification.tickerText;
870 if (!TextUtils.isEmpty(tickerText)) {
871 event.getText().add(tickerText);
872 }
873
874 manager.sendAccessibilityEvent(event);
875 }
876
Joe Onorato46439ce2010-11-19 13:56:21 -0800877 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
878 // tell the app
879 if (sendDelete) {
880 if (r.notification.deleteIntent != null) {
881 try {
882 r.notification.deleteIntent.send();
883 } catch (PendingIntent.CanceledException ex) {
884 // do nothing - there's no relevant way to recover, and
885 // no reason to let this propagate
886 Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
887 }
888 }
889 }
890
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 // status bar
892 if (r.notification.icon != 0) {
893 long identity = Binder.clearCallingIdentity();
894 try {
Joe Onorato0cbda992010-05-02 16:28:15 -0700895 mStatusBar.removeNotification(r.statusBarKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 }
897 finally {
898 Binder.restoreCallingIdentity(identity);
899 }
900 r.statusBarKey = null;
901 }
902
903 // sound
904 if (mSoundNotification == r) {
905 mSoundNotification = null;
906 long identity = Binder.clearCallingIdentity();
907 try {
908 mSound.stop();
909 }
910 finally {
911 Binder.restoreCallingIdentity(identity);
912 }
913 }
914
915 // vibrate
916 if (mVibrateNotification == r) {
917 mVibrateNotification = null;
918 long identity = Binder.clearCallingIdentity();
919 try {
920 mVibrator.cancel();
921 }
922 finally {
923 Binder.restoreCallingIdentity(identity);
924 }
925 }
926
927 // light
928 mLights.remove(r);
929 if (mLedNotification == r) {
930 mLedNotification = null;
931 }
932 }
933
934 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700935 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800936 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 */
Fred Quintana6ecaff12009-09-25 14:23:13 -0700938 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
Joe Onorato46439ce2010-11-19 13:56:21 -0800939 int mustNotHaveFlags, boolean sendDelete) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800940 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941
942 synchronized (mNotificationList) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700943 int index = indexOfNotificationLocked(pkg, tag, id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 if (index >= 0) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700945 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800946
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
948 return;
949 }
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700950 if ((r.notification.flags & mustNotHaveFlags) != 0) {
951 return;
952 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800953
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 mNotificationList.remove(index);
955
Joe Onorato46439ce2010-11-19 13:56:21 -0800956 cancelNotificationLocked(r, sendDelete);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 updateLightsLocked();
958 }
959 }
960 }
961
962 /**
963 * Cancels all notifications from a given package that have all of the
964 * {@code mustHaveFlags}.
965 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800966 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
967 int mustNotHaveFlags, boolean doit) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800968 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969
970 synchronized (mNotificationList) {
971 final int N = mNotificationList.size();
972 boolean canceledSomething = false;
973 for (int i = N-1; i >= 0; --i) {
974 NotificationRecord r = mNotificationList.get(i);
975 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
976 continue;
977 }
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700978 if ((r.notification.flags & mustNotHaveFlags) != 0) {
979 continue;
980 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 if (!r.pkg.equals(pkg)) {
982 continue;
983 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800984 canceledSomething = true;
985 if (!doit) {
986 return true;
987 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 mNotificationList.remove(i);
Joe Onorato46439ce2010-11-19 13:56:21 -0800989 cancelNotificationLocked(r, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 }
991 if (canceledSomething) {
992 updateLightsLocked();
993 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800994 return canceledSomething;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 }
996 }
997
Andy Stadler110988c2010-12-03 14:29:16 -0800998 @Deprecated
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700999 public void cancelNotification(String pkg, int id) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07001000 cancelNotificationWithTag(pkg, null /* tag */, id);
1001 }
1002
1003 public void cancelNotificationWithTag(String pkg, String tag, int id) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001004 checkIncomingCall(pkg);
1005 // Don't allow client applications to cancel foreground service notis.
Fred Quintana6ecaff12009-09-25 14:23:13 -07001006 cancelNotification(pkg, tag, id, 0,
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001007 Binder.getCallingUid() == Process.SYSTEM_UID
Joe Onorato46439ce2010-11-19 13:56:21 -08001008 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 }
1010
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001011 public void cancelAllNotifications(String pkg) {
1012 checkIncomingCall(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001013
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001014 // Calling from user space, don't allow the canceling of actively
1015 // running foreground services.
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001016 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 }
1018
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001019 void checkIncomingCall(String pkg) {
1020 int uid = Binder.getCallingUid();
1021 if (uid == Process.SYSTEM_UID || uid == 0) {
1022 return;
1023 }
1024 try {
1025 ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
1026 pkg, 0);
1027 if (ai.uid != uid) {
1028 throw new SecurityException("Calling uid " + uid + " gave package"
1029 + pkg + " which is owned by uid " + ai.uid);
1030 }
1031 } catch (PackageManager.NameNotFoundException e) {
1032 throw new SecurityException("Unknown package " + pkg);
1033 }
1034 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001035
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001036 void cancelAll() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 synchronized (mNotificationList) {
1038 final int N = mNotificationList.size();
1039 for (int i=N-1; i>=0; i--) {
1040 NotificationRecord r = mNotificationList.get(i);
1041
1042 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
1043 | Notification.FLAG_NO_CLEAR)) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 mNotificationList.remove(i);
Joe Onorato46439ce2010-11-19 13:56:21 -08001045 cancelNotificationLocked(r, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046 }
1047 }
1048
1049 updateLightsLocked();
1050 }
1051 }
1052
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 // lock on mNotificationList
1054 private void updateLightsLocked()
1055 {
Mike Lockwood2117f6f2010-09-09 09:48:08 -04001056 // clear pending pulse notification if screen is on
1057 if (mScreenOn || mLedNotification == null) {
1058 mPendingPulseNotification = false;
1059 }
1060
The Android Open Source Project10592532009-03-18 17:39:46 -07001061 // handle notification lights
1062 if (mLedNotification == null) {
1063 // get next notification, if any
1064 int n = mLights.size();
1065 if (n > 0) {
1066 mLedNotification = mLights.get(n-1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 }
Mike Lockwood2117f6f2010-09-09 09:48:08 -04001068 if (mLedNotification != null && !mScreenOn) {
1069 mPendingPulseNotification = true;
1070 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001072
1073 // we only flash if screen is off and persistent pulsing is enabled
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001074 // and we are not currently in a call
Mike Lockwood2117f6f2010-09-09 09:48:08 -04001075 if (!mPendingPulseNotification || mScreenOn || mInCall) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001076 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07001077 } else {
Mike Lockwood670f9322010-01-20 12:13:36 -05001078 int ledARGB = mLedNotification.notification.ledARGB;
1079 int ledOnMS = mLedNotification.notification.ledOnMS;
1080 int ledOffMS = mLedNotification.notification.ledOffMS;
1081 if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
1082 ledARGB = mDefaultNotificationColor;
1083 ledOnMS = mDefaultNotificationLedOn;
1084 ledOffMS = mDefaultNotificationLedOff;
1085 }
1086 if (mNotificationPulseEnabled) {
1087 // pulse repeatedly
1088 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
1089 ledOnMS, ledOffMS);
1090 } else {
1091 // pulse only once
1092 mNotificationLight.pulse(ledARGB, ledOnMS);
1093 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001094 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001095 }
1096
1097 // lock on mNotificationList
Fred Quintana6ecaff12009-09-25 14:23:13 -07001098 private int indexOfNotificationLocked(String pkg, String tag, int id)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 {
1100 ArrayList<NotificationRecord> list = mNotificationList;
1101 final int len = list.size();
1102 for (int i=0; i<len; i++) {
1103 NotificationRecord r = list.get(i);
Fred Quintana6ecaff12009-09-25 14:23:13 -07001104 if (tag == null) {
1105 if (r.tag != null) {
1106 continue;
1107 }
1108 } else {
1109 if (!tag.equals(r.tag)) {
1110 continue;
1111 }
1112 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 if (r.id == id && r.pkg.equals(pkg)) {
1114 return i;
1115 }
1116 }
1117 return -1;
1118 }
1119
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001120 // This is here instead of StatusBarPolicy because it is an important
1121 // security feature that we don't want people customizing the platform
1122 // to accidentally lose.
Mike Lockwoodff2544c2010-06-28 09:17:50 -04001123 private void updateAdbNotification(boolean adbEnabled) {
1124 if (adbEnabled) {
Mike Lockwooded760372009-07-09 07:07:27 -04001125 if ("0".equals(SystemProperties.get("persist.adb.notify"))) {
1126 return;
1127 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001128 if (!mAdbNotificationShown) {
1129 NotificationManager notificationManager = (NotificationManager) mContext
1130 .getSystemService(Context.NOTIFICATION_SERVICE);
1131 if (notificationManager != null) {
1132 Resources r = mContext.getResources();
1133 CharSequence title = r.getText(
1134 com.android.internal.R.string.adb_active_notification_title);
1135 CharSequence message = r.getText(
1136 com.android.internal.R.string.adb_active_notification_message);
1137
1138 if (mAdbNotification == null) {
1139 mAdbNotification = new Notification();
Daniel Sandler39576c82010-03-25 16:02:33 -04001140 mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb;
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001141 mAdbNotification.when = 0;
1142 mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;
1143 mAdbNotification.tickerText = title;
Daniel Sandler39576c82010-03-25 16:02:33 -04001144 mAdbNotification.defaults = 0; // please be quiet
1145 mAdbNotification.sound = null;
1146 mAdbNotification.vibrate = null;
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001147 }
1148
1149 Intent intent = new Intent(
1150 Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
1151 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1152 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1153 // Note: we are hard-coding the component because this is
1154 // an important security UI that we don't want anyone
1155 // intercepting.
1156 intent.setComponent(new ComponentName("com.android.settings",
1157 "com.android.settings.DevelopmentSettings"));
1158 PendingIntent pi = PendingIntent.getActivity(mContext, 0,
1159 intent, 0);
1160
1161 mAdbNotification.setLatestEventInfo(mContext, title, message, pi);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001162
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001163 mAdbNotificationShown = true;
1164 notificationManager.notify(
1165 com.android.internal.R.string.adb_active_notification_title,
1166 mAdbNotification);
1167 }
1168 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001169
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001170 } else if (mAdbNotificationShown) {
1171 NotificationManager notificationManager = (NotificationManager) mContext
1172 .getSystemService(Context.NOTIFICATION_SERVICE);
1173 if (notificationManager != null) {
1174 mAdbNotificationShown = false;
1175 notificationManager.cancel(
1176 com.android.internal.R.string.adb_active_notification_title);
1177 }
1178 }
1179 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001180
1181 private void updateNotificationPulse() {
1182 synchronized (mNotificationList) {
1183 updateLightsLocked();
1184 }
1185 }
1186
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 // ======================================================================
1188 @Override
1189 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1190 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1191 != PackageManager.PERMISSION_GRANTED) {
1192 pw.println("Permission Denial: can't dump NotificationManager from from pid="
1193 + Binder.getCallingPid()
1194 + ", uid=" + Binder.getCallingUid());
1195 return;
1196 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001197
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001198 pw.println("Current Notification Manager state:");
1199
1200 int N;
1201
1202 synchronized (mToastQueue) {
1203 N = mToastQueue.size();
1204 if (N > 0) {
1205 pw.println(" Toast Queue:");
1206 for (int i=0; i<N; i++) {
1207 mToastQueue.get(i).dump(pw, " ");
1208 }
1209 pw.println(" ");
1210 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001211
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 }
1213
1214 synchronized (mNotificationList) {
1215 N = mNotificationList.size();
1216 if (N > 0) {
1217 pw.println(" Notification List:");
1218 for (int i=0; i<N; i++) {
1219 mNotificationList.get(i).dump(pw, " ", mContext);
1220 }
1221 pw.println(" ");
1222 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224 N = mLights.size();
1225 if (N > 0) {
1226 pw.println(" Lights List:");
1227 for (int i=0; i<N; i++) {
1228 mLights.get(i).dump(pw, " ", mContext);
1229 }
1230 pw.println(" ");
1231 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001232
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 pw.println(" mSoundNotification=" + mSoundNotification);
1234 pw.println(" mSound=" + mSound);
1235 pw.println(" mVibrateNotification=" + mVibrateNotification);
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001236 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
1237 pw.println(" mSystemReady=" + mSystemReady);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 }
1239 }
1240}