Android中Notification的framework层讲解

来源:互联网 发布:林心如霍建华结婚知乎 编辑:程序博客网 时间:2024/06/08 12:22

android的notificaiton的声音sound也是申请的AudioManager机制来播放声音的。最近让我找恢复出厂设置后,手机刚启动,接受短信没有声音,如果恢复出厂设置后,等一会儿,过个2分钟再接受短信,就有铃声了。下面我把我分析代码的方法写下来,给自己和读者一些启发:
日历也是用的是Notification,但是恢复出厂设置后,立马设置日历后,日历可以出声音,我看日历的代码,结果发现日历只是用了Notification的闪屏,真正声音是日历自己实现了Mediaplayer来出声音的。所以我又不得不老老实实地研究Notification.sound到底把声音传递到什么地方去了?

   首先,我在短信的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}.** <p>For a guide to creating notifications, see the* <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Creating Status* Bar Notifications</a> document in the Dev Guide.</p>*/ public class Notificationimplements Parcelable {     /**     * Use all default values (where applicable).     */     public staticfinalint DEFAULT_ALL = ~0;     /**     * Use the default notification sound. This will ignore any given     * {@link #sound}.     *     * @see #defaults     */      public staticfinalint 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 finalint 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 staticfinalint 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 <em>Activities and Tasks</em>     * section of the <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application     * Fundamentals</a> 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     * <strong>immediate</strong> 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.     *     * <p>     * To play the default notification sound, see {@link #defaults}.     * </p>     */     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 staticfinalint 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.     *     * <p>     * To vibrate the default pattern, see {@link #defaults}.     * </p>     *     * @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.     * <p>     * To set, OR the desired from {@link #DEFAULT_SOUND},     * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default     * values, use {@link #DEFAULT_ALL}.     * </p>     */     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.     * <ul>     * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB     *      or 0 for both ledOnMS and ledOffMS.</li>     * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>     * <li>To flash the LED, pass the number of milliseconds that it should     *      be on and off to ledOnMS and ledOffMS.</li>     * </ul>     * <p>     * 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.     * <p>     * The alpha channel must be set for forward compatibility.     *     */     public staticfinalint 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 finalint 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 staticfinalint 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 finalint 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 staticfinalint 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 finalint 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 staticfinalint 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) {             finalint N = vibrate.length;             final long[] vib = that.vibrate =newlong[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 staticfinal Parcelable.Creator<Notification> CREATOR             = new Parcelable.Creator<Notification>()     {         public Notification createFromParcel(Parcel parcel)         {             returnnew 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.     *     * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields     * in the view.</p>     * @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     * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application Fundamentals: Activities and Tasks</a>.     */     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<N; i++) {                 sb.append(this.vibrate[i]);                 sb.append(',');             }             if (N != -1) {                 sb.append(this.vibrate[N]);             }             sb.append("]");         } else if ((this.defaults & DEFAULT_VIBRATE) !=0) {             sb.append("default");         } else {             sb.append("null");         }         sb.append(",sound=");         if (this.sound !=null) {             sb.append(this.sound.toString());         } else if ((this.defaults & DEFAULT_SOUND) !=0) {             sb.append("default");         } else {             sb.append("null");         }         sb.append(",defaults=0x");         sb.append(Integer.toHexString(this.defaults));         sb.append(",flags=0x");         sb.append(Integer.toHexString(this.flags));         sb.append(")");         return sb.toString();     } } [java] view plaincopy/** 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}.** <p>For a guide to creating notifications, see the* <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Creating Status* Bar Notifications</a> document in the Dev Guide.</p>*/ public class Notificationimplements Parcelable {     /**     * Use all default values (where applicable).     */     public staticfinalint DEFAULT_ALL = ~0;     /**     * Use the default notification sound. This will ignore any given     * {@link #sound}.     *     * @see #defaults     */      public staticfinalint 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 staticfinalint 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 staticfinalint 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 <em>Activities and Tasks</em>     * section of the <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application     * Fundamentals</a> 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     * <strong>immediate</strong> 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.     *     * <p>     * To play the default notification sound, see {@link #defaults}.     * </p>     */     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 staticfinalint 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.     *     * <p>     * To vibrate the default pattern, see {@link #defaults}.     * </p>     *     * @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.     * <p>     * To set, OR the desired from {@link #DEFAULT_SOUND},     * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default     * values, use {@link #DEFAULT_ALL}.     * </p>     */     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.     * <ul>     * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB     *      or 0 for both ledOnMS and ledOffMS.</li>     * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>     * <li>To flash the LED, pass the number of milliseconds that it should     *      be on and off to ledOnMS and ledOffMS.</li>     * </ul>     * <p>     * 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.     * <p>     * The alpha channel must be set for forward compatibility.     *     */     public staticfinalint 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 staticfinalint 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 staticfinalint 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 staticfinalint 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 staticfinalint 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 staticfinalint 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 staticfinalint 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 =newlong[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 staticfinal Parcelable.Creator<Notification> CREATOR             = new Parcelable.Creator<Notification>()     {         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.     *     * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields     * in the view.</p>     * @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     * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application Fundamentals: Activities and Tasks</a>.     */     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<N; i++) {                 sb.append(this.vibrate[i]);                 sb.append(',');             }             if (N != -1) {                 sb.append(this.vibrate[N]);             }             sb.append("]");         } else if ((this.defaults & DEFAULT_VIBRATE) !=0) {             sb.append("default");         } else {             sb.append("null");         }         sb.append(",sound=");         if (this.sound !=null) {             sb.append(this.sound.toString());         } else if ((this.defaults & DEFAULT_SOUND) !=0) {             sb.append("default");         } else {             sb.append("null");         }         sb.append(",defaults=0x");         sb.append(Integer.toHexString(this.defaults));         sb.append(",flags=0x");         sb.append(Integer.toHexString(this.flags));         sb.append(")");         return sb.toString();     } } 

在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 NotificationManagerServiceextends INotificationManager.Stub {     private staticfinal String TAG ="NotificationService";     private staticfinalboolean DBG =false;     private staticfinalint MAX_PACKAGE_NOTIFICATIONS =50;     private staticfinalint NOTIFICATION_REQUEST_INTERVAL =30000;// 30 seconds     private staticfinalint MAX_PACKAGE_NOTIFICATION_REQUESTS =500;     // message codes     private staticfinalint MESSAGE_TIMEOUT =2;     private staticfinalint LONG_DELAY =3500;// 3.5 seconds     private staticfinalint SHORT_DELAY =2000;// 2 seconds     private staticfinallong[] DEFAULT_VIBRATE_PATTERN = {0,250,250,250};     private staticfinalint 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<NotificationRecord> mNotificationList =             new ArrayList<NotificationRecord>();     private class PackageRequestInfo {         int requestCount;         long lastPostTime;     }     private final HashMap<String, PackageRequestInfo> mPackageInfo =             new HashMap<String, PackageRequestInfo>();     private ArrayList<ToastRecord> mToastQueue;     private ArrayList<NotificationRecord> mLights =new ArrayList<NotificationRecord>();     private boolean mBatteryCharging;     private boolean mBatteryLow;     private boolean mBatteryFull;     private NotificationRecord mLedNotification;     private staticfinalint BATTERY_LOW_ARGB =0xFFFF0000;// Charging Low - red solid on     private staticfinalint BATTERY_MEDIUM_ARGB =0xFFFFFF00;   // Charging - orange solid on     private staticfinalint BATTERY_FULL_ARGB =0xFF00FF00;// Charging Full - green solid on     private staticfinalint BATTERY_BLINK_ON =125;     private staticfinalint 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 "<name unknown>";         }     }     private staticfinalclass 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 staticfinalclass 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 SettingsObserverextends 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<ToastRecord>();         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;                         finalint N = mToastQueue.size();                         for (int i=0; i<N; i++) {                              final ToastRecord r = mToastQueue.get(i);                              if (r.pkg.equals(pkg)) {                                  count++;                                  if (count >= 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<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 finalclass WorkerHandlerextends 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<N; i++) {                     final NotificationRecord r = mNotificationList.get(i);                     if (r.pkg.equals(pkg)) {                         count++;                         if (count >= 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);                     finallong 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<NotificationRecord> list = mNotificationList;         final int len = list.size();         for (int i=0; i<len; i++) {             NotificationRecord r = list.get(i);             if (tag == null) {                 if (r.tag !=null) {                     continue;                 }             } else {                 if (!tag.equals(r.tag)) {                     continue;                 }             }             if (r.id == id && r.pkg.equals(pkg)) {                 return i;             }         }         return -1;     }     // This is here instead of StatusBarPolicy because it is an important     // security feature that we don't want people customizing the platform     // to accidentally lose.     private void updateAdbNotification(boolean adbEnabled) {         if (adbEnabled) {             if ("0".equals(SystemProperties.get("persist.adb.notify"))) {                 return;             }             if (!mAdbNotificationShown) {                 NotificationManager notificationManager = (NotificationManager) mContext                         .getSystemService(Context.NOTIFICATION_SERVICE);                 if (notificationManager !=null) {                     Resources r = mContext.getResources();                     CharSequence title = r.getText(                             com.android.internal.R.string.adb_active_notification_title);                     CharSequence message = r.getText(                             com.android.internal.R.string.adb_active_notification_message);                     if (mAdbNotification ==null) {                         mAdbNotification = new Notification();                         mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb;                         mAdbNotification.when = 0;                         mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;                         mAdbNotification.tickerText = title;                         mAdbNotification.defaults = 0; // please be quiet                         mAdbNotification.sound = null;                         mAdbNotification.vibrate = null;                     }                     Intent intent = new Intent(                             Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |                             Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);                     // Note: we are hard-coding the component because this is                     // an important security UI that we don't want anyone                     // intercepting.                     intent.setComponent(new ComponentName("com.android.settings",                             "com.android.settings.DevelopmentSettings"));                     PendingIntent pi = PendingIntent.getActivity(mContext,0,                             intent, 0);                     mAdbNotification.setLatestEventInfo(mContext, title, message, pi);                     mAdbNotificationShown = true;                     notificationManager.notify(                             com.android.internal.R.string.adb_active_notification_title,                             mAdbNotification);                 }             }         } else if (mAdbNotificationShown) {             NotificationManager notificationManager = (NotificationManager) mContext                     .getSystemService(Context.NOTIFICATION_SERVICE);             if (notificationManager !=null) {                 mAdbNotificationShown = false;                 notificationManager.cancel(                         com.android.internal.R.string.adb_active_notification_title);             }         }     }     private void updateNotificationPulse() {         synchronized (mNotificationList) {             updateLightsLocked();         }     }     // ======================================================================     @Override     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {         if (mContext.checkCallingOrSelfPermission(android.Manifest.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);             pw.println("  mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));             pw.println("  mSystemReady=" + mSystemReady);         }     } } 

在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来实现的:

publicvoid 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();         }</span> 

出处:http://blog.csdn.net/wdaming1986/article/details/7081787

0 0