设为首页 - 加入收藏 焦点技术网
热搜:java
当前位置:首页 >

Android中Notification的framework层讲解【安卓源码解析四】

导读:             android的notificaiton的声音sound也是申请的AudioManager机制来播放声音的。最近让我找恢复出厂设置后,手机刚启动,接受短信没有声音,如果恢复出厂设置后,等一会儿,过个2分钟再接受短信,就有铃声了。下面我把我分析代码的方法写下来,给自己和读者一些启发:      日历也是用的是Notification,但是恢复出厂设置后,立马设置日历后,日历...。。。

             android的notificaiton的声音sound也是申请的AudioManager机制来播放声音的。最近让我找恢复出厂设置后,手机刚启动,接受短信没有声音,如果恢复出厂设置后,等一会儿,过个2分钟再接受短信,就有铃声了。下面我把我分析代码的方法写下来,给自己和读者一些启发:

      日历也是用的是Notification,但是恢复出厂设置后,立马设置日历后,日历可以出声音,我看日历的代码,结果发现日历只是用了Notification的闪屏,真正声音是日历自己实现了Mediaplayer来出声音的。所以我又不得不老老实实地研究Notification.sound到底把声音传递到什么地方去了?

       转载请标明出处:http://blog.csdn.net/wdaming1986/article/details/7081787

       首先我在短信的com.android.mms.transaction包中的MessagingNotification的573行的java代码:notification.sound = TextUtils.isEmpty(ringtoneStr) ? null : Uri.parse(ringtoneStr);打log查看,发现这个uri确实是存在的,我推测:就是说这个uri传给了framework一层,但是framework一层有没有执行完的动作,所以不响。为了验证是不是短信的错误,我自己单独写了个notification的例子,一个按钮,点击就发出声音。这个例子在正常情况下能正常发声。我就恢复出厂设置后,运行这个例子,结果发现没有声音,这就充分验证了我的猜测。我就去framework去着notificaion.sound = 的声音传递给framework做什么事情了??

       接着,在Source Insight软件中导入framework整个工程,然后搜索,notificaiton.sounds,结果搜到了,在framework/base/core/java/android/app/Notification.java类。

 

/* * 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 android.app;import java.util.Date;import android.app.PendingIntent;import android.content.Context;import android.content.Intent;import android.media.AudioManager;import android.net.Uri;import android.os.Parcel;import android.os.Parcelable;import android.text.TextUtils;import android.text.format.DateFormat;import android.text.format.DateUtils;import android.widget.RemoteViews;/** * A class that represents how a persistent notification is to be presented to * the user using the {@link android.app.NotificationManager}. * * 

For a guide to creating notifications, see the * Creating Status * Bar Notifications document in the Dev Guide.

*/public class Notification implements Parcelable{ /** * Use all default values (where applicable). */ public static final int DEFAULT_ALL = ~0; /** * Use the default notification sound. This will ignore any given * {@link #sound}. * * @see #defaults */ public static final int DEFAULT_SOUND = 1; /** * Use the default notification vibrate. This will ignore any given * {@link #vibrate}. Using phone vibration requires the * {@link android.Manifest.permission#VIBRATE VIBRATE} permission. * * @see #defaults */ public static final int DEFAULT_VIBRATE = 2; /** * Use the default notification lights. This will ignore the * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or * {@link #ledOnMS}. * * @see #defaults */ public static final int DEFAULT_LIGHTS = 4; /** * The timestamp for the notification. The icons and expanded views * are sorted by this key. */ public long when; /** * The resource id of a drawable to use as the icon in the status bar. */ public int icon; /** * The number of events that this notification represents. For example, in a new mail * notification, this could be the number of unread messages. This number is superimposed over * the icon in the status bar. If the number is 0 or negative, it is not shown in the status * bar. */ public int number; /** * The intent to execute when the expanded status entry is clicked. If * this is an activity, it must include the * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires * that you take care of task management as described in the Activities and Tasks * section of the Application * Fundamentals document. */ public PendingIntent contentIntent; /** * The intent to execute when the status entry is deleted by the user * with the "Clear All Notifications" button. This probably shouldn't * be launching an activity since several of those will be sent at the * same time. */ public PendingIntent deleteIntent; /** * An intent to launch instead of posting the notification to the status bar. * Only for use with extremely high-priority notifications demanding the user's * immediate attention, such as an incoming phone call or * alarm clock that the user has explicitly set to a particular time. * If this facility is used for something else, please give the user an option * to turn it off and use a normal notification, as this can be extremely * disruptive. */ public PendingIntent fullScreenIntent; /** * Text to scroll across the screen when this item is added to * the status bar. */ public CharSequence tickerText; /** * The view that will represent this notification in the expanded status bar. */ public RemoteViews contentView; /** * If the icon in the status bar is to have more than one level, you can set this. Otherwise, * leave it at its default value of 0. * * @see android.widget.ImageView#setImageLevel * @see android.graphics.drawable#setLevel */ public int iconLevel; /** * The sound to play. * *

* To play the default notification sound, see {@link #defaults}. *

*/ public Uri sound; /** * Use this constant as the value for audioStreamType to request that * the default stream type for notifications be used. Currently the * default stream type is STREAM_RING. */ public static final int STREAM_DEFAULT = -1; /** * The audio stream type to use when playing the sound. * Should be one of the STREAM_ constants from * {@link android.media.AudioManager}. */ public int audioStreamType = STREAM_DEFAULT; /** * The pattern with which to vibrate. * *

* To vibrate the default pattern, see {@link #defaults}. *

* * @see android.os.Vibrator#vibrate(long[],int) */ public long[] vibrate; /** * The color of the led. The hardware will do its best approximation. * * @see #FLAG_SHOW_LIGHTS * @see #flags */ public int ledARGB; /** * The number of milliseconds for the LED to be on while it's flashing. * The hardware will do its best approximation. * * @see #FLAG_SHOW_LIGHTS * @see #flags */ public int ledOnMS; /** * The number of milliseconds for the LED to be off while it's flashing. * The hardware will do its best approximation. * * @see #FLAG_SHOW_LIGHTS * @see #flags */ public int ledOffMS; /** * Specifies which values should be taken from the defaults. *

* To set, OR the desired from {@link #DEFAULT_SOUND}, * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default * values, use {@link #DEFAULT_ALL}. *

*/ public int defaults; /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if you want the LED on for this notification. *
    *
  • To turn the LED off, pass 0 in the alpha channel for colorARGB * or 0 for both ledOnMS and ledOffMS.
  • *
  • To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.
  • *
  • To flash the LED, pass the number of milliseconds that it should * be on and off to ledOnMS and ledOffMS.
  • *
*

* Since hardware varies, you are not guaranteed that any of the values * you pass are honored exactly. Use the system defaults (TODO) if possible * because they will be set to values that work on any given hardware. *

* The alpha channel must be set for forward compatibility. * */ public static final int FLAG_SHOW_LIGHTS = 0x00000001; /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if this notification is in reference to something that is ongoing, * like a phone call. It should not be set if this notification is in * reference to something that happened at a particular point in time, * like a missed phone call. */ public static final int FLAG_ONGOING_EVENT = 0x00000002; /** * Bit to be bitwise-ored into the {@link #flags} field that if set, * the audio will be repeated until the notification is * cancelled or the notification window is opened. */ public static final int FLAG_INSISTENT = 0x00000004; /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if you want the sound and/or vibration play each time the * notification is sent, even if it has not been canceled before that. */ public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008; /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if the notification should be canceled when it is clicked by the * user. */ public static final int FLAG_AUTO_CANCEL = 0x00000010; /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if the notification should not be canceled when the user clicks * the Clear all button. */ public static final int FLAG_NO_CLEAR = 0x00000020; /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if this notification represents a currently running service. This * will normally be set for you by {@link Service#startForeground}. */ public static final int FLAG_FOREGROUND_SERVICE = 0x00000040; public int flags; /** * Constructs a Notification object with everything set to 0. */ public Notification() { this.when = System.currentTimeMillis(); } /** * @deprecated use {@link #Notification(int,CharSequence,long)} and {@link #setLatestEventInfo}. * @hide */ public Notification(Context context, int icon, CharSequence tickerText, long when, CharSequence contentTitle, CharSequence contentText, Intent contentIntent) { this.when = when; this.icon = icon; this.tickerText = tickerText; setLatestEventInfo(context, contentTitle, contentText, PendingIntent.getActivity(context, 0, contentIntent, 0)); } /** * Constructs a Notification object with the information needed to * have a status bar icon without the standard expanded view. * * @param icon The resource id of the icon to put in the status bar. * @param tickerText The text that flows by in the status bar when the notification first * activates. * @param when The time to show in the time field. In the System.currentTimeMillis * timebase. */ public Notification(int icon, CharSequence tickerText, long when) { this.icon = icon; this.tickerText = tickerText; this.when = when; } /** * Unflatten the notification from a parcel. */ public Notification(Parcel parcel) { int version = parcel.readInt(); when = parcel.readLong(); icon = parcel.readInt(); number = parcel.readInt(); if (parcel.readInt() != 0) { contentIntent = PendingIntent.CREATOR.createFromParcel(parcel); } if (parcel.readInt() != 0) { deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel); } if (parcel.readInt() != 0) { tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); } if (parcel.readInt() != 0) { contentView = RemoteViews.CREATOR.createFromParcel(parcel); } defaults = parcel.readInt(); flags = parcel.readInt(); if (parcel.readInt() != 0) { sound = Uri.CREATOR.createFromParcel(parcel); } audioStreamType = parcel.readInt(); vibrate = parcel.createLongArray(); ledARGB = parcel.readInt(); ledOnMS = parcel.readInt(); ledOffMS = parcel.readInt(); iconLevel = parcel.readInt(); if (parcel.readInt() != 0) { fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel); } } public Notification clone() { Notification that = new Notification(); that.when = this.when; that.icon = this.icon; that.number = this.number; // PendingIntents are global, so there's no reason (or way) to clone them. that.contentIntent = this.contentIntent; that.deleteIntent = this.deleteIntent; that.fullScreenIntent = this.fullScreenIntent; if (this.tickerText != null) { that.tickerText = this.tickerText.toString(); } if (this.contentView != null) { that.contentView = this.contentView.clone(); } that.iconLevel = that.iconLevel; that.sound = this.sound; // android.net.Uri is immutable that.audioStreamType = this.audioStreamType; final long[] vibrate = this.vibrate; if (vibrate != null) { final int N = vibrate.length; final long[] vib = that.vibrate = new long[N]; System.arraycopy(vibrate, 0, vib, 0, N); } that.ledARGB = this.ledARGB; that.ledOnMS = this.ledOnMS; that.ledOffMS = this.ledOffMS; that.defaults = this.defaults; that.flags = this.flags; return that; } public int describeContents() { return 0; } /** * Flatten this notification from a parcel. */ public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(1); parcel.writeLong(when); parcel.writeInt(icon); parcel.writeInt(number); if (contentIntent != null) { parcel.writeInt(1); contentIntent.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } if (deleteIntent != null) { parcel.writeInt(1); deleteIntent.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } if (tickerText != null) { parcel.writeInt(1); TextUtils.writeToParcel(tickerText, parcel, flags); } else { parcel.writeInt(0); } if (contentView != null) { parcel.writeInt(1); contentView.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } parcel.writeInt(defaults); parcel.writeInt(this.flags); if (sound != null) { parcel.writeInt(1); sound.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } parcel.writeInt(audioStreamType); parcel.writeLongArray(vibrate); parcel.writeInt(ledARGB); parcel.writeInt(ledOnMS); parcel.writeInt(ledOffMS); parcel.writeInt(iconLevel); if (fullScreenIntent != null) { parcel.writeInt(1); fullScreenIntent.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } } /** * Parcelable.Creator that instantiates Notification objects */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Notification createFromParcel(Parcel parcel) { return new Notification(parcel); } public Notification[] newArray(int size) { return new Notification[size]; } }; /** * Sets the {@link #contentView} field to be a view with the standard "Latest Event" * layout. * *

Uses the {@link #icon} and {@link #when} fields to set the icon and time fields * in the view.

* @param context The context for your application / activity. * @param contentTitle The title that goes in the expanded entry. * @param contentText The text that goes in the expanded entry. * @param contentIntent The intent to launch when the user clicks the expanded notification. * If this is an activity, it must include the * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires * that you take care of task management as described in * Application Fundamentals: Activities and Tasks. */ public void setLatestEventInfo(Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) { RemoteViews contentView = new RemoteViews(context.getPackageName(), com.android.internal.R.layout.status_bar_latest_event_content); if (this.icon != 0) { contentView.setImageViewResource(com.android.internal.R.id.icon, this.icon); } if (contentTitle != null) { contentView.setTextViewText(com.android.internal.R.id.title, contentTitle); } if (contentText != null) { contentView.setTextViewText(com.android.internal.R.id.text, contentText); } if (this.when != 0) { contentView.setLong(com.android.internal.R.id.time, "setTime", when); } this.contentView = contentView; this.contentIntent = contentIntent; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Notification(vibrate="); if (this.vibrate != null) { int N = this.vibrate.length-1; sb.append("["); for (int i=0; i


在344行, sound = Uri.CREATOR.createFromParcel(parcel);回来经过打log发现,问题的关键不再这个类中。

          再次,改变方向,换条思路走,找notification的服务类,看有什么新的发现,在framework/base/services/java/com/android/server/NotificationManagerService.java类中,真的找到了我需要的,

/* * 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 com.android.internal.statusbar.StatusBarNotification;import com.android.server.StatusBarManagerService;import android.app.ActivityManagerNative;import android.app.IActivityManager;import android.app.INotificationManager;import android.app.ITransientNotification;import android.app.Notification;import android.app.NotificationManager;import android.app.PendingIntent;import android.app.StatusBarManager;import android.content.BroadcastReceiver;import android.content.ComponentName;import android.content.ContentResolver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.content.pm.ApplicationInfo;import android.content.pm.PackageManager;import android.content.pm.PackageManager.NameNotFoundException;import android.content.res.Resources;import android.database.ContentObserver;import android.hardware.usb.UsbManager;import android.media.AudioManager;import android.net.Uri;import android.os.BatteryManager;import android.os.Bundle;import android.os.Binder;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.os.Power;import android.os.Process;import android.os.RemoteException;import android.os.SystemProperties;import android.os.Vibrator;import android.os.SystemClock;import android.provider.Settings;import android.telephony.TelephonyManager;import android.text.TextUtils;import android.util.EventLog;import android.util.Slog;import android.util.Log;import android.view.accessibility.AccessibilityEvent;import android.view.accessibility.AccessibilityManager;import android.widget.Toast;import java.io.FileDescriptor;import java.io.PrintWriter;import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;/** {@hide} */public class NotificationManagerService extends INotificationManager.Stub{    private static final String TAG = "NotificationService";    private static final boolean DBG = false;    private static final int MAX_PACKAGE_NOTIFICATIONS = 50;    private static final int NOTIFICATION_REQUEST_INTERVAL = 30000; // 30 seconds    private static final int MAX_PACKAGE_NOTIFICATION_REQUESTS = 500;    // 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_NOTIFICATION;    final Context mContext;    final IActivityManager mAm;    final IBinder mForegroundToken = new Binder();    private WorkerHandler mHandler;    private StatusBarManagerService mStatusBar;    private LightsService mLightsService;    private LightsService.Light mBatteryLight;    private LightsService.Light mNotificationLight;    private LightsService.Light mAttentionLight;    private int mDefaultNotificationColor;    private int mDefaultNotificationLedOn;    private int mDefaultNotificationLedOff;    private NotificationRecord mSoundNotification;    private NotificationPlayer mSound;    private boolean mSystemReady;    private int mDisabledNotifications;    private NotificationRecord mVibrateNotification;    private Vibrator mVibrator = new Vibrator();    // for enabling and disabling notification pulse behavior    private boolean mScreenOn = true;    private boolean mInCall = false;    private boolean mNotificationPulseEnabled;    // This is true if we have received a new notification while the screen is off    // (that is, if mLedNotification was set while the screen was off)    // This is reset to false when the screen is turned on.    private boolean mPendingPulseNotification;    // for adb connected notifications    private boolean mAdbNotificationShown = false;    private Notification mAdbNotification;    private final ArrayList mNotificationList =            new ArrayList();    private class PackageRequestInfo {        int requestCount;        long lastPostTime;    }    private final HashMap mPackageInfo =            new HashMap();    private ArrayList mToastQueue;    private ArrayList mLights = new ArrayList();    private boolean mBatteryCharging;    private boolean mBatteryLow;    private boolean mBatteryFull;    private NotificationRecord mLedNotification;    private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on    private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00;    // Charging - orange solid on    private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on    private static final int BATTERY_BLINK_ON = 125;    private static final int BATTERY_BLINK_OFF = 2875;    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 "";        }    }    private static final class NotificationRecord    {        final String pkg;        final String tag;        final int id;        final int uid;        final int initialPid;        ITransientNotification callback;        int duration;        final Notification notification;        IBinder statusBarKey;        NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,                Notification notification)        {            this.pkg = pkg;            this.tag = tag;            this.id = id;            this.uid = uid;            this.initialPid = initialPid;            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)                + " tag=" + tag + "}";        }    }    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 StatusBarManagerService.NotificationCallbacks mNotificationCallbacks            = new StatusBarManagerService.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, String tag, int id) {            cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,                    Notification.FLAG_FOREGROUND_SERVICE);        }        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();            }        }        public void onNotificationError(String pkg, String tag, int id,                int uid, int initialPid, String message) {            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");            cancelNotification(pkg, tag, id, 0, 0);            long ident = Binder.clearCallingIdentity();            try {                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,                        "Bad notification posted from package " + pkg                        + ": " + message);            } catch (RemoteException e) {            }            Binder.restoreCallingIdentity(ident);        }    };    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            String action = intent.getAction();            boolean queryRestart = false;                        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();                }            } else if (action.equals(UsbManager.ACTION_USB_STATE)) {                Bundle extras = intent.getExtras();                boolean usbConnected = extras.getBoolean(UsbManager.USB_CONNECTED);                boolean adbEnabled = (UsbManager.USB_FUNCTION_ENABLED.equals(                                    extras.getString(UsbManager.USB_FUNCTION_ADB)));                updateAdbNotification(usbConnected && adbEnabled);            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {                String pkgList[] = null;                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);                } else if (queryRestart) {                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);                } else {                    Uri uri = intent.getData();                    if (uri == null) {                        return;                    }                    String pkgName = uri.getSchemeSpecificPart();                    if (pkgName == null) {                        return;                    }                    pkgList = new String[]{pkgName};                }                if (pkgList != null && (pkgList.length > 0)) {                    for (String pkgName : pkgList) {                        cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);                    }                }            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {                mScreenOn = true;                updateNotificationPulse();            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {                mScreenOn = false;                updateNotificationPulse();            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {                mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK));                updateNotificationPulse();            }        }    };    class SettingsObserver extends ContentObserver {        SettingsObserver(Handler handler) {            super(handler);        }        void observe() {            ContentResolver resolver = mContext.getContentResolver();            resolver.registerContentObserver(Settings.System.getUriFor(                    Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);            update();        }        @Override public void onChange(boolean selfChange) {            update();        }        public void update() {            ContentResolver resolver = mContext.getContentResolver();            boolean pulseEnabled = Settings.System.getInt(resolver,                        Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;            if (mNotificationPulseEnabled != pulseEnabled) {                mNotificationPulseEnabled = pulseEnabled;                updateNotificationPulse();            }        }    }    NotificationManagerService(Context context, StatusBarManagerService statusBar,            LightsService lights)    {        super();        mContext = context;        mLightsService = lights;        mAm = ActivityManagerNative.getDefault();        mSound = new NotificationPlayer(TAG);        mSound.setUsesWakeLock(context);        mToastQueue = new ArrayList();        mHandler = new WorkerHandler();        mStatusBar = statusBar;        statusBar.setNotificationCallbacks(mNotificationCallbacks);        mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);        mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);        mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);        Resources resources = mContext.getResources();        mDefaultNotificationColor = resources.getColor(                com.android.internal.R.color.config_defaultNotificationColor);        mDefaultNotificationLedOn = resources.getInteger(                com.android.internal.R.integer.config_defaultNotificationLedOn);        mDefaultNotificationLedOff = resources.getInteger(                com.android.internal.R.integer.config_defaultNotificationLedOff);        // Don't start allowing notifications until the setup wizard has run once.        // After that, including subsequent boots, init with notifications turned on.        // This works on the first boot because the setup wizard will toggle this        // flag at least once and we'll go back to 0 after that.        if (0 == Settings.Secure.getInt(mContext.getContentResolver(),                    Settings.Secure.DEVICE_PROVISIONED, 0)) {            mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;        }        // register for battery changed notifications        IntentFilter filter = new IntentFilter();        filter.addAction(Intent.ACTION_BATTERY_CHANGED);        filter.addAction(UsbManager.ACTION_USB_STATE);        filter.addAction(Intent.ACTION_SCREEN_ON);        filter.addAction(Intent.ACTION_SCREEN_OFF);        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);        mContext.registerReceiver(mIntentReceiver, filter);        IntentFilter pkgFilter = new IntentFilter();        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);        pkgFilter.addDataScheme("package");        mContext.registerReceiver(mIntentReceiver, pkgFilter);        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);        mContext.registerReceiver(mIntentReceiver, sdFilter);        SettingsObserver observer = new SettingsObserver(mHandler);        observer.observe();    }    void systemReady() {        // no beeping until we're basically done booting        mSystemReady = true;    }    // Toasts    // ============================================================================    public void enqueueToast(String pkg, ITransientNotification callback, int duration)    {        if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);        if (pkg == null || callback == null) {            Slog.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 {                    // Limit the number of toasts that any given package except the android                    // package can enqueue.  Prevents DOS attacks and deals with leaks.                    if (!"android".equals(pkg)) {                        int count = 0;                        final int N = mToastQueue.size();                        for (int i=0; i= MAX_PACKAGE_NOTIFICATIONS) {                                     Slog.e(TAG, "Package has already posted " + count                                            + " toasts. Not showing more. Package=" + pkg);                                     return;                                 }                             }                        }                    }                    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) {        Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);        if (pkg == null || callback == null) {            Slog.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 {                    Slog.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) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);            try {                record.callback.show();                scheduleTimeoutLocked(record, false);                return;            } catch (RemoteException e) {                Slog.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) {            Slog.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) Slog.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 list = mToastQueue;        int len = list.size();        for (int i=0; i list = mToastQueue;        int N = list.size();        for (int i=0; i 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)    {        enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);    }    public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,            int[] idOut)    {        enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),                tag, id, notification, idOut);    }    // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the    // uid/pid of another application)    public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,            String tag, int id, Notification notification, int[] idOut)    {        checkIncomingCall(pkg);        // Limit the number of notifications that any given package except the android        // package can enqueue.  Prevents DOS attacks and deals with leaks.        if (!"android".equals(pkg)) {            synchronized (mNotificationList) {                int count = 0;                final int N = mNotificationList.size();                for (int i=0; i= MAX_PACKAGE_NOTIFICATIONS) {                            Slog.e(TAG, "Package has already posted " + count                                    + " notifications.  Not showing more.  package=" + pkg);                            return;                        }                    }                }            }        }        // Limit the number of notification requests, notify and cancel that        // a package can post. The MAX_PACKAGE_NOTIFICATIONS doesn't work for        // packages which notify and quickly cancel it and do this in an        // iteration        if (!"android".equals(pkg)) {            synchronized (mPackageInfo) {                if (!mPackageInfo.containsKey(pkg)) {                    final PackageRequestInfo pInfo = new PackageRequestInfo();                    pInfo.requestCount = 1;                    pInfo.lastPostTime = SystemClock.elapsedRealtime();                    mPackageInfo.put(pkg,pInfo);                }                else {                    final PackageRequestInfo pInfo = mPackageInfo.get(pkg);                    final long currentTime = SystemClock.elapsedRealtime();                    if ((currentTime - pInfo.lastPostTime) <= NOTIFICATION_REQUEST_INTERVAL) {                         // Keep track of requests posted within last 30 seconds                         pInfo.requestCount++;                    }                    else {                         pInfo.requestCount = 1;                         pInfo.lastPostTime = SystemClock.elapsedRealtime();                    }                    if (pInfo.requestCount >= MAX_PACKAGE_NOTIFICATION_REQUESTS) {                        // 500 requests within a span of 30 seconds is high                        if (pInfo.requestCount%MAX_PACKAGE_NOTIFICATION_REQUESTS == 0) {                            Slog.e(TAG, "Package has already posted too many notifications. "                                    + "Not showing more.  package=" + pkg);                        }                        return;                    }                }            }        }        // 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(EventLogTags.NOTIFICATION_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, tag, id,                    callingUid, callingPid, notification);            NotificationRecord old = null;            int index = indexOfNotificationLocked(pkg, tag, id);            if (index < 0) {                mNotificationList.add(r);            } else {                old = mNotificationList.remove(index);                mNotificationList.add(index, r);                // Make sure we don't lose the foreground service state.                if (old != null) {                    notification.flags |=                        old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;                }            }            // Ensure if this is a foreground service that the proper additional            // flags are set.            if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {                notification.flags |= Notification.FLAG_ONGOING_EVENT                        | Notification.FLAG_NO_CLEAR;            }            if (notification.icon != 0) {                StatusBarNotification n = new StatusBarNotification(pkg, id, tag,                        r.uid, r.initialPid, notification);                if (old != null && old.statusBarKey != null) {                    r.statusBarKey = old.statusBarKey;                    long identity = Binder.clearCallingIdentity();                    try {                        mStatusBar.updateNotification(r.statusBarKey, n);                    }                    finally {                        Binder.restoreCallingIdentity(identity);                    }                } else {                    long identity = Binder.clearCallingIdentity();                    try {                        r.statusBarKey = mStatusBar.addNotification(n);                        mAttentionLight.pulse();                    }                    finally {                        Binder.restoreCallingIdentity(identity);                    }                }                sendAccessibilityEvent(notification, pkg);            } else {                if (old != null && old.statusBarKey != null) {                    long identity = Binder.clearCallingIdentity();                    try {                        mStatusBar.removeNotification(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 ))                    && mSystemReady) {                final AudioManager audioManager = (AudioManager) mContext                .getSystemService(Context.AUDIO_SERVICE);                // 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;                    // do not play notifications if stream volume is 0                    // (typically because ringer mode is silent).                    if (audioManager.getStreamVolume(audioStreamType) != 0) {                        long identity = Binder.clearCallingIdentity();                        try {                            mSound.play(mContext, uri, looping, audioStreamType);                        }                        finally {                            Binder.restoreCallingIdentity(identity);                        }                    }                }                // vibrate                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;            }            //Slog.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 sendAccessibilityEvent(Notification notification, CharSequence packageName) {        AccessibilityManager manager = AccessibilityManager.getInstance(mContext);        if (!manager.isEnabled()) {            return;        }        AccessibilityEvent event =            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);        event.setPackageName(packageName);        event.setClassName(Notification.class.getName());        event.setParcelableData(notification);        CharSequence tickerText = notification.tickerText;        if (!TextUtils.isEmpty(tickerText)) {            event.getText().add(tickerText);        }        manager.sendAccessibilityEvent(event);    }    private void cancelNotificationLocked(NotificationRecord r) {        // status bar        if (r.notification.icon != 0) {            long identity = Binder.clearCallingIdentity();            try {                mStatusBar.removeNotification(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}     * and none of the {@code mustNotHaveFlags}.     */    private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,            int mustNotHaveFlags) {        EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags);        synchronized (mNotificationList) {            int index = indexOfNotificationLocked(pkg, tag, id);            if (index >= 0) {                NotificationRecord r = mNotificationList.get(index);                if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {                    return;                }                if ((r.notification.flags & mustNotHaveFlags) != 0) {                    return;                }                mNotificationList.remove(index);                cancelNotificationLocked(r);                updateLightsLocked();            }        }    }    /**     * Cancels all notifications from a given package that have all of the     * {@code mustHaveFlags}.     */    boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,            int mustNotHaveFlags, boolean doit) {        EventLog.writeEvent(EventLogTags.NOTIFICATION_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.notification.flags & mustNotHaveFlags) != 0) {                    continue;                }                if (!r.pkg.equals(pkg)) {                    continue;                }                canceledSomething = true;                if (!doit) {                    return true;                }                mNotificationList.remove(i);                cancelNotificationLocked(r);            }            if (canceledSomething) {                updateLightsLocked();            }            return canceledSomething;        }    }    public void cancelNotification(String pkg, int id) {        cancelNotificationWithTag(pkg, null /* tag */, id);    }    public void cancelNotificationWithTag(String pkg, String tag, int id) {        checkIncomingCall(pkg);        // Limit the number of notification requests, notify and cancel that        // a package can post. The MAX_PACKAGE_NOTIFICATIONS doesn't work for        // packages which notify and quickly cancel it and do this in an        // iteration        synchronized (mPackageInfo) {            if (!"android".equals(pkg) && mPackageInfo.containsKey(pkg)) {                final PackageRequestInfo pInfo = mPackageInfo.get(pkg);                final long currentTime = SystemClock.elapsedRealtime();                if ((currentTime - pInfo.lastPostTime) <= NOTIFICATION_REQUEST_INTERVAL) {                    // Keep track of requests posted within last 30 seconds                    pInfo.requestCount++;                }                else {                    pInfo.requestCount = 1;                    pInfo.lastPostTime = SystemClock.elapsedRealtime();                }            }        }        // Don't allow client applications to cancel foreground service notis.        cancelNotification(pkg, tag, id, 0,                Binder.getCallingUid() == Process.SYSTEM_UID                ? 0 : Notification.FLAG_FOREGROUND_SERVICE);    }    public void cancelAllNotifications(String pkg) {        checkIncomingCall(pkg);        // Calling from user space, don't allow the canceling of actively        // running foreground services.        cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true);    }    void checkIncomingCall(String pkg) {        int uid = Binder.getCallingUid();        if (uid == Process.SYSTEM_UID || uid == 0) {            return;        }        try {            ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(                    pkg, 0);            if (ai.uid != uid) {                throw new SecurityException("Calling uid " + uid + " gave package"                        + pkg + " which is owned by uid " + ai.uid);            }        } catch (PackageManager.NameNotFoundException e) {            throw new SecurityException("Unknown package " + pkg);        }    }    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                            Slog.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 always shows, other states only show if charging.        if (mBatteryLow) {            if (mBatteryCharging) {                mBatteryLight.setColor(BATTERY_LOW_ARGB);            } else {                // Flash when battery is low and not charging                mBatteryLight.setFlashing(BATTERY_LOW_ARGB, LightsService.LIGHT_FLASH_TIMED,                        BATTERY_BLINK_ON, BATTERY_BLINK_OFF);            }        } else if (mBatteryCharging) {            if (mBatteryFull) {                mBatteryLight.setColor(BATTERY_FULL_ARGB);            } else {                mBatteryLight.setColor(BATTERY_MEDIUM_ARGB);            }        } else {            mBatteryLight.turnOff();        }        // clear pending pulse notification if screen is on        if (mScreenOn || mLedNotification == null) {            mPendingPulseNotification = false;        }        // 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 && !mScreenOn) {                mPendingPulseNotification = true;            }        }        // we only flash if screen is off and persistent pulsing is enabled        // and we are not currently in a call        if (!mPendingPulseNotification || mScreenOn || mInCall) {            mNotificationLight.turnOff();        } else {            int ledARGB = mLedNotification.notification.ledARGB;            int ledOnMS = mLedNotification.notification.ledOnMS;            int ledOffMS = mLedNotification.notification.ledOffMS;            if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {                ledARGB = mDefaultNotificationColor;                ledOnMS = mDefaultNotificationLedOn;                ledOffMS = mDefaultNotificationLedOff;            }            if (mNotificationPulseEnabled) {                // pulse repeatedly                mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,                        ledOnMS, ledOffMS);            } else {                // pulse only once                mNotificationLight.pulse(ledARGB, ledOnMS);            }        }    }    // lock on mNotificationList    private int indexOfNotificationLocked(String pkg, String tag, int id)    {        ArrayList list = mNotificationList;        final int len = list.size();        for (int i=0; i 0) {                pw.println("  Toast Queue:");                for (int i=0; i 0) {                pw.println("  Notification List:");                for (int i=0; i 0) {                pw.println("  Lights List:");                for (int i=0; i在871行有这句代码:uri = notification.sound;在886行有这句代码:mSound.play(mContext, uri, looping, audioStreamType);当时我就有点兴奋了,感觉这就是问题的关键,然后打log,发现这就是问题的关键,当notification正常发声音的时候,这个886行的代码走进来了,不发声音的时候这个代码没有走进来,所以我离问题的根源又进了一步。最后发现是包着这段代码的if语句中的判断引起的,所以我把if语句中的代码都打印出来,发现是mDisabledNotifications这个变量引起的,我就追踪这个mDisabledNotifications变量值的变化的地方。发现在467行有这段代码:

   // Don't start allowing notifications until the setup wizard has run once.        // After that, including subsequent boots, init with notifications turned on.        // This works on the first boot because the setup wizard will toggle this        // flag at least once and we'll go back to 0 after that.        if (0 == Settings.Secure.getInt(mContext.getContentResolver(),                    Settings.Secure.DEVICE_PROVISIONED, 0)) {            mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;        }

研究以上的注释,发现原来google故意这么设置的,至于google为什么要这么设置,我没有深究,暂时没有想明白,但是这个这个初始化的时候必须要tsetup wizard (设置向导)运行一次,所以导致了值不对,所以这个notification就不响了。


在264行有这段代码对mDisabledNotification进行改变的:

    private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks            = new StatusBarManagerService.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);                    }                }            }        }

找到问题的根源了,太兴奋了,这个问题断断续续困扰了我3周,终于经过我3天地认真分析,把问题的根源找到了,要想解决就很简单了,可以在初始化的时候直接赋值为0就ok了!

       最后、886行mSound.play(mContext, uri, looping, audioStreamType);是NotificationPlayer类中的一个方法,在play()方法中有一个线程,  mThread = new CmdThread();   mThread.start();线程中的run方法中有:case   PLAY:startSound(cmd);在startSound()方法中又有一个线程:  mCompletionThread = new CreationAndCompletionThread(cmd);

mCompletionThread = new CreationAndCompletionThread(cmd);                synchronized(mCompletionThread) {                    mCompletionThread.start();                    mCompletionThread.wait();                }

在这个线程类中的run方法中:在这个线程中进行播放音乐的 ,真正的发声音也是通过Mediapaly来实现的:

 public void run() {            Looper.prepare();            mLooper = Looper.myLooper();            synchronized(this) {                AudioManager audioManager =                    (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);                try {                    MediaPlayer player = new MediaPlayer();                    player.setAudioStreamType(mCmd.stream);                    player.setDataSource(mCmd.context, mCmd.uri);                    player.setLooping(mCmd.looping);                    player.prepare();                    if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)                            && (mCmd.uri.getEncodedPath().length() > 0)) {                        if (mCmd.looping) {                            audioManager.requestAudioFocus(null, mCmd.stream,                                    AudioManager.AUDIOFOCUS_GAIN);                        } else {                            audioManager.requestAudioFocus(null, mCmd.stream,                                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);                        }                    }                    player.setOnCompletionListener(NotificationPlayer.this);                    player.start();                    if (mPlayer != null) {                        mPlayer.release();                    }                    mPlayer = player;                }                catch (Exception e) {                    Log.w(mTag, "error loading sound for " + mCmd.uri, e);                }                mAudioManager = audioManager;                this.notify();            }            Looper.loop();        }



 

        希望给读者留下点启发,转载请标明出处:http://blog.csdn.net/wdaming1986/article/details/7081787

 

(编辑: wdaming1986)

网友评论