blob: ad8ab84bb9fdc18ae9bb9d10638c19b8bf4eb244 [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;
San Mehatb1043402010-02-05 08:26:50 -080053import android.os.storage.StorageManager;
Mike Lockwooded760372009-07-09 07:07:27 -040054import android.os.SystemProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.os.Vibrator;
56import android.provider.Settings;
svetoslavganov75986cf2009-05-14 22:28:01 -070057import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.util.EventLog;
59import android.util.Log;
svetoslavganov75986cf2009-05-14 22:28:01 -070060import android.view.accessibility.AccessibilityEvent;
61import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.widget.Toast;
63
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import java.io.FileDescriptor;
65import java.io.PrintWriter;
66import java.util.ArrayList;
67import java.util.Arrays;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068
69class NotificationManagerService extends INotificationManager.Stub
70{
71 private static final String TAG = "NotificationService";
72 private static final boolean DBG = false;
73
74 // message codes
75 private static final int MESSAGE_TIMEOUT = 2;
76
77 private static final int LONG_DELAY = 3500; // 3.5 seconds
78 private static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -080079
80 private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
82 private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
83
84 final Context mContext;
85 final IActivityManager mAm;
86 final IBinder mForegroundToken = new Binder();
87
88 private WorkerHandler mHandler;
89 private StatusBarService mStatusBarService;
Mike Lockwood3a322132009-11-24 00:30:52 -050090 private LightsService mLightsService;
Mike Lockwood3cb67a32009-11-27 14:25:58 -050091 private LightsService.Light mBatteryLight;
92 private LightsService.Light mNotificationLight;
93 private LightsService.Light mAttentionLight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094
Mike Lockwood670f9322010-01-20 12:13:36 -050095 private int mDefaultNotificationColor;
96 private int mDefaultNotificationLedOn;
97 private int mDefaultNotificationLedOff;
98
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 private NotificationRecord mSoundNotification;
100 private AsyncPlayer mSound;
Joe Onorato30275482009-07-08 17:09:14 -0700101 private boolean mSystemReady;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400102 private int mDisabledNotifications;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103
104 private NotificationRecord mVibrateNotification;
105 private Vibrator mVibrator = new Vibrator();
106
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500107 // for enabling and disabling notification pulse behavior
108 private boolean mScreenOn = true;
109 private boolean mNotificationPulseEnabled;
110
111 // for adb connected notifications
Mike Lockwoodea8b7d52009-08-04 17:03:15 -0400112 private boolean mUsbConnected;
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700113 private boolean mAdbEnabled = false;
114 private boolean mAdbNotificationShown = false;
115 private Notification mAdbNotification;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800116
Fred Quintana6ecaff12009-09-25 14:23:13 -0700117 private final ArrayList<NotificationRecord> mNotificationList =
118 new ArrayList<NotificationRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119
120 private ArrayList<ToastRecord> mToastQueue;
121
122 private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
123
124 private boolean mBatteryCharging;
125 private boolean mBatteryLow;
126 private boolean mBatteryFull;
127 private NotificationRecord mLedNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -0700128
The Android Open Source Project10592532009-03-18 17:39:46 -0700129 private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on
130 private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on
131 private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on
132 private static final int BATTERY_BLINK_ON = 125;
133 private static final int BATTERY_BLINK_OFF = 2875;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 private static String idDebugString(Context baseContext, String packageName, int id) {
136 Context c = null;
137
138 if (packageName != null) {
139 try {
140 c = baseContext.createPackageContext(packageName, 0);
141 } catch (NameNotFoundException e) {
142 c = baseContext;
143 }
144 } else {
145 c = baseContext;
146 }
147
148 String pkg;
149 String type;
150 String name;
151
152 Resources r = c.getResources();
153 try {
154 return r.getResourceName(id);
155 } catch (Resources.NotFoundException e) {
156 return "<name unknown>";
157 }
158 }
159
160 private static final class NotificationRecord
161 {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700162 final String pkg;
163 final String tag;
164 final int id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 ITransientNotification callback;
166 int duration;
Fred Quintana6ecaff12009-09-25 14:23:13 -0700167 final Notification notification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 IBinder statusBarKey;
169
Fred Quintana6ecaff12009-09-25 14:23:13 -0700170 NotificationRecord(String pkg, String tag, int id, Notification notification)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 {
172 this.pkg = pkg;
Fred Quintana6ecaff12009-09-25 14:23:13 -0700173 this.tag = tag;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 this.id = id;
175 this.notification = notification;
176 }
Fred Quintana6ecaff12009-09-25 14:23:13 -0700177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 void dump(PrintWriter pw, String prefix, Context baseContext) {
179 pw.println(prefix + this);
180 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
181 + " / " + idDebugString(baseContext, this.pkg, notification.icon));
182 pw.println(prefix + " contentIntent=" + notification.contentIntent);
183 pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
184 pw.println(prefix + " tickerText=" + notification.tickerText);
185 pw.println(prefix + " contentView=" + notification.contentView);
186 pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults));
187 pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags));
188 pw.println(prefix + " sound=" + notification.sound);
189 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
190 pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB)
191 + " ledOnMS=" + notification.ledOnMS
192 + " ledOffMS=" + notification.ledOffMS);
193 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 @Override
196 public final String toString()
197 {
198 return "NotificationRecord{"
199 + Integer.toHexString(System.identityHashCode(this))
200 + " pkg=" + pkg
Fred Quintana6ecaff12009-09-25 14:23:13 -0700201 + " id=" + Integer.toHexString(id)
202 + " tag=" + tag + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 }
204 }
205
206 private static final class ToastRecord
207 {
208 final int pid;
209 final String pkg;
210 final ITransientNotification callback;
211 int duration;
212
213 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
214 {
215 this.pid = pid;
216 this.pkg = pkg;
217 this.callback = callback;
218 this.duration = duration;
219 }
220
221 void update(int duration) {
222 this.duration = duration;
223 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800224
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 void dump(PrintWriter pw, String prefix) {
226 pw.println(prefix + this);
227 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800228
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 @Override
230 public final String toString()
231 {
232 return "ToastRecord{"
233 + Integer.toHexString(System.identityHashCode(this))
234 + " pkg=" + pkg
235 + " callback=" + callback
236 + " duration=" + duration;
237 }
238 }
239
240 private StatusBarService.NotificationCallbacks mNotificationCallbacks
241 = new StatusBarService.NotificationCallbacks() {
242
243 public void onSetDisabled(int status) {
244 synchronized (mNotificationList) {
245 mDisabledNotifications = status;
246 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
247 // cancel whatever's going on
248 long identity = Binder.clearCallingIdentity();
249 try {
250 mSound.stop();
251 }
252 finally {
253 Binder.restoreCallingIdentity(identity);
254 }
255
256 identity = Binder.clearCallingIdentity();
257 try {
258 mVibrator.cancel();
259 }
260 finally {
261 Binder.restoreCallingIdentity(identity);
262 }
263 }
264 }
265 }
266
267 public void onClearAll() {
268 cancelAll();
269 }
270
Fred Quintana6ecaff12009-09-25 14:23:13 -0700271 public void onNotificationClick(String pkg, String tag, int id) {
272 cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700273 Notification.FLAG_FOREGROUND_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 }
275
276 public void onPanelRevealed() {
277 synchronized (mNotificationList) {
278 // sound
279 mSoundNotification = null;
280 long identity = Binder.clearCallingIdentity();
281 try {
282 mSound.stop();
283 }
284 finally {
285 Binder.restoreCallingIdentity(identity);
286 }
287
288 // vibrate
289 mVibrateNotification = null;
290 identity = Binder.clearCallingIdentity();
291 try {
292 mVibrator.cancel();
293 }
294 finally {
295 Binder.restoreCallingIdentity(identity);
296 }
297
298 // light
299 mLights.clear();
300 mLedNotification = null;
301 updateLightsLocked();
302 }
303 }
304 };
305
306 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
307 @Override
308 public void onReceive(Context context, Intent intent) {
309 String action = intent.getAction();
310
311 if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
312 boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0);
313 int level = intent.getIntExtra("level", -1);
314 boolean batteryLow = (level >= 0 && level <= Power.LOW_BATTERY_THRESHOLD);
315 int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
316 boolean batteryFull = (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90);
317
318 if (batteryCharging != mBatteryCharging ||
319 batteryLow != mBatteryLow ||
320 batteryFull != mBatteryFull) {
321 mBatteryCharging = batteryCharging;
322 mBatteryLow = batteryLow;
323 mBatteryFull = batteryFull;
324 updateLights();
325 }
Mike Lockwoodea8b7d52009-08-04 17:03:15 -0400326 } else if (action.equals(Intent.ACTION_UMS_CONNECTED)) {
327 mUsbConnected = true;
328 updateAdbNotification();
329 } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
330 mUsbConnected = false;
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700331 updateAdbNotification();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800333 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800334 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800335 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800336 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800337 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
338 } else {
339 Uri uri = intent.getData();
340 if (uri == null) {
341 return;
342 }
343 String pkgName = uri.getSchemeSpecificPart();
344 if (pkgName == null) {
345 return;
346 }
347 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800349 if (pkgList != null && (pkgList.length > 0)) {
350 for (String pkgName : pkgList) {
351 cancelAllNotificationsInt(pkgName, 0, 0);
352 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500354 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
355 mScreenOn = true;
356 updateNotificationPulse();
357 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
358 mScreenOn = false;
359 updateNotificationPulse();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 }
361 }
362 };
363
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700364 class SettingsObserver extends ContentObserver {
365 SettingsObserver(Handler handler) {
366 super(handler);
367 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800368
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700369 void observe() {
370 ContentResolver resolver = mContext.getContentResolver();
371 resolver.registerContentObserver(Settings.Secure.getUriFor(
372 Settings.Secure.ADB_ENABLED), false, this);
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500373 resolver.registerContentObserver(Settings.System.getUriFor(
374 Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700375 update();
376 }
377
378 @Override public void onChange(boolean selfChange) {
379 update();
380 }
381
382 public void update() {
383 ContentResolver resolver = mContext.getContentResolver();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500384 boolean adbEnabled = Settings.Secure.getInt(resolver,
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700385 Settings.Secure.ADB_ENABLED, 0) != 0;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500386 if (mAdbEnabled != adbEnabled) {
387 mAdbEnabled = adbEnabled;
388 updateAdbNotification();
389 }
390 boolean pulseEnabled = Settings.System.getInt(resolver,
391 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
392 if (mNotificationPulseEnabled != pulseEnabled) {
393 mNotificationPulseEnabled = pulseEnabled;
394 updateNotificationPulse();
395 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700396 }
397 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500398
The Android Open Source Project10592532009-03-18 17:39:46 -0700399 NotificationManagerService(Context context, StatusBarService statusBar,
Mike Lockwood3a322132009-11-24 00:30:52 -0500400 LightsService lights)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 {
402 super();
403 mContext = context;
Mike Lockwood3a322132009-11-24 00:30:52 -0500404 mLightsService = lights;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 mAm = ActivityManagerNative.getDefault();
406 mSound = new AsyncPlayer(TAG);
407 mSound.setUsesWakeLock(context);
408 mToastQueue = new ArrayList<ToastRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 mHandler = new WorkerHandler();
San Mehat3ee13172010-02-04 20:54:43 -0800410
411 StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
412 sm.registerListener(new com.android.internal.app.StorageNotification(context));
413
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 mStatusBarService = statusBar;
415 statusBar.setNotificationCallbacks(mNotificationCallbacks);
416
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500417 mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
418 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 // register for battery changed notifications
439 IntentFilter filter = new IntentFilter();
440 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
Mike Lockwoodea8b7d52009-08-04 17:03:15 -0400441 filter.addAction(Intent.ACTION_UMS_CONNECTED);
442 filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
444 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500445 filter.addAction(Intent.ACTION_SCREEN_ON);
446 filter.addAction(Intent.ACTION_SCREEN_OFF);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 mContext.registerReceiver(mIntentReceiver, filter);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800448 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800449 mContext.registerReceiver(mIntentReceiver, sdFilter);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800450
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500451 SettingsObserver observer = new SettingsObserver(mHandler);
452 observer.observe();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 }
454
Joe Onorato30275482009-07-08 17:09:14 -0700455 void systemReady() {
456 // no beeping until we're basically done booting
457 mSystemReady = true;
458 }
459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 // Toasts
461 // ============================================================================
462 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
463 {
464 Log.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);
465
466 if (pkg == null || callback == null) {
467 Log.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
468 return ;
469 }
470
471 synchronized (mToastQueue) {
472 int callingPid = Binder.getCallingPid();
473 long callingId = Binder.clearCallingIdentity();
474 try {
475 ToastRecord record;
476 int index = indexOfToastLocked(pkg, callback);
477 // If it's already in the queue, we update it in place, we don't
478 // move it to the end of the queue.
479 if (index >= 0) {
480 record = mToastQueue.get(index);
481 record.update(duration);
482 } else {
483 record = new ToastRecord(callingPid, pkg, callback, duration);
484 mToastQueue.add(record);
485 index = mToastQueue.size() - 1;
486 keepProcessAliveLocked(callingPid);
487 }
488 // If it's at index 0, it's the current toast. It doesn't matter if it's
489 // new or just been updated. Call back and tell it to show itself.
490 // If the callback fails, this will remove it from the list, so don't
491 // assume that it's valid after this.
492 if (index == 0) {
493 showNextToastLocked();
494 }
495 } finally {
496 Binder.restoreCallingIdentity(callingId);
497 }
498 }
499 }
500
501 public void cancelToast(String pkg, ITransientNotification callback) {
502 Log.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
503
504 if (pkg == null || callback == null) {
505 Log.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
506 return ;
507 }
508
509 synchronized (mToastQueue) {
510 long callingId = Binder.clearCallingIdentity();
511 try {
512 int index = indexOfToastLocked(pkg, callback);
513 if (index >= 0) {
514 cancelToastLocked(index);
515 } else {
516 Log.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback);
517 }
518 } finally {
519 Binder.restoreCallingIdentity(callingId);
520 }
521 }
522 }
523
524 private void showNextToastLocked() {
525 ToastRecord record = mToastQueue.get(0);
526 while (record != null) {
527 if (DBG) Log.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
528 try {
529 record.callback.show();
530 scheduleTimeoutLocked(record, false);
531 return;
532 } catch (RemoteException e) {
533 Log.w(TAG, "Object died trying to show notification " + record.callback
534 + " in package " + record.pkg);
535 // remove it from the list and let the process die
536 int index = mToastQueue.indexOf(record);
537 if (index >= 0) {
538 mToastQueue.remove(index);
539 }
540 keepProcessAliveLocked(record.pid);
541 if (mToastQueue.size() > 0) {
542 record = mToastQueue.get(0);
543 } else {
544 record = null;
545 }
546 }
547 }
548 }
549
550 private void cancelToastLocked(int index) {
551 ToastRecord record = mToastQueue.get(index);
552 try {
553 record.callback.hide();
554 } catch (RemoteException e) {
555 Log.w(TAG, "Object died trying to hide notification " + record.callback
556 + " in package " + record.pkg);
557 // don't worry about this, we're about to remove it from
558 // the list anyway
559 }
560 mToastQueue.remove(index);
561 keepProcessAliveLocked(record.pid);
562 if (mToastQueue.size() > 0) {
563 // Show the next one. If the callback fails, this will remove
564 // it from the list, so don't assume that the list hasn't changed
565 // after this point.
566 showNextToastLocked();
567 }
568 }
569
570 private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
571 {
572 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
573 long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
574 mHandler.removeCallbacksAndMessages(r);
575 mHandler.sendMessageDelayed(m, delay);
576 }
577
578 private void handleTimeout(ToastRecord record)
579 {
580 if (DBG) Log.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
581 synchronized (mToastQueue) {
582 int index = indexOfToastLocked(record.pkg, record.callback);
583 if (index >= 0) {
584 cancelToastLocked(index);
585 }
586 }
587 }
588
589 // lock on mToastQueue
590 private int indexOfToastLocked(String pkg, ITransientNotification callback)
591 {
592 IBinder cbak = callback.asBinder();
593 ArrayList<ToastRecord> list = mToastQueue;
594 int len = list.size();
595 for (int i=0; i<len; i++) {
596 ToastRecord r = list.get(i);
597 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
598 return i;
599 }
600 }
601 return -1;
602 }
603
604 // lock on mToastQueue
605 private void keepProcessAliveLocked(int pid)
606 {
607 int toastCount = 0; // toasts from this pid
608 ArrayList<ToastRecord> list = mToastQueue;
609 int N = list.size();
610 for (int i=0; i<N; i++) {
611 ToastRecord r = list.get(i);
612 if (r.pid == pid) {
613 toastCount++;
614 }
615 }
616 try {
617 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
618 } catch (RemoteException e) {
619 // Shouldn't happen.
620 }
621 }
622
623 private final class WorkerHandler extends Handler
624 {
625 @Override
626 public void handleMessage(Message msg)
627 {
628 switch (msg.what)
629 {
630 case MESSAGE_TIMEOUT:
631 handleTimeout((ToastRecord)msg.obj);
632 break;
633 }
634 }
635 }
636
637
638 // Notifications
639 // ============================================================================
640 public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
641 {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700642 enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
643 }
644
645 public void enqueueNotificationWithTag(String pkg, String tag, int id,
646 Notification notification, int[] idOut)
647 {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700648 checkIncomingCall(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800649
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650 // This conditional is a dirty hack to limit the logging done on
651 // behalf of the download manager without affecting other apps.
652 if (!pkg.equals("com.android.providers.downloads")
653 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800654 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, notification.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655 }
656
657 if (pkg == null || notification == null) {
658 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
659 + " id=" + id + " notification=" + notification);
660 }
661 if (notification.icon != 0) {
662 if (notification.contentView == null) {
663 throw new IllegalArgumentException("contentView required: pkg=" + pkg
664 + " id=" + id + " notification=" + notification);
665 }
666 if (notification.contentIntent == null) {
667 throw new IllegalArgumentException("contentIntent required: pkg=" + pkg
668 + " id=" + id + " notification=" + notification);
669 }
670 }
671
672 synchronized (mNotificationList) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700673 NotificationRecord r = new NotificationRecord(pkg, tag, id, notification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 NotificationRecord old = null;
675
Fred Quintana6ecaff12009-09-25 14:23:13 -0700676 int index = indexOfNotificationLocked(pkg, tag, id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 if (index < 0) {
678 mNotificationList.add(r);
679 } else {
680 old = mNotificationList.remove(index);
681 mNotificationList.add(index, r);
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700682 // Make sure we don't lose the foreground service state.
683 if (old != null) {
684 notification.flags |=
685 old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
686 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800688
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700689 // Ensure if this is a foreground service that the proper additional
690 // flags are set.
691 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
692 notification.flags |= Notification.FLAG_ONGOING_EVENT
693 | Notification.FLAG_NO_CLEAR;
694 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800695
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 if (notification.icon != 0) {
697 IconData icon = IconData.makeIcon(null, pkg, notification.icon,
698 notification.iconLevel,
699 notification.number);
700 CharSequence truncatedTicker = notification.tickerText;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800701
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 // TODO: make this restriction do something smarter like never fill
703 // more than two screens. "Why would anyone need more than 80 characters." :-/
704 final int maxTickerLen = 80;
705 if (truncatedTicker != null && truncatedTicker.length() > maxTickerLen) {
706 truncatedTicker = truncatedTicker.subSequence(0, maxTickerLen);
707 }
708
709 NotificationData n = new NotificationData();
Fred Quintana6ecaff12009-09-25 14:23:13 -0700710 n.pkg = pkg;
711 n.tag = tag;
712 n.id = id;
713 n.when = notification.when;
714 n.tickerText = truncatedTicker;
715 n.ongoingEvent = (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
716 if (!n.ongoingEvent && (notification.flags & Notification.FLAG_NO_CLEAR) == 0) {
717 n.clearable = true;
718 }
719 n.contentView = notification.contentView;
720 n.contentIntent = notification.contentIntent;
721 n.deleteIntent = notification.deleteIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 if (old != null && old.statusBarKey != null) {
723 r.statusBarKey = old.statusBarKey;
724 long identity = Binder.clearCallingIdentity();
725 try {
726 mStatusBarService.updateIcon(r.statusBarKey, icon, n);
727 }
728 finally {
729 Binder.restoreCallingIdentity(identity);
730 }
731 } else {
732 long identity = Binder.clearCallingIdentity();
733 try {
734 r.statusBarKey = mStatusBarService.addIcon(icon, n);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500735 mAttentionLight.pulse();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 }
737 finally {
738 Binder.restoreCallingIdentity(identity);
739 }
740 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700741
Joe Onorato30275482009-07-08 17:09:14 -0700742 sendAccessibilityEvent(notification, pkg);
svetoslavganov75986cf2009-05-14 22:28:01 -0700743
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 } else {
745 if (old != null && old.statusBarKey != null) {
746 long identity = Binder.clearCallingIdentity();
747 try {
748 mStatusBarService.removeIcon(old.statusBarKey);
749 }
750 finally {
751 Binder.restoreCallingIdentity(identity);
752 }
753 }
754 }
755
756 // If we're not supposed to beep, vibrate, etc. then don't.
757 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
758 && (!(old != null
Joe Onorato30275482009-07-08 17:09:14 -0700759 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
760 && mSystemReady) {
Eric Laurent524dc042009-11-27 05:07:55 -0800761
762 final AudioManager audioManager = (AudioManager) mContext
763 .getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 // sound
765 final boolean useDefaultSound =
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800766 (notification.defaults & Notification.DEFAULT_SOUND) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 if (useDefaultSound || notification.sound != null) {
768 Uri uri;
769 if (useDefaultSound) {
770 uri = Settings.System.DEFAULT_NOTIFICATION_URI;
771 } else {
772 uri = notification.sound;
773 }
774 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
775 int audioStreamType;
776 if (notification.audioStreamType >= 0) {
777 audioStreamType = notification.audioStreamType;
778 } else {
779 audioStreamType = DEFAULT_STREAM_TYPE;
780 }
781 mSoundNotification = r;
Eric Laurent524dc042009-11-27 05:07:55 -0800782 // do not play notifications if stream volume is 0
783 // (typically because ringer mode is silent).
784 if (audioManager.getStreamVolume(audioStreamType) != 0) {
785 long identity = Binder.clearCallingIdentity();
786 try {
787 mSound.play(mContext, uri, looping, audioStreamType);
788 }
789 finally {
790 Binder.restoreCallingIdentity(identity);
791 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 }
793 }
794
795 // vibrate
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 final boolean useDefaultVibrate =
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800797 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 if ((useDefaultVibrate || notification.vibrate != null)
799 && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) {
800 mVibrateNotification = r;
801
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800802 mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 : notification.vibrate,
804 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
805 }
806 }
807
808 // this option doesn't shut off the lights
809
810 // light
811 // the most recent thing gets the light
812 mLights.remove(old);
813 if (mLedNotification == old) {
814 mLedNotification = null;
815 }
816 //Log.i(TAG, "notification.lights="
817 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
818 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
819 mLights.add(r);
820 updateLightsLocked();
821 } else {
822 if (old != null
823 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
824 updateLightsLocked();
825 }
826 }
827 }
828
829 idOut[0] = id;
830 }
831
Joe Onorato30275482009-07-08 17:09:14 -0700832 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
svetoslavganov75986cf2009-05-14 22:28:01 -0700833 AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
834 if (!manager.isEnabled()) {
835 return;
836 }
837
838 AccessibilityEvent event =
839 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
840 event.setPackageName(packageName);
841 event.setClassName(Notification.class.getName());
842 event.setParcelableData(notification);
843 CharSequence tickerText = notification.tickerText;
844 if (!TextUtils.isEmpty(tickerText)) {
845 event.getText().add(tickerText);
846 }
847
848 manager.sendAccessibilityEvent(event);
849 }
850
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 private void cancelNotificationLocked(NotificationRecord r) {
852 // status bar
853 if (r.notification.icon != 0) {
854 long identity = Binder.clearCallingIdentity();
855 try {
856 mStatusBarService.removeIcon(r.statusBarKey);
857 }
858 finally {
859 Binder.restoreCallingIdentity(identity);
860 }
861 r.statusBarKey = null;
862 }
863
864 // sound
865 if (mSoundNotification == r) {
866 mSoundNotification = null;
867 long identity = Binder.clearCallingIdentity();
868 try {
869 mSound.stop();
870 }
871 finally {
872 Binder.restoreCallingIdentity(identity);
873 }
874 }
875
876 // vibrate
877 if (mVibrateNotification == r) {
878 mVibrateNotification = null;
879 long identity = Binder.clearCallingIdentity();
880 try {
881 mVibrator.cancel();
882 }
883 finally {
884 Binder.restoreCallingIdentity(identity);
885 }
886 }
887
888 // light
889 mLights.remove(r);
890 if (mLedNotification == r) {
891 mLedNotification = null;
892 }
893 }
894
895 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700896 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800897 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 */
Fred Quintana6ecaff12009-09-25 14:23:13 -0700899 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700900 int mustNotHaveFlags) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800901 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902
903 synchronized (mNotificationList) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700904 int index = indexOfNotificationLocked(pkg, tag, id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 if (index >= 0) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700906 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800907
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800908 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
909 return;
910 }
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700911 if ((r.notification.flags & mustNotHaveFlags) != 0) {
912 return;
913 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800914
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 mNotificationList.remove(index);
916
917 cancelNotificationLocked(r);
918 updateLightsLocked();
919 }
920 }
921 }
922
923 /**
924 * Cancels all notifications from a given package that have all of the
925 * {@code mustHaveFlags}.
926 */
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700927 void cancelAllNotificationsInt(String pkg, int mustHaveFlags,
928 int mustNotHaveFlags) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800929 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930
931 synchronized (mNotificationList) {
932 final int N = mNotificationList.size();
933 boolean canceledSomething = false;
934 for (int i = N-1; i >= 0; --i) {
935 NotificationRecord r = mNotificationList.get(i);
936 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
937 continue;
938 }
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700939 if ((r.notification.flags & mustNotHaveFlags) != 0) {
940 continue;
941 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 if (!r.pkg.equals(pkg)) {
943 continue;
944 }
945 mNotificationList.remove(i);
946 cancelNotificationLocked(r);
947 canceledSomething = true;
948 }
949 if (canceledSomething) {
950 updateLightsLocked();
951 }
952 }
953 }
954
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800955
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700956 public void cancelNotification(String pkg, int id) {
Fred Quintana6ecaff12009-09-25 14:23:13 -0700957 cancelNotificationWithTag(pkg, null /* tag */, id);
958 }
959
960 public void cancelNotificationWithTag(String pkg, String tag, int id) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700961 checkIncomingCall(pkg);
962 // Don't allow client applications to cancel foreground service notis.
Fred Quintana6ecaff12009-09-25 14:23:13 -0700963 cancelNotification(pkg, tag, id, 0,
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700964 Binder.getCallingUid() == Process.SYSTEM_UID
965 ? 0 : Notification.FLAG_FOREGROUND_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966 }
967
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700968 public void cancelAllNotifications(String pkg) {
969 checkIncomingCall(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800970
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700971 // Calling from user space, don't allow the canceling of actively
972 // running foreground services.
973 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 }
975
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700976 void checkIncomingCall(String pkg) {
977 int uid = Binder.getCallingUid();
978 if (uid == Process.SYSTEM_UID || uid == 0) {
979 return;
980 }
981 try {
982 ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
983 pkg, 0);
984 if (ai.uid != uid) {
985 throw new SecurityException("Calling uid " + uid + " gave package"
986 + pkg + " which is owned by uid " + ai.uid);
987 }
988 } catch (PackageManager.NameNotFoundException e) {
989 throw new SecurityException("Unknown package " + pkg);
990 }
991 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800992
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700993 void cancelAll() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 synchronized (mNotificationList) {
995 final int N = mNotificationList.size();
996 for (int i=N-1; i>=0; i--) {
997 NotificationRecord r = mNotificationList.get(i);
998
999 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
1000 | Notification.FLAG_NO_CLEAR)) == 0) {
1001 if (r.notification.deleteIntent != null) {
1002 try {
1003 r.notification.deleteIntent.send();
1004 } catch (PendingIntent.CanceledException ex) {
1005 // do nothing - there's no relevant way to recover, and
1006 // no reason to let this propagate
1007 Log.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
1008 }
1009 }
1010 mNotificationList.remove(i);
1011 cancelNotificationLocked(r);
1012 }
1013 }
1014
1015 updateLightsLocked();
1016 }
1017 }
1018
1019 private void updateLights() {
1020 synchronized (mNotificationList) {
1021 updateLightsLocked();
1022 }
1023 }
1024
1025 // lock on mNotificationList
1026 private void updateLightsLocked()
1027 {
The Android Open Source Project10592532009-03-18 17:39:46 -07001028 // Battery low always shows, other states only show if charging.
1029 if (mBatteryLow) {
Mike Lockwood445f4302009-09-04 11:06:46 -04001030 if (mBatteryCharging) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001031 mBatteryLight.setColor(BATTERY_LOW_ARGB);
Mike Lockwood445f4302009-09-04 11:06:46 -04001032 } else {
1033 // Flash when battery is low and not charging
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001034 mBatteryLight.setFlashing(BATTERY_LOW_ARGB, LightsService.LIGHT_FLASH_TIMED,
1035 BATTERY_BLINK_ON, BATTERY_BLINK_OFF);
Mike Lockwood445f4302009-09-04 11:06:46 -04001036 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 } else if (mBatteryCharging) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001038 if (mBatteryFull) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001039 mBatteryLight.setColor(BATTERY_FULL_ARGB);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040 } else {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001041 mBatteryLight.setColor(BATTERY_MEDIUM_ARGB);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 }
1043 } else {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001044 mBatteryLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07001045 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046
The Android Open Source Project10592532009-03-18 17:39:46 -07001047 // handle notification lights
1048 if (mLedNotification == null) {
1049 // get next notification, if any
1050 int n = mLights.size();
1051 if (n > 0) {
1052 mLedNotification = mLights.get(n-1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 }
1054 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001055
1056 // we only flash if screen is off and persistent pulsing is enabled
Mike Lockwood670f9322010-01-20 12:13:36 -05001057 if (mLedNotification == null || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001058 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07001059 } else {
Mike Lockwood670f9322010-01-20 12:13:36 -05001060 int ledARGB = mLedNotification.notification.ledARGB;
1061 int ledOnMS = mLedNotification.notification.ledOnMS;
1062 int ledOffMS = mLedNotification.notification.ledOffMS;
1063 if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
1064 ledARGB = mDefaultNotificationColor;
1065 ledOnMS = mDefaultNotificationLedOn;
1066 ledOffMS = mDefaultNotificationLedOff;
1067 }
1068 if (mNotificationPulseEnabled) {
1069 // pulse repeatedly
1070 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
1071 ledOnMS, ledOffMS);
1072 } else {
1073 // pulse only once
1074 mNotificationLight.pulse(ledARGB, ledOnMS);
1075 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001076 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 }
1078
1079 // lock on mNotificationList
Fred Quintana6ecaff12009-09-25 14:23:13 -07001080 private int indexOfNotificationLocked(String pkg, String tag, int id)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 {
1082 ArrayList<NotificationRecord> list = mNotificationList;
1083 final int len = list.size();
1084 for (int i=0; i<len; i++) {
1085 NotificationRecord r = list.get(i);
Fred Quintana6ecaff12009-09-25 14:23:13 -07001086 if (tag == null) {
1087 if (r.tag != null) {
1088 continue;
1089 }
1090 } else {
1091 if (!tag.equals(r.tag)) {
1092 continue;
1093 }
1094 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001095 if (r.id == id && r.pkg.equals(pkg)) {
1096 return i;
1097 }
1098 }
1099 return -1;
1100 }
1101
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001102 // This is here instead of StatusBarPolicy because it is an important
1103 // security feature that we don't want people customizing the platform
1104 // to accidentally lose.
1105 private void updateAdbNotification() {
Mike Lockwoodea8b7d52009-08-04 17:03:15 -04001106 if (mAdbEnabled && mUsbConnected) {
Mike Lockwooded760372009-07-09 07:07:27 -04001107 if ("0".equals(SystemProperties.get("persist.adb.notify"))) {
1108 return;
1109 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001110 if (!mAdbNotificationShown) {
1111 NotificationManager notificationManager = (NotificationManager) mContext
1112 .getSystemService(Context.NOTIFICATION_SERVICE);
1113 if (notificationManager != null) {
1114 Resources r = mContext.getResources();
1115 CharSequence title = r.getText(
1116 com.android.internal.R.string.adb_active_notification_title);
1117 CharSequence message = r.getText(
1118 com.android.internal.R.string.adb_active_notification_message);
1119
1120 if (mAdbNotification == null) {
1121 mAdbNotification = new Notification();
1122 mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_warning;
1123 mAdbNotification.when = 0;
1124 mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;
1125 mAdbNotification.tickerText = title;
1126 mAdbNotification.defaults |= Notification.DEFAULT_SOUND;
1127 }
1128
1129 Intent intent = new Intent(
1130 Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
1131 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1132 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1133 // Note: we are hard-coding the component because this is
1134 // an important security UI that we don't want anyone
1135 // intercepting.
1136 intent.setComponent(new ComponentName("com.android.settings",
1137 "com.android.settings.DevelopmentSettings"));
1138 PendingIntent pi = PendingIntent.getActivity(mContext, 0,
1139 intent, 0);
1140
1141 mAdbNotification.setLatestEventInfo(mContext, title, message, pi);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001142
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001143 mAdbNotificationShown = true;
1144 notificationManager.notify(
1145 com.android.internal.R.string.adb_active_notification_title,
1146 mAdbNotification);
1147 }
1148 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001149
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001150 } else if (mAdbNotificationShown) {
1151 NotificationManager notificationManager = (NotificationManager) mContext
1152 .getSystemService(Context.NOTIFICATION_SERVICE);
1153 if (notificationManager != null) {
1154 mAdbNotificationShown = false;
1155 notificationManager.cancel(
1156 com.android.internal.R.string.adb_active_notification_title);
1157 }
1158 }
1159 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001160
1161 private void updateNotificationPulse() {
1162 synchronized (mNotificationList) {
1163 updateLightsLocked();
1164 }
1165 }
1166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167 // ======================================================================
1168 @Override
1169 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1170 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1171 != PackageManager.PERMISSION_GRANTED) {
1172 pw.println("Permission Denial: can't dump NotificationManager from from pid="
1173 + Binder.getCallingPid()
1174 + ", uid=" + Binder.getCallingUid());
1175 return;
1176 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001178 pw.println("Current Notification Manager state:");
1179
1180 int N;
1181
1182 synchronized (mToastQueue) {
1183 N = mToastQueue.size();
1184 if (N > 0) {
1185 pw.println(" Toast Queue:");
1186 for (int i=0; i<N; i++) {
1187 mToastQueue.get(i).dump(pw, " ");
1188 }
1189 pw.println(" ");
1190 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001191
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001192 }
1193
1194 synchronized (mNotificationList) {
1195 N = mNotificationList.size();
1196 if (N > 0) {
1197 pw.println(" Notification List:");
1198 for (int i=0; i<N; i++) {
1199 mNotificationList.get(i).dump(pw, " ", mContext);
1200 }
1201 pw.println(" ");
1202 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 N = mLights.size();
1205 if (N > 0) {
1206 pw.println(" Lights List:");
1207 for (int i=0; i<N; i++) {
1208 mLights.get(i).dump(pw, " ", mContext);
1209 }
1210 pw.println(" ");
1211 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001212
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213 pw.println(" mSoundNotification=" + mSoundNotification);
1214 pw.println(" mSound=" + mSound);
1215 pw.println(" mVibrateNotification=" + mVibrateNotification);
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001216 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
1217 pw.println(" mSystemReady=" + mSystemReady);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001218 }
1219 }
1220}