Android中Notification的framework层讲解

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

你可能感兴趣的:(Android中Notification的framework层讲解)