blob: 436a60e377f5f9eb1265a0666f94caee13a09ac5 [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
svetoslavganov75986cf2009-05-14 22:28:01 -070019import com.android.server.status.IconData;
20import com.android.server.status.NotificationData;
21import com.android.server.status.StatusBarService;
22
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.app.ActivityManagerNative;
24import android.app.IActivityManager;
25import android.app.INotificationManager;
26import android.app.ITransientNotification;
27import android.app.Notification;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070028import android.app.NotificationManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.app.PendingIntent;
30import android.app.StatusBarManager;
31import android.content.BroadcastReceiver;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070032import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070033import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.Context;
35import android.content.Intent;
36import android.content.IntentFilter;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070037import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.content.pm.PackageManager;
39import android.content.pm.PackageManager.NameNotFoundException;
40import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070041import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.media.AsyncPlayer;
svetoslavganov75986cf2009-05-14 22:28:01 -070043import android.media.AudioManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.net.Uri;
45import android.os.BatteryManager;
46import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.os.IBinder;
49import android.os.Message;
50import android.os.Power;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070051import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070052import android.os.RemoteException;
Mike Lockwooded760372009-07-09 07:07:27 -040053import android.os.SystemProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.os.Vibrator;
55import android.provider.Settings;
svetoslavganov75986cf2009-05-14 22:28:01 -070056import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.util.EventLog;
58import android.util.Log;
svetoslavganov75986cf2009-05-14 22:28:01 -070059import android.view.accessibility.AccessibilityEvent;
60import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.widget.Toast;
62
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import java.io.FileDescriptor;
64import java.io.PrintWriter;
65import java.util.ArrayList;
66import java.util.Arrays;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067
68class NotificationManagerService extends INotificationManager.Stub
69{
70 private static final String TAG = "NotificationService";
71 private static final boolean DBG = false;
72
73 // 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;
82
83 final Context mContext;
84 final IActivityManager mAm;
85 final IBinder mForegroundToken = new Binder();
86
87 private WorkerHandler mHandler;
88 private StatusBarService mStatusBarService;
Mike Lockwood3a322132009-11-24 00:30:52 -050089 private LightsService mLightsService;
Mike Lockwood3cb67a32009-11-27 14:25:58 -050090 private LightsService.Light mBatteryLight;
91 private LightsService.Light mNotificationLight;
92 private LightsService.Light mAttentionLight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093
94 private NotificationRecord mSoundNotification;
95 private AsyncPlayer mSound;
Joe Onorato30275482009-07-08 17:09:14 -070096 private boolean mSystemReady;
Joe Onorato39f5b6a2009-07-23 12:29:19 -040097 private int mDisabledNotifications;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098
99 private NotificationRecord mVibrateNotification;
100 private Vibrator mVibrator = new Vibrator();
101
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500102 // for enabling and disabling notification pulse behavior
103 private boolean mScreenOn = true;
104 private boolean mNotificationPulseEnabled;
105
106 // for adb connected notifications
Mike Lockwoodea8b7d52009-08-04 17:03:15 -0400107 private boolean mUsbConnected;
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700108 private boolean mAdbEnabled = false;
109 private boolean mAdbNotificationShown = false;
110 private Notification mAdbNotification;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800111
Fred Quintana6ecaff12009-09-25 14:23:13 -0700112 private final ArrayList<NotificationRecord> mNotificationList =
113 new ArrayList<NotificationRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114
115 private ArrayList<ToastRecord> mToastQueue;
116
117 private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
118
119 private boolean mBatteryCharging;
120 private boolean mBatteryLow;
121 private boolean mBatteryFull;
122 private NotificationRecord mLedNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -0700123
The Android Open Source Project10592532009-03-18 17:39:46 -0700124 private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on
125 private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on
126 private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on
127 private static final int BATTERY_BLINK_ON = 125;
128 private static final int BATTERY_BLINK_OFF = 2875;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 private static String idDebugString(Context baseContext, String packageName, int id) {
131 Context c = null;
132
133 if (packageName != null) {
134 try {
135 c = baseContext.createPackageContext(packageName, 0);
136 } catch (NameNotFoundException e) {
137 c = baseContext;
138 }
139 } else {
140 c = baseContext;
141 }
142
143 String pkg;
144 String type;
145 String name;
146
147 Resources r = c.getResources();
148 try {
149 return r.getResourceName(id);
150 } catch (Resources.NotFoundException e) {
151 return "<name unknown>";
152 }
153 }
154
155 private static final class NotificationRecord
156 {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700157 final String pkg;
158 final String tag;
159 final int id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 ITransientNotification callback;
161 int duration;
Fred Quintana6ecaff12009-09-25 14:23:13 -0700162 final Notification notification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 IBinder statusBarKey;
164
Fred Quintana6ecaff12009-09-25 14:23:13 -0700165 NotificationRecord(String pkg, String tag, int id, Notification notification)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 {
167 this.pkg = pkg;
Fred Quintana6ecaff12009-09-25 14:23:13 -0700168 this.tag = tag;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 this.id = id;
170 this.notification = notification;
171 }
Fred Quintana6ecaff12009-09-25 14:23:13 -0700172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 void dump(PrintWriter pw, String prefix, Context baseContext) {
174 pw.println(prefix + this);
175 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
176 + " / " + idDebugString(baseContext, this.pkg, notification.icon));
177 pw.println(prefix + " contentIntent=" + notification.contentIntent);
178 pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
179 pw.println(prefix + " tickerText=" + notification.tickerText);
180 pw.println(prefix + " contentView=" + notification.contentView);
181 pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults));
182 pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags));
183 pw.println(prefix + " sound=" + notification.sound);
184 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
185 pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB)
186 + " ledOnMS=" + notification.ledOnMS
187 + " ledOffMS=" + notification.ledOffMS);
188 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800189
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 @Override
191 public final String toString()
192 {
193 return "NotificationRecord{"
194 + Integer.toHexString(System.identityHashCode(this))
195 + " pkg=" + pkg
Fred Quintana6ecaff12009-09-25 14:23:13 -0700196 + " id=" + Integer.toHexString(id)
197 + " tag=" + tag + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 }
199 }
200
201 private static final class ToastRecord
202 {
203 final int pid;
204 final String pkg;
205 final ITransientNotification callback;
206 int duration;
207
208 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
209 {
210 this.pid = pid;
211 this.pkg = pkg;
212 this.callback = callback;
213 this.duration = duration;
214 }
215
216 void update(int duration) {
217 this.duration = duration;
218 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800219
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 void dump(PrintWriter pw, String prefix) {
221 pw.println(prefix + this);
222 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 @Override
225 public final String toString()
226 {
227 return "ToastRecord{"
228 + Integer.toHexString(System.identityHashCode(this))
229 + " pkg=" + pkg
230 + " callback=" + callback
231 + " duration=" + duration;
232 }
233 }
234
235 private StatusBarService.NotificationCallbacks mNotificationCallbacks
236 = new StatusBarService.NotificationCallbacks() {
237
238 public void onSetDisabled(int status) {
239 synchronized (mNotificationList) {
240 mDisabledNotifications = status;
241 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
242 // cancel whatever's going on
243 long identity = Binder.clearCallingIdentity();
244 try {
245 mSound.stop();
246 }
247 finally {
248 Binder.restoreCallingIdentity(identity);
249 }
250
251 identity = Binder.clearCallingIdentity();
252 try {
253 mVibrator.cancel();
254 }
255 finally {
256 Binder.restoreCallingIdentity(identity);
257 }
258 }
259 }
260 }
261
262 public void onClearAll() {
263 cancelAll();
264 }
265
Fred Quintana6ecaff12009-09-25 14:23:13 -0700266 public void onNotificationClick(String pkg, String tag, int id) {
267 cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700268 Notification.FLAG_FOREGROUND_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 }
270
271 public void onPanelRevealed() {
272 synchronized (mNotificationList) {
273 // sound
274 mSoundNotification = null;
275 long identity = Binder.clearCallingIdentity();
276 try {
277 mSound.stop();
278 }
279 finally {
280 Binder.restoreCallingIdentity(identity);
281 }
282
283 // vibrate
284 mVibrateNotification = null;
285 identity = Binder.clearCallingIdentity();
286 try {
287 mVibrator.cancel();
288 }
289 finally {
290 Binder.restoreCallingIdentity(identity);
291 }
292
293 // light
294 mLights.clear();
295 mLedNotification = null;
296 updateLightsLocked();
297 }
298 }
299 };
300
301 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
302 @Override
303 public void onReceive(Context context, Intent intent) {
304 String action = intent.getAction();
305
306 if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
307 boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0);
308 int level = intent.getIntExtra("level", -1);
309 boolean batteryLow = (level >= 0 && level <= Power.LOW_BATTERY_THRESHOLD);
310 int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
311 boolean batteryFull = (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90);
312
313 if (batteryCharging != mBatteryCharging ||
314 batteryLow != mBatteryLow ||
315 batteryFull != mBatteryFull) {
316 mBatteryCharging = batteryCharging;
317 mBatteryLow = batteryLow;
318 mBatteryFull = batteryFull;
319 updateLights();
320 }
Mike Lockwoodea8b7d52009-08-04 17:03:15 -0400321 } else if (action.equals(Intent.ACTION_UMS_CONNECTED)) {
322 mUsbConnected = true;
323 updateAdbNotification();
324 } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
325 mUsbConnected = false;
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700326 updateAdbNotification();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
328 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
329 Uri uri = intent.getData();
330 if (uri == null) {
331 return;
332 }
333 String pkgName = uri.getSchemeSpecificPart();
334 if (pkgName == null) {
335 return;
336 }
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700337 cancelAllNotificationsInt(pkgName, 0, 0);
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500338 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
339 mScreenOn = true;
340 updateNotificationPulse();
341 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
342 mScreenOn = false;
343 updateNotificationPulse();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 }
345 }
346 };
347
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700348 class SettingsObserver extends ContentObserver {
349 SettingsObserver(Handler handler) {
350 super(handler);
351 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800352
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700353 void observe() {
354 ContentResolver resolver = mContext.getContentResolver();
355 resolver.registerContentObserver(Settings.Secure.getUriFor(
356 Settings.Secure.ADB_ENABLED), false, this);
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500357 resolver.registerContentObserver(Settings.System.getUriFor(
358 Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700359 update();
360 }
361
362 @Override public void onChange(boolean selfChange) {
363 update();
364 }
365
366 public void update() {
367 ContentResolver resolver = mContext.getContentResolver();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500368 boolean adbEnabled = Settings.Secure.getInt(resolver,
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700369 Settings.Secure.ADB_ENABLED, 0) != 0;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500370 if (mAdbEnabled != adbEnabled) {
371 mAdbEnabled = adbEnabled;
372 updateAdbNotification();
373 }
374 boolean pulseEnabled = Settings.System.getInt(resolver,
375 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
376 if (mNotificationPulseEnabled != pulseEnabled) {
377 mNotificationPulseEnabled = pulseEnabled;
378 updateNotificationPulse();
379 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700380 }
381 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500382
The Android Open Source Project10592532009-03-18 17:39:46 -0700383 NotificationManagerService(Context context, StatusBarService statusBar,
Mike Lockwood3a322132009-11-24 00:30:52 -0500384 LightsService lights)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 {
386 super();
387 mContext = context;
Mike Lockwood3a322132009-11-24 00:30:52 -0500388 mLightsService = lights;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 mAm = ActivityManagerNative.getDefault();
390 mSound = new AsyncPlayer(TAG);
391 mSound.setUsesWakeLock(context);
392 mToastQueue = new ArrayList<ToastRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 mHandler = new WorkerHandler();
394 mStatusBarService = statusBar;
395 statusBar.setNotificationCallbacks(mNotificationCallbacks);
396
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500397 mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
398 mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
399 mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
400
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400401 // Don't start allowing notifications until the setup wizard has run once.
402 // After that, including subsequent boots, init with notifications turned on.
403 // This works on the first boot because the setup wizard will toggle this
404 // flag at least once and we'll go back to 0 after that.
405 if (0 == Settings.Secure.getInt(mContext.getContentResolver(),
406 Settings.Secure.DEVICE_PROVISIONED, 0)) {
407 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
408 }
409
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 // register for battery changed notifications
411 IntentFilter filter = new IntentFilter();
412 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
Mike Lockwoodea8b7d52009-08-04 17:03:15 -0400413 filter.addAction(Intent.ACTION_UMS_CONNECTED);
414 filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
416 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500417 filter.addAction(Intent.ACTION_SCREEN_ON);
418 filter.addAction(Intent.ACTION_SCREEN_OFF);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 mContext.registerReceiver(mIntentReceiver, filter);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800420
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500421 SettingsObserver observer = new SettingsObserver(mHandler);
422 observer.observe();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 }
424
Joe Onorato30275482009-07-08 17:09:14 -0700425 void systemReady() {
426 // no beeping until we're basically done booting
427 mSystemReady = true;
428 }
429
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 // Toasts
431 // ============================================================================
432 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
433 {
434 Log.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);
435
436 if (pkg == null || callback == null) {
437 Log.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
438 return ;
439 }
440
441 synchronized (mToastQueue) {
442 int callingPid = Binder.getCallingPid();
443 long callingId = Binder.clearCallingIdentity();
444 try {
445 ToastRecord record;
446 int index = indexOfToastLocked(pkg, callback);
447 // If it's already in the queue, we update it in place, we don't
448 // move it to the end of the queue.
449 if (index >= 0) {
450 record = mToastQueue.get(index);
451 record.update(duration);
452 } else {
453 record = new ToastRecord(callingPid, pkg, callback, duration);
454 mToastQueue.add(record);
455 index = mToastQueue.size() - 1;
456 keepProcessAliveLocked(callingPid);
457 }
458 // If it's at index 0, it's the current toast. It doesn't matter if it's
459 // new or just been updated. Call back and tell it to show itself.
460 // If the callback fails, this will remove it from the list, so don't
461 // assume that it's valid after this.
462 if (index == 0) {
463 showNextToastLocked();
464 }
465 } finally {
466 Binder.restoreCallingIdentity(callingId);
467 }
468 }
469 }
470
471 public void cancelToast(String pkg, ITransientNotification callback) {
472 Log.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
473
474 if (pkg == null || callback == null) {
475 Log.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
476 return ;
477 }
478
479 synchronized (mToastQueue) {
480 long callingId = Binder.clearCallingIdentity();
481 try {
482 int index = indexOfToastLocked(pkg, callback);
483 if (index >= 0) {
484 cancelToastLocked(index);
485 } else {
486 Log.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback);
487 }
488 } finally {
489 Binder.restoreCallingIdentity(callingId);
490 }
491 }
492 }
493
494 private void showNextToastLocked() {
495 ToastRecord record = mToastQueue.get(0);
496 while (record != null) {
497 if (DBG) Log.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
498 try {
499 record.callback.show();
500 scheduleTimeoutLocked(record, false);
501 return;
502 } catch (RemoteException e) {
503 Log.w(TAG, "Object died trying to show notification " + record.callback
504 + " in package " + record.pkg);
505 // remove it from the list and let the process die
506 int index = mToastQueue.indexOf(record);
507 if (index >= 0) {
508 mToastQueue.remove(index);
509 }
510 keepProcessAliveLocked(record.pid);
511 if (mToastQueue.size() > 0) {
512 record = mToastQueue.get(0);
513 } else {
514 record = null;
515 }
516 }
517 }
518 }
519
520 private void cancelToastLocked(int index) {
521 ToastRecord record = mToastQueue.get(index);
522 try {
523 record.callback.hide();
524 } catch (RemoteException e) {
525 Log.w(TAG, "Object died trying to hide notification " + record.callback
526 + " in package " + record.pkg);
527 // don't worry about this, we're about to remove it from
528 // the list anyway
529 }
530 mToastQueue.remove(index);
531 keepProcessAliveLocked(record.pid);
532 if (mToastQueue.size() > 0) {
533 // Show the next one. If the callback fails, this will remove
534 // it from the list, so don't assume that the list hasn't changed
535 // after this point.
536 showNextToastLocked();
537 }
538 }
539
540 private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
541 {
542 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
543 long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
544 mHandler.removeCallbacksAndMessages(r);
545 mHandler.sendMessageDelayed(m, delay);
546 }
547
548 private void handleTimeout(ToastRecord record)
549 {
550 if (DBG) Log.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
551 synchronized (mToastQueue) {
552 int index = indexOfToastLocked(record.pkg, record.callback);
553 if (index >= 0) {
554 cancelToastLocked(index);
555 }
556 }
557 }
558
559 // lock on mToastQueue
560 private int indexOfToastLocked(String pkg, ITransientNotification callback)
561 {
562 IBinder cbak = callback.asBinder();
563 ArrayList<ToastRecord> list = mToastQueue;
564 int len = list.size();
565 for (int i=0; i<len; i++) {
566 ToastRecord r = list.get(i);
567 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
568 return i;
569 }
570 }
571 return -1;
572 }
573
574 // lock on mToastQueue
575 private void keepProcessAliveLocked(int pid)
576 {
577 int toastCount = 0; // toasts from this pid
578 ArrayList<ToastRecord> list = mToastQueue;
579 int N = list.size();
580 for (int i=0; i<N; i++) {
581 ToastRecord r = list.get(i);
582 if (r.pid == pid) {
583 toastCount++;
584 }
585 }
586 try {
587 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
588 } catch (RemoteException e) {
589 // Shouldn't happen.
590 }
591 }
592
593 private final class WorkerHandler extends Handler
594 {
595 @Override
596 public void handleMessage(Message msg)
597 {
598 switch (msg.what)
599 {
600 case MESSAGE_TIMEOUT:
601 handleTimeout((ToastRecord)msg.obj);
602 break;
603 }
604 }
605 }
606
607
608 // Notifications
609 // ============================================================================
610 public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
611 {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700612 enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
613 }
614
615 public void enqueueNotificationWithTag(String pkg, String tag, int id,
616 Notification notification, int[] idOut)
617 {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700618 checkIncomingCall(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800619
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 // This conditional is a dirty hack to limit the logging done on
621 // behalf of the download manager without affecting other apps.
622 if (!pkg.equals("com.android.providers.downloads")
623 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800624 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, notification.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 }
626
627 if (pkg == null || notification == null) {
628 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
629 + " id=" + id + " notification=" + notification);
630 }
631 if (notification.icon != 0) {
632 if (notification.contentView == null) {
633 throw new IllegalArgumentException("contentView required: pkg=" + pkg
634 + " id=" + id + " notification=" + notification);
635 }
636 if (notification.contentIntent == null) {
637 throw new IllegalArgumentException("contentIntent required: pkg=" + pkg
638 + " id=" + id + " notification=" + notification);
639 }
640 }
641
642 synchronized (mNotificationList) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700643 NotificationRecord r = new NotificationRecord(pkg, tag, id, notification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 NotificationRecord old = null;
645
Fred Quintana6ecaff12009-09-25 14:23:13 -0700646 int index = indexOfNotificationLocked(pkg, tag, id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 if (index < 0) {
648 mNotificationList.add(r);
649 } else {
650 old = mNotificationList.remove(index);
651 mNotificationList.add(index, r);
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700652 // Make sure we don't lose the foreground service state.
653 if (old != null) {
654 notification.flags |=
655 old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
656 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800658
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700659 // Ensure if this is a foreground service that the proper additional
660 // flags are set.
661 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
662 notification.flags |= Notification.FLAG_ONGOING_EVENT
663 | Notification.FLAG_NO_CLEAR;
664 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800665
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 if (notification.icon != 0) {
667 IconData icon = IconData.makeIcon(null, pkg, notification.icon,
668 notification.iconLevel,
669 notification.number);
670 CharSequence truncatedTicker = notification.tickerText;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800671
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 // TODO: make this restriction do something smarter like never fill
673 // more than two screens. "Why would anyone need more than 80 characters." :-/
674 final int maxTickerLen = 80;
675 if (truncatedTicker != null && truncatedTicker.length() > maxTickerLen) {
676 truncatedTicker = truncatedTicker.subSequence(0, maxTickerLen);
677 }
678
679 NotificationData n = new NotificationData();
Fred Quintana6ecaff12009-09-25 14:23:13 -0700680 n.pkg = pkg;
681 n.tag = tag;
682 n.id = id;
683 n.when = notification.when;
684 n.tickerText = truncatedTicker;
685 n.ongoingEvent = (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
686 if (!n.ongoingEvent && (notification.flags & Notification.FLAG_NO_CLEAR) == 0) {
687 n.clearable = true;
688 }
689 n.contentView = notification.contentView;
690 n.contentIntent = notification.contentIntent;
691 n.deleteIntent = notification.deleteIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 if (old != null && old.statusBarKey != null) {
693 r.statusBarKey = old.statusBarKey;
694 long identity = Binder.clearCallingIdentity();
695 try {
696 mStatusBarService.updateIcon(r.statusBarKey, icon, n);
697 }
698 finally {
699 Binder.restoreCallingIdentity(identity);
700 }
701 } else {
702 long identity = Binder.clearCallingIdentity();
703 try {
704 r.statusBarKey = mStatusBarService.addIcon(icon, n);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500705 mAttentionLight.pulse();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 }
707 finally {
708 Binder.restoreCallingIdentity(identity);
709 }
710 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700711
Joe Onorato30275482009-07-08 17:09:14 -0700712 sendAccessibilityEvent(notification, pkg);
svetoslavganov75986cf2009-05-14 22:28:01 -0700713
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714 } else {
715 if (old != null && old.statusBarKey != null) {
716 long identity = Binder.clearCallingIdentity();
717 try {
718 mStatusBarService.removeIcon(old.statusBarKey);
719 }
720 finally {
721 Binder.restoreCallingIdentity(identity);
722 }
723 }
724 }
725
726 // If we're not supposed to beep, vibrate, etc. then don't.
727 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
728 && (!(old != null
Joe Onorato30275482009-07-08 17:09:14 -0700729 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
730 && mSystemReady) {
Eric Laurent524dc042009-11-27 05:07:55 -0800731
732 final AudioManager audioManager = (AudioManager) mContext
733 .getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 // sound
735 final boolean useDefaultSound =
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800736 (notification.defaults & Notification.DEFAULT_SOUND) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 if (useDefaultSound || notification.sound != null) {
738 Uri uri;
739 if (useDefaultSound) {
740 uri = Settings.System.DEFAULT_NOTIFICATION_URI;
741 } else {
742 uri = notification.sound;
743 }
744 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
745 int audioStreamType;
746 if (notification.audioStreamType >= 0) {
747 audioStreamType = notification.audioStreamType;
748 } else {
749 audioStreamType = DEFAULT_STREAM_TYPE;
750 }
751 mSoundNotification = r;
Eric Laurent524dc042009-11-27 05:07:55 -0800752 // do not play notifications if stream volume is 0
753 // (typically because ringer mode is silent).
754 if (audioManager.getStreamVolume(audioStreamType) != 0) {
755 long identity = Binder.clearCallingIdentity();
756 try {
757 mSound.play(mContext, uri, looping, audioStreamType);
758 }
759 finally {
760 Binder.restoreCallingIdentity(identity);
761 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 }
763 }
764
765 // vibrate
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 final boolean useDefaultVibrate =
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800767 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 if ((useDefaultVibrate || notification.vibrate != null)
769 && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) {
770 mVibrateNotification = r;
771
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800772 mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 : notification.vibrate,
774 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
775 }
776 }
777
778 // this option doesn't shut off the lights
779
780 // light
781 // the most recent thing gets the light
782 mLights.remove(old);
783 if (mLedNotification == old) {
784 mLedNotification = null;
785 }
786 //Log.i(TAG, "notification.lights="
787 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
788 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
789 mLights.add(r);
790 updateLightsLocked();
791 } else {
792 if (old != null
793 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
794 updateLightsLocked();
795 }
796 }
797 }
798
799 idOut[0] = id;
800 }
801
Joe Onorato30275482009-07-08 17:09:14 -0700802 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700803 AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
804 if (!manager.isEnabled()) {
805 return;
806 }
807
808 AccessibilityEvent event =
809 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
810 event.setPackageName(packageName);
811 event.setClassName(Notification.class.getName());
812 event.setParcelableData(notification);
813 CharSequence tickerText = notification.tickerText;
814 if (!TextUtils.isEmpty(tickerText)) {
815 event.getText().add(tickerText);
816 }
817
818 manager.sendAccessibilityEvent(event);
819 }
820
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 private void cancelNotificationLocked(NotificationRecord r) {
822 // status bar
823 if (r.notification.icon != 0) {
824 long identity = Binder.clearCallingIdentity();
825 try {
826 mStatusBarService.removeIcon(r.statusBarKey);
827 }
828 finally {
829 Binder.restoreCallingIdentity(identity);
830 }
831 r.statusBarKey = null;
832 }
833
834 // sound
835 if (mSoundNotification == r) {
836 mSoundNotification = null;
837 long identity = Binder.clearCallingIdentity();
838 try {
839 mSound.stop();
840 }
841 finally {
842 Binder.restoreCallingIdentity(identity);
843 }
844 }
845
846 // vibrate
847 if (mVibrateNotification == r) {
848 mVibrateNotification = null;
849 long identity = Binder.clearCallingIdentity();
850 try {
851 mVibrator.cancel();
852 }
853 finally {
854 Binder.restoreCallingIdentity(identity);
855 }
856 }
857
858 // light
859 mLights.remove(r);
860 if (mLedNotification == r) {
861 mLedNotification = null;
862 }
863 }
864
865 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700866 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800867 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 */
Fred Quintana6ecaff12009-09-25 14:23:13 -0700869 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700870 int mustNotHaveFlags) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800871 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872
873 synchronized (mNotificationList) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700874 int index = indexOfNotificationLocked(pkg, tag, id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 if (index >= 0) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700876 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800877
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
879 return;
880 }
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700881 if ((r.notification.flags & mustNotHaveFlags) != 0) {
882 return;
883 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800884
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 mNotificationList.remove(index);
886
887 cancelNotificationLocked(r);
888 updateLightsLocked();
889 }
890 }
891 }
892
893 /**
894 * Cancels all notifications from a given package that have all of the
895 * {@code mustHaveFlags}.
896 */
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700897 void cancelAllNotificationsInt(String pkg, int mustHaveFlags,
898 int mustNotHaveFlags) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800899 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900
901 synchronized (mNotificationList) {
902 final int N = mNotificationList.size();
903 boolean canceledSomething = false;
904 for (int i = N-1; i >= 0; --i) {
905 NotificationRecord r = mNotificationList.get(i);
906 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
907 continue;
908 }
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700909 if ((r.notification.flags & mustNotHaveFlags) != 0) {
910 continue;
911 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800912 if (!r.pkg.equals(pkg)) {
913 continue;
914 }
915 mNotificationList.remove(i);
916 cancelNotificationLocked(r);
917 canceledSomething = true;
918 }
919 if (canceledSomething) {
920 updateLightsLocked();
921 }
922 }
923 }
924
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800925
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700926 public void cancelNotification(String pkg, int id) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700927 cancelNotificationWithTag(pkg, null /* tag */, id);
928 }
929
930 public void cancelNotificationWithTag(String pkg, String tag, int id) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700931 checkIncomingCall(pkg);
932 // Don't allow client applications to cancel foreground service notis.
Fred Quintana6ecaff12009-09-25 14:23:13 -0700933 cancelNotification(pkg, tag, id, 0,
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700934 Binder.getCallingUid() == Process.SYSTEM_UID
935 ? 0 : Notification.FLAG_FOREGROUND_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 }
937
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700938 public void cancelAllNotifications(String pkg) {
939 checkIncomingCall(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800940
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700941 // Calling from user space, don't allow the canceling of actively
942 // running foreground services.
943 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 }
945
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700946 void checkIncomingCall(String pkg) {
947 int uid = Binder.getCallingUid();
948 if (uid == Process.SYSTEM_UID || uid == 0) {
949 return;
950 }
951 try {
952 ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
953 pkg, 0);
954 if (ai.uid != uid) {
955 throw new SecurityException("Calling uid " + uid + " gave package"
956 + pkg + " which is owned by uid " + ai.uid);
957 }
958 } catch (PackageManager.NameNotFoundException e) {
959 throw new SecurityException("Unknown package " + pkg);
960 }
961 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800962
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700963 void cancelAll() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 synchronized (mNotificationList) {
965 final int N = mNotificationList.size();
966 for (int i=N-1; i>=0; i--) {
967 NotificationRecord r = mNotificationList.get(i);
968
969 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
970 | Notification.FLAG_NO_CLEAR)) == 0) {
971 if (r.notification.deleteIntent != null) {
972 try {
973 r.notification.deleteIntent.send();
974 } catch (PendingIntent.CanceledException ex) {
975 // do nothing - there's no relevant way to recover, and
976 // no reason to let this propagate
977 Log.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
978 }
979 }
980 mNotificationList.remove(i);
981 cancelNotificationLocked(r);
982 }
983 }
984
985 updateLightsLocked();
986 }
987 }
988
989 private void updateLights() {
990 synchronized (mNotificationList) {
991 updateLightsLocked();
992 }
993 }
994
995 // lock on mNotificationList
996 private void updateLightsLocked()
997 {
The Android Open Source Project10592532009-03-18 17:39:46 -0700998 // Battery low always shows, other states only show if charging.
999 if (mBatteryLow) {
Mike Lockwood445f4302009-09-04 11:06:46 -04001000 if (mBatteryCharging) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001001 mBatteryLight.setColor(BATTERY_LOW_ARGB);
Mike Lockwood445f4302009-09-04 11:06:46 -04001002 } else {
1003 // Flash when battery is low and not charging
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001004 mBatteryLight.setFlashing(BATTERY_LOW_ARGB, LightsService.LIGHT_FLASH_TIMED,
1005 BATTERY_BLINK_ON, BATTERY_BLINK_OFF);
Mike Lockwood445f4302009-09-04 11:06:46 -04001006 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 } else if (mBatteryCharging) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001008 if (mBatteryFull) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001009 mBatteryLight.setColor(BATTERY_FULL_ARGB);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 } else {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001011 mBatteryLight.setColor(BATTERY_MEDIUM_ARGB);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 }
1013 } else {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001014 mBatteryLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07001015 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001016
The Android Open Source Project10592532009-03-18 17:39:46 -07001017 // handle notification lights
1018 if (mLedNotification == null) {
1019 // get next notification, if any
1020 int n = mLights.size();
1021 if (n > 0) {
1022 mLedNotification = mLights.get(n-1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 }
1024 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001025
1026 // we only flash if screen is off and persistent pulsing is enabled
1027 if (mLedNotification == null || mScreenOn || !mNotificationPulseEnabled) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001028 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07001029 } else {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001030 mNotificationLight.setFlashing(
The Android Open Source Project10592532009-03-18 17:39:46 -07001031 mLedNotification.notification.ledARGB,
Mike Lockwood3a322132009-11-24 00:30:52 -05001032 LightsService.LIGHT_FLASH_TIMED,
The Android Open Source Project10592532009-03-18 17:39:46 -07001033 mLedNotification.notification.ledOnMS,
1034 mLedNotification.notification.ledOffMS);
1035 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 }
1037
1038 // lock on mNotificationList
Fred Quintana6ecaff12009-09-25 14:23:13 -07001039 private int indexOfNotificationLocked(String pkg, String tag, int id)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040 {
1041 ArrayList<NotificationRecord> list = mNotificationList;
1042 final int len = list.size();
1043 for (int i=0; i<len; i++) {
1044 NotificationRecord r = list.get(i);
Fred Quintana6ecaff12009-09-25 14:23:13 -07001045 if (tag == null) {
1046 if (r.tag != null) {
1047 continue;
1048 }
1049 } else {
1050 if (!tag.equals(r.tag)) {
1051 continue;
1052 }
1053 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 if (r.id == id && r.pkg.equals(pkg)) {
1055 return i;
1056 }
1057 }
1058 return -1;
1059 }
1060
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001061 // This is here instead of StatusBarPolicy because it is an important
1062 // security feature that we don't want people customizing the platform
1063 // to accidentally lose.
1064 private void updateAdbNotification() {
Mike Lockwoodea8b7d52009-08-04 17:03:15 -04001065 if (mAdbEnabled && mUsbConnected) {
Mike Lockwooded760372009-07-09 07:07:27 -04001066 if ("0".equals(SystemProperties.get("persist.adb.notify"))) {
1067 return;
1068 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001069 if (!mAdbNotificationShown) {
1070 NotificationManager notificationManager = (NotificationManager) mContext
1071 .getSystemService(Context.NOTIFICATION_SERVICE);
1072 if (notificationManager != null) {
1073 Resources r = mContext.getResources();
1074 CharSequence title = r.getText(
1075 com.android.internal.R.string.adb_active_notification_title);
1076 CharSequence message = r.getText(
1077 com.android.internal.R.string.adb_active_notification_message);
1078
1079 if (mAdbNotification == null) {
1080 mAdbNotification = new Notification();
1081 mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_warning;
1082 mAdbNotification.when = 0;
1083 mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;
1084 mAdbNotification.tickerText = title;
1085 mAdbNotification.defaults |= Notification.DEFAULT_SOUND;
1086 }
1087
1088 Intent intent = new Intent(
1089 Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
1090 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1091 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1092 // Note: we are hard-coding the component because this is
1093 // an important security UI that we don't want anyone
1094 // intercepting.
1095 intent.setComponent(new ComponentName("com.android.settings",
1096 "com.android.settings.DevelopmentSettings"));
1097 PendingIntent pi = PendingIntent.getActivity(mContext, 0,
1098 intent, 0);
1099
1100 mAdbNotification.setLatestEventInfo(mContext, title, message, pi);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001101
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001102 mAdbNotificationShown = true;
1103 notificationManager.notify(
1104 com.android.internal.R.string.adb_active_notification_title,
1105 mAdbNotification);
1106 }
1107 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001108
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001109 } else if (mAdbNotificationShown) {
1110 NotificationManager notificationManager = (NotificationManager) mContext
1111 .getSystemService(Context.NOTIFICATION_SERVICE);
1112 if (notificationManager != null) {
1113 mAdbNotificationShown = false;
1114 notificationManager.cancel(
1115 com.android.internal.R.string.adb_active_notification_title);
1116 }
1117 }
1118 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001119
1120 private void updateNotificationPulse() {
1121 synchronized (mNotificationList) {
1122 updateLightsLocked();
1123 }
1124 }
1125
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 // ======================================================================
1127 @Override
1128 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1129 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1130 != PackageManager.PERMISSION_GRANTED) {
1131 pw.println("Permission Denial: can't dump NotificationManager from from pid="
1132 + Binder.getCallingPid()
1133 + ", uid=" + Binder.getCallingUid());
1134 return;
1135 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 pw.println("Current Notification Manager state:");
1138
1139 int N;
1140
1141 synchronized (mToastQueue) {
1142 N = mToastQueue.size();
1143 if (N > 0) {
1144 pw.println(" Toast Queue:");
1145 for (int i=0; i<N; i++) {
1146 mToastQueue.get(i).dump(pw, " ");
1147 }
1148 pw.println(" ");
1149 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001151 }
1152
1153 synchronized (mNotificationList) {
1154 N = mNotificationList.size();
1155 if (N > 0) {
1156 pw.println(" Notification List:");
1157 for (int i=0; i<N; i++) {
1158 mNotificationList.get(i).dump(pw, " ", mContext);
1159 }
1160 pw.println(" ");
1161 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001162
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 N = mLights.size();
1164 if (N > 0) {
1165 pw.println(" Lights List:");
1166 for (int i=0; i<N; i++) {
1167 mLights.get(i).dump(pw, " ", mContext);
1168 }
1169 pw.println(" ");
1170 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 pw.println(" mSoundNotification=" + mSoundNotification);
1173 pw.println(" mSound=" + mSound);
1174 pw.println(" mVibrateNotification=" + mVibrateNotification);
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001175 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
1176 pw.println(" mSystemReady=" + mSystemReady);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 }
1178 }
1179}