diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /services/java/com/android/server/NotificationManagerService.java |
Initial Contribution
Diffstat (limited to 'services/java/com/android/server/NotificationManagerService.java')
-rw-r--r-- | services/java/com/android/server/NotificationManagerService.java | 916 |
1 files changed, 916 insertions, 0 deletions
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java new file mode 100644 index 000000000000..14ac1be39097 --- /dev/null +++ b/services/java/com/android/server/NotificationManagerService.java @@ -0,0 +1,916 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.app.INotificationManager; +import android.app.ITransientNotification; +import android.app.Notification; +import android.app.PendingIntent; +import android.app.StatusBarManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.media.AudioManager; +import android.media.AsyncPlayer; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.BatteryManager; +import android.os.Binder; +import android.os.RemoteException; +import android.os.Handler; +import android.os.Hardware; +import android.os.IBinder; +import android.os.Message; +import android.os.Power; +import android.os.Vibrator; +import android.provider.Settings; +import android.util.Config; +import android.util.EventLog; +import android.util.Log; +import android.widget.Toast; + +import com.android.server.status.IconData; +import com.android.server.status.NotificationData; +import com.android.server.status.StatusBarService; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.io.IOException; + +class NotificationManagerService extends INotificationManager.Stub +{ + private static final String TAG = "NotificationService"; + private static final boolean DBG = false; + + // message codes + private static final int MESSAGE_TIMEOUT = 2; + + private static final int LONG_DELAY = 3500; // 3.5 seconds + private static final int SHORT_DELAY = 2000; // 2 seconds + + private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; + + private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_RING; + + final Context mContext; + final IActivityManager mAm; + final IBinder mForegroundToken = new Binder(); + + private WorkerHandler mHandler; + private StatusBarService mStatusBarService; + + private NotificationRecord mSoundNotification; + private AsyncPlayer mSound; + private int mDisabledNotifications; + + private NotificationRecord mVibrateNotification; + private Vibrator mVibrator = new Vibrator(); + + private ArrayList<NotificationRecord> mNotificationList; + + private ArrayList<ToastRecord> mToastQueue; + + private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); + + private boolean mBatteryCharging; + private boolean mBatteryLow; + private boolean mBatteryFull; + private NotificationRecord mLedNotification; + + // Low battery - red, blinking on 0.125s every 3 seconds + private static final int BATTERY_LOW_ARGB = 0xFFFF0000; + private static final int BATTERY_LOW_ON = 125; + private static final int BATTERY_LOW_OFF = 2875; + + // Charging Low - red solid on + private static final int CHARGING_LOW_ARGB = 0xFFFF0000; + private static final int CHARGING_LOW_ON = 0; + private static final int CHARGING_LOW_OFF = 0; + + // Charging - orange solid on + private static final int CHARGING_ARGB = 0xFFFFFF00; + private static final int CHARGING_ON = 0; + private static final int CHARGING_OFF = 0; + + // Charging Full - green solid on + private static final int CHARGING_FULL_ARGB = 0xFF00FF00; + private static final int CHARGING_FULL_ON = 0; + private static final int CHARGING_FULL_OFF = 0; + + // Tag IDs for EventLog. + private static final int EVENT_LOG_ENQUEUE = 2750; + private static final int EVENT_LOG_CANCEL = 2751; + private static final int EVENT_LOG_CANCEL_ALL = 2752; + + private static String idDebugString(Context baseContext, String packageName, int id) { + Context c = null; + + if (packageName != null) { + try { + c = baseContext.createPackageContext(packageName, 0); + } catch (NameNotFoundException e) { + c = baseContext; + } + } else { + c = baseContext; + } + + String pkg; + String type; + String name; + + Resources r = c.getResources(); + try { + return r.getResourceName(id); + } catch (Resources.NotFoundException e) { + return "<name unknown>"; + } + } + + private static final class NotificationRecord + { + String pkg; + int id; + ITransientNotification callback; + int duration; + Notification notification; + IBinder statusBarKey; + + NotificationRecord(String pkg, int id, Notification notification) + { + this.pkg = pkg; + this.id = id; + this.notification = notification; + } + + void dump(PrintWriter pw, String prefix, Context baseContext) { + pw.println(prefix + this); + pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon) + + " / " + idDebugString(baseContext, this.pkg, notification.icon)); + pw.println(prefix + " contentIntent=" + notification.contentIntent); + pw.println(prefix + " deleteIntent=" + notification.deleteIntent); + pw.println(prefix + " tickerText=" + notification.tickerText); + pw.println(prefix + " contentView=" + notification.contentView); + pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults)); + pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags)); + pw.println(prefix + " sound=" + notification.sound); + pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); + pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB) + + " ledOnMS=" + notification.ledOnMS + + " ledOffMS=" + notification.ledOffMS); + } + + @Override + public final String toString() + { + return "NotificationRecord{" + + Integer.toHexString(System.identityHashCode(this)) + + " pkg=" + pkg + + " id=" + Integer.toHexString(id) + "}"; + } + } + + private static final class ToastRecord + { + final int pid; + final String pkg; + final ITransientNotification callback; + int duration; + + ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) + { + this.pid = pid; + this.pkg = pkg; + this.callback = callback; + this.duration = duration; + } + + void update(int duration) { + this.duration = duration; + } + + void dump(PrintWriter pw, String prefix) { + pw.println(prefix + this); + } + + @Override + public final String toString() + { + return "ToastRecord{" + + Integer.toHexString(System.identityHashCode(this)) + + " pkg=" + pkg + + " callback=" + callback + + " duration=" + duration; + } + } + + private StatusBarService.NotificationCallbacks mNotificationCallbacks + = new StatusBarService.NotificationCallbacks() { + + public void onSetDisabled(int status) { + synchronized (mNotificationList) { + mDisabledNotifications = status; + if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { + // cancel whatever's going on + long identity = Binder.clearCallingIdentity(); + try { + mSound.stop(); + } + finally { + Binder.restoreCallingIdentity(identity); + } + + identity = Binder.clearCallingIdentity(); + try { + mVibrator.cancel(); + } + finally { + Binder.restoreCallingIdentity(identity); + } + } + } + } + + public void onClearAll() { + cancelAll(); + } + + public void onNotificationClick(String pkg, int id) { + cancelNotification(pkg, id, Notification.FLAG_AUTO_CANCEL); + } + + public void onPanelRevealed() { + synchronized (mNotificationList) { + // sound + mSoundNotification = null; + long identity = Binder.clearCallingIdentity(); + try { + mSound.stop(); + } + finally { + Binder.restoreCallingIdentity(identity); + } + + // vibrate + mVibrateNotification = null; + identity = Binder.clearCallingIdentity(); + try { + mVibrator.cancel(); + } + finally { + Binder.restoreCallingIdentity(identity); + } + + // light + mLights.clear(); + mLedNotification = null; + updateLightsLocked(); + } + } + }; + + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { + boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0); + int level = intent.getIntExtra("level", -1); + boolean batteryLow = (level >= 0 && level <= Power.LOW_BATTERY_THRESHOLD); + int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN); + boolean batteryFull = (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90); + + if (batteryCharging != mBatteryCharging || + batteryLow != mBatteryLow || + batteryFull != mBatteryFull) { + mBatteryCharging = batteryCharging; + mBatteryLow = batteryLow; + mBatteryFull = batteryFull; + updateLights(); + } + } + } + }; + + NotificationManagerService(Context context, StatusBarService statusBar) + { + super(); + mContext = context; + mAm = ActivityManagerNative.getDefault(); + mSound = new AsyncPlayer(TAG); + mSound.setUsesWakeLock(context); + mToastQueue = new ArrayList<ToastRecord>(); + mNotificationList = new ArrayList<NotificationRecord>(); + mHandler = new WorkerHandler(); + mStatusBarService = statusBar; + statusBar.setNotificationCallbacks(mNotificationCallbacks); + + // register for battery changed notifications + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + mContext.registerReceiver(mIntentReceiver, filter); + } + + // Toasts + // ============================================================================ + public void enqueueToast(String pkg, ITransientNotification callback, int duration) + { + Log.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration); + + if (pkg == null || callback == null) { + Log.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); + return ; + } + + synchronized (mToastQueue) { + int callingPid = Binder.getCallingPid(); + long callingId = Binder.clearCallingIdentity(); + try { + ToastRecord record; + int index = indexOfToastLocked(pkg, callback); + // If it's already in the queue, we update it in place, we don't + // move it to the end of the queue. + if (index >= 0) { + record = mToastQueue.get(index); + record.update(duration); + } else { + record = new ToastRecord(callingPid, pkg, callback, duration); + mToastQueue.add(record); + index = mToastQueue.size() - 1; + keepProcessAliveLocked(callingPid); + } + // If it's at index 0, it's the current toast. It doesn't matter if it's + // new or just been updated. Call back and tell it to show itself. + // If the callback fails, this will remove it from the list, so don't + // assume that it's valid after this. + if (index == 0) { + showNextToastLocked(); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + } + + public void cancelToast(String pkg, ITransientNotification callback) { + Log.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); + + if (pkg == null || callback == null) { + Log.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); + return ; + } + + synchronized (mToastQueue) { + long callingId = Binder.clearCallingIdentity(); + try { + int index = indexOfToastLocked(pkg, callback); + if (index >= 0) { + cancelToastLocked(index); + } else { + Log.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + } + + private void showNextToastLocked() { + ToastRecord record = mToastQueue.get(0); + while (record != null) { + if (DBG) Log.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); + try { + record.callback.show(); + scheduleTimeoutLocked(record, false); + return; + } catch (RemoteException e) { + Log.w(TAG, "Object died trying to show notification " + record.callback + + " in package " + record.pkg); + // remove it from the list and let the process die + int index = mToastQueue.indexOf(record); + if (index >= 0) { + mToastQueue.remove(index); + } + keepProcessAliveLocked(record.pid); + if (mToastQueue.size() > 0) { + record = mToastQueue.get(0); + } else { + record = null; + } + } + } + } + + private void cancelToastLocked(int index) { + ToastRecord record = mToastQueue.get(index); + try { + record.callback.hide(); + } catch (RemoteException e) { + Log.w(TAG, "Object died trying to hide notification " + record.callback + + " in package " + record.pkg); + // don't worry about this, we're about to remove it from + // the list anyway + } + mToastQueue.remove(index); + keepProcessAliveLocked(record.pid); + if (mToastQueue.size() > 0) { + // Show the next one. If the callback fails, this will remove + // it from the list, so don't assume that the list hasn't changed + // after this point. + showNextToastLocked(); + } + } + + private void scheduleTimeoutLocked(ToastRecord r, boolean immediate) + { + Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); + long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY); + mHandler.removeCallbacksAndMessages(r); + mHandler.sendMessageDelayed(m, delay); + } + + private void handleTimeout(ToastRecord record) + { + if (DBG) Log.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); + synchronized (mToastQueue) { + int index = indexOfToastLocked(record.pkg, record.callback); + if (index >= 0) { + cancelToastLocked(index); + } + } + } + + // lock on mToastQueue + private int indexOfToastLocked(String pkg, ITransientNotification callback) + { + IBinder cbak = callback.asBinder(); + ArrayList<ToastRecord> list = mToastQueue; + int len = list.size(); + for (int i=0; i<len; i++) { + ToastRecord r = list.get(i); + if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { + return i; + } + } + return -1; + } + + // lock on mToastQueue + private void keepProcessAliveLocked(int pid) + { + int toastCount = 0; // toasts from this pid + ArrayList<ToastRecord> list = mToastQueue; + int N = list.size(); + for (int i=0; i<N; i++) { + ToastRecord r = list.get(i); + if (r.pid == pid) { + toastCount++; + } + } + try { + mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); + } catch (RemoteException e) { + // Shouldn't happen. + } + } + + private final class WorkerHandler extends Handler + { + @Override + public void handleMessage(Message msg) + { + switch (msg.what) + { + case MESSAGE_TIMEOUT: + handleTimeout((ToastRecord)msg.obj); + break; + } + } + } + + + // Notifications + // ============================================================================ + public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut) + { + // This conditional is a dirty hack to limit the logging done on + // behalf of the download manager without affecting other apps. + if (!pkg.equals("com.android.providers.downloads") + || Log.isLoggable("DownloadManager", Log.VERBOSE)) { + EventLog.writeEvent(EVENT_LOG_ENQUEUE, pkg, id, notification.toString()); + } + + if (pkg == null || notification == null) { + throw new IllegalArgumentException("null not allowed: pkg=" + pkg + + " id=" + id + " notification=" + notification); + } + if (notification.icon != 0) { + if (notification.contentView == null) { + throw new IllegalArgumentException("contentView required: pkg=" + pkg + + " id=" + id + " notification=" + notification); + } + if (notification.contentIntent == null) { + throw new IllegalArgumentException("contentIntent required: pkg=" + pkg + + " id=" + id + " notification=" + notification); + } + } + + synchronized (mNotificationList) { + NotificationRecord r = new NotificationRecord(pkg, id, notification); + NotificationRecord old = null; + + int index = indexOfNotificationLocked(pkg, id); + if (index < 0) { + mNotificationList.add(r); + } else { + old = mNotificationList.remove(index); + mNotificationList.add(index, r); + } + if (notification.icon != 0) { + IconData icon = IconData.makeIcon(null, pkg, notification.icon, + notification.iconLevel, + notification.number); + CharSequence truncatedTicker = notification.tickerText; + + // TODO: make this restriction do something smarter like never fill + // more than two screens. "Why would anyone need more than 80 characters." :-/ + final int maxTickerLen = 80; + if (truncatedTicker != null && truncatedTicker.length() > maxTickerLen) { + truncatedTicker = truncatedTicker.subSequence(0, maxTickerLen); + } + + NotificationData n = new NotificationData(); + n.id = id; + n.pkg = pkg; + n.when = notification.when; + n.tickerText = truncatedTicker; + n.ongoingEvent = (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0; + if (!n.ongoingEvent && (notification.flags & Notification.FLAG_NO_CLEAR) == 0) { + n.clearable = true; + } + n.contentView = notification.contentView; + n.contentIntent = notification.contentIntent; + n.deleteIntent = notification.deleteIntent; + if (old != null && old.statusBarKey != null) { + r.statusBarKey = old.statusBarKey; + long identity = Binder.clearCallingIdentity(); + try { + mStatusBarService.updateIcon(r.statusBarKey, icon, n); + } + finally { + Binder.restoreCallingIdentity(identity); + } + } else { + long identity = Binder.clearCallingIdentity(); + try { + r.statusBarKey = mStatusBarService.addIcon(icon, n); + } + finally { + Binder.restoreCallingIdentity(identity); + } + } + } else { + if (old != null && old.statusBarKey != null) { + long identity = Binder.clearCallingIdentity(); + try { + mStatusBarService.removeIcon(old.statusBarKey); + } + finally { + Binder.restoreCallingIdentity(identity); + } + } + } + + // If we're not supposed to beep, vibrate, etc. then don't. + if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) + && (!(old != null + && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))) { + // sound + final boolean useDefaultSound = + (notification.defaults & Notification.DEFAULT_SOUND) != 0; + if (useDefaultSound || notification.sound != null) { + Uri uri; + if (useDefaultSound) { + uri = Settings.System.DEFAULT_NOTIFICATION_URI; + } else { + uri = notification.sound; + } + boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; + int audioStreamType; + if (notification.audioStreamType >= 0) { + audioStreamType = notification.audioStreamType; + } else { + audioStreamType = DEFAULT_STREAM_TYPE; + } + mSoundNotification = r; + long identity = Binder.clearCallingIdentity(); + try { + mSound.play(mContext, uri, looping, audioStreamType); + } + finally { + Binder.restoreCallingIdentity(identity); + } + } + + // vibrate + final AudioManager audioManager = (AudioManager) mContext + .getSystemService(Context.AUDIO_SERVICE); + final boolean useDefaultVibrate = + (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; + if ((useDefaultVibrate || notification.vibrate != null) + && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) { + mVibrateNotification = r; + + mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN + : notification.vibrate, + ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); + } + } + + // this option doesn't shut off the lights + + // light + // the most recent thing gets the light + mLights.remove(old); + if (mLedNotification == old) { + mLedNotification = null; + } + //Log.i(TAG, "notification.lights=" + // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); + if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { + mLights.add(r); + updateLightsLocked(); + } else { + if (old != null + && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) { + updateLightsLocked(); + } + } + } + + idOut[0] = id; + } + + private void cancelNotificationLocked(NotificationRecord r) { + // status bar + if (r.notification.icon != 0) { + long identity = Binder.clearCallingIdentity(); + try { + mStatusBarService.removeIcon(r.statusBarKey); + } + finally { + Binder.restoreCallingIdentity(identity); + } + r.statusBarKey = null; + } + + // sound + if (mSoundNotification == r) { + mSoundNotification = null; + long identity = Binder.clearCallingIdentity(); + try { + mSound.stop(); + } + finally { + Binder.restoreCallingIdentity(identity); + } + } + + // vibrate + if (mVibrateNotification == r) { + mVibrateNotification = null; + long identity = Binder.clearCallingIdentity(); + try { + mVibrator.cancel(); + } + finally { + Binder.restoreCallingIdentity(identity); + } + } + + // light + mLights.remove(r); + if (mLedNotification == r) { + mLedNotification = null; + } + } + + /** + * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}. + */ + private void cancelNotification(String pkg, int id, int mustHaveFlags) { + EventLog.writeEvent(EVENT_LOG_CANCEL, pkg, id, mustHaveFlags); + + synchronized (mNotificationList) { + NotificationRecord r = null; + + int index = indexOfNotificationLocked(pkg, id); + if (index >= 0) { + r = mNotificationList.get(index); + + if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { + return; + } + + mNotificationList.remove(index); + + cancelNotificationLocked(r); + updateLightsLocked(); + } + } + } + + /** + * Cancels all notifications from a given package that have all of the + * {@code mustHaveFlags}. + */ + private void cancelAllNotificationsInt(String pkg, int mustHaveFlags) { + EventLog.writeEvent(EVENT_LOG_CANCEL_ALL, pkg, mustHaveFlags); + + synchronized (mNotificationList) { + final int N = mNotificationList.size(); + boolean canceledSomething = false; + for (int i = N-1; i >= 0; --i) { + NotificationRecord r = mNotificationList.get(i); + if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { + continue; + } + if (!r.pkg.equals(pkg)) { + continue; + } + mNotificationList.remove(i); + cancelNotificationLocked(r); + canceledSomething = true; + } + if (canceledSomething) { + updateLightsLocked(); + } + } + } + + + public void cancelNotification(String pkg, int id) + { + cancelNotification(pkg, id, 0); + } + + public void cancelAllNotifications(String pkg) + { + cancelAllNotificationsInt(pkg, 0); + } + + public void cancelAll() { + synchronized (mNotificationList) { + final int N = mNotificationList.size(); + for (int i=N-1; i>=0; i--) { + NotificationRecord r = mNotificationList.get(i); + + if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT + | Notification.FLAG_NO_CLEAR)) == 0) { + if (r.notification.deleteIntent != null) { + try { + r.notification.deleteIntent.send(); + } catch (PendingIntent.CanceledException ex) { + // do nothing - there's no relevant way to recover, and + // no reason to let this propagate + Log.w(TAG, "canceled PendingIntent for " + r.pkg, ex); + } + } + mNotificationList.remove(i); + cancelNotificationLocked(r); + } + } + + updateLightsLocked(); + } + } + + private void updateLights() { + synchronized (mNotificationList) { + updateLightsLocked(); + } + } + + // lock on mNotificationList + private void updateLightsLocked() + { + // battery low has highest priority, then charging + if (mBatteryLow && !mBatteryCharging) { + Hardware.setLedState(BATTERY_LOW_ARGB, BATTERY_LOW_ON, BATTERY_LOW_OFF); + } else if (mBatteryCharging) { + if (mBatteryLow) { + Hardware.setLedState(CHARGING_LOW_ARGB, CHARGING_LOW_ON, CHARGING_LOW_OFF); + } else if (mBatteryFull) { + Hardware.setLedState(CHARGING_FULL_ARGB, CHARGING_FULL_ON, CHARGING_FULL_OFF); + } else { + Hardware.setLedState(CHARGING_ARGB, CHARGING_ON, CHARGING_OFF); + } + } else { + // handle notification lights + if (mLedNotification == null) { + // get next notification, if any + int n = mLights.size(); + if (n > 0) { + mLedNotification = mLights.get(n-1); + } + } + + if (mLedNotification == null) { + Hardware.setLedState(0, 0, 0); + } else { + Hardware.setLedState(mLedNotification.notification.ledARGB, + mLedNotification.notification.ledOnMS, + mLedNotification.notification.ledOffMS); + } + } + } + + // lock on mNotificationList + private int indexOfNotificationLocked(String pkg, int id) + { + ArrayList<NotificationRecord> list = mNotificationList; + final int len = list.size(); + for (int i=0; i<len; i++) { + NotificationRecord r = list.get(i); + if (r.id == id && r.pkg.equals(pkg)) { + return i; + } + } + return -1; + } + + // ====================================================================== + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingPermission("android.permission.DUMP") + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump NotificationManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + pw.println("Current Notification Manager state:"); + + int N; + + synchronized (mToastQueue) { + N = mToastQueue.size(); + if (N > 0) { + pw.println(" Toast Queue:"); + for (int i=0; i<N; i++) { + mToastQueue.get(i).dump(pw, " "); + } + pw.println(" "); + } + + } + + synchronized (mNotificationList) { + N = mNotificationList.size(); + if (N > 0) { + pw.println(" Notification List:"); + for (int i=0; i<N; i++) { + mNotificationList.get(i).dump(pw, " ", mContext); + } + pw.println(" "); + } + + N = mLights.size(); + if (N > 0) { + pw.println(" Lights List:"); + for (int i=0; i<N; i++) { + mLights.get(i).dump(pw, " ", mContext); + } + pw.println(" "); + } + + pw.println(" mSoundNotification=" + mSoundNotification); + pw.println(" mSound=" + mSound); + pw.println(" mVibrateNotification=" + mVibrateNotification); + } + } +} |