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类。

[java] view plain copy print ?
  1. /*
  2. * Copyright (C) 2007 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. *      http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */ 
  16.  
  17. package android.app; 
  18.  
  19. import java.util.Date; 
  20.  
  21. import android.app.PendingIntent; 
  22. import android.content.Context; 
  23. import android.content.Intent; 
  24. import android.media.AudioManager; 
  25. import android.net.Uri; 
  26. import android.os.Parcel; 
  27. import android.os.Parcelable; 
  28. import android.text.TextUtils; 
  29. import android.text.format.DateFormat; 
  30. import android.text.format.DateUtils; 
  31. import android.widget.RemoteViews; 
  32.  
  33. /**
  34. * A class that represents how a persistent notification is to be presented to
  35. * the user using the {@link android.app.NotificationManager}.
  36. *
  37. * <p>For a guide to creating notifications, see the
  38. * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Creating Status
  39. * Bar Notifications</a> document in the Dev Guide.</p>
  40. */ 
  41. public class Notificationimplements Parcelable 
  42.     /**
  43.      * Use all default values (where applicable).
  44.      */ 
  45.     public staticfinalint DEFAULT_ALL = ~0
  46.      
  47.     /**
  48.      * Use the default notification sound. This will ignore any given
  49.      * {@link #sound}.
  50.      *
  51.      * @see #defaults
  52.      */  
  53.     public staticfinalint DEFAULT_SOUND =1
  54.  
  55.     /**
  56.      * Use the default notification vibrate. This will ignore any given
  57.      * {@link #vibrate}. Using phone vibration requires the
  58.      * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
  59.      *
  60.      * @see #defaults
  61.      */  
  62.     public static finalint DEFAULT_VIBRATE =2
  63.      
  64.     /**
  65.      * Use the default notification lights. This will ignore the
  66.      * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
  67.      * {@link #ledOnMS}.
  68.      *
  69.      * @see #defaults
  70.      */  
  71.     public staticfinalint DEFAULT_LIGHTS =4
  72.      
  73.     /**
  74.      * The timestamp for the notification.  The icons and expanded views
  75.      * are sorted by this key.
  76.      */ 
  77.     public long when; 
  78.  
  79.     /**
  80.      * The resource id of a drawable to use as the icon in the status bar.
  81.      */ 
  82.     public int icon; 
  83.  
  84.     /**
  85.      * The number of events that this notification represents.  For example, in a new mail
  86.      * notification, this could be the number of unread messages.  This number is superimposed over
  87.      * the icon in the status bar.  If the number is 0 or negative, it is not shown in the status
  88.      * bar.
  89.      */ 
  90.     public int number; 
  91.  
  92.     /**
  93.      * The intent to execute when the expanded status entry is clicked.  If
  94.      * this is an activity, it must include the
  95.      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
  96.      * that you take care of task management as described in the <em>Activities and Tasks</em>
  97.      * section of the <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application
  98.      * Fundamentals</a> document.
  99.      */ 
  100.     public PendingIntent contentIntent; 
  101.  
  102.     /**
  103.      * The intent to execute when the status entry is deleted by the user
  104.      * with the "Clear All Notifications" button. This probably shouldn't
  105.      * be launching an activity since several of those will be sent at the
  106.      * same time.
  107.      */ 
  108.     public PendingIntent deleteIntent; 
  109.  
  110.     /**
  111.      * An intent to launch instead of posting the notification to the status bar.
  112.      * Only for use with extremely high-priority notifications demanding the user's
  113.      * <strong>immediate</strong> attention, such as an incoming phone call or
  114.      * alarm clock that the user has explicitly set to a particular time.
  115.      * If this facility is used for something else, please give the user an option
  116.      * to turn it off and use a normal notification, as this can be extremely
  117.      * disruptive.
  118.      */ 
  119.     public PendingIntent fullScreenIntent; 
  120.  
  121.     /**
  122.      * Text to scroll across the screen when this item is added to
  123.      * the status bar.
  124.      */ 
  125.     public CharSequence tickerText; 
  126.  
  127.     /**
  128.      * The view that will represent this notification in the expanded status bar.
  129.      */ 
  130.     public RemoteViews contentView; 
  131.  
  132.     /**
  133.      * If the icon in the status bar is to have more than one level, you can set this.  Otherwise,
  134.      * leave it at its default value of 0.
  135.      *
  136.      * @see android.widget.ImageView#setImageLevel
  137.      * @see android.graphics.drawable#setLevel
  138.      */ 
  139.     public int iconLevel; 
  140.  
  141.     /**
  142.      * The sound to play.
  143.      *
  144.      * <p>
  145.      * To play the default notification sound, see {@link #defaults}.
  146.      * </p>
  147.      */ 
  148.     public Uri sound; 
  149.  
  150.     /**
  151.      * Use this constant as the value for audioStreamType to request that
  152.      * the default stream type for notifications be used.  Currently the
  153.      * default stream type is STREAM_RING.
  154.      */ 
  155.     public staticfinalint STREAM_DEFAULT = -1
  156.  
  157.     /**
  158.      * The audio stream type to use when playing the sound.
  159.      * Should be one of the STREAM_ constants from
  160.      * {@link android.media.AudioManager}.
  161.      */ 
  162.     public int audioStreamType = STREAM_DEFAULT; 
  163.  
  164.      
  165.     /**
  166.      * The pattern with which to vibrate.
  167.      *
  168.      * <p>
  169.      * To vibrate the default pattern, see {@link #defaults}.
  170.      * </p>
  171.      *
  172.      * @see android.os.Vibrator#vibrate(long[],int)
  173.      */ 
  174.     public long[] vibrate; 
  175.  
  176.     /**
  177.      * The color of the led.  The hardware will do its best approximation.
  178.      *
  179.      * @see #FLAG_SHOW_LIGHTS
  180.      * @see #flags
  181.      */ 
  182.     public int ledARGB; 
  183.  
  184.     /**
  185.      * The number of milliseconds for the LED to be on while it's flashing.
  186.      * The hardware will do its best approximation.
  187.      *
  188.      * @see #FLAG_SHOW_LIGHTS
  189.      * @see #flags
  190.      */ 
  191.     public int ledOnMS; 
  192.  
  193.     /**
  194.      * The number of milliseconds for the LED to be off while it's flashing.
  195.      * The hardware will do its best approximation.
  196.      *
  197.      * @see #FLAG_SHOW_LIGHTS
  198.      * @see #flags
  199.      */ 
  200.     public int ledOffMS; 
  201.  
  202.     /**
  203.      * Specifies which values should be taken from the defaults.
  204.      * <p>
  205.      * To set, OR the desired from {@link #DEFAULT_SOUND},
  206.      * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
  207.      * values, use {@link #DEFAULT_ALL}.
  208.      * </p>
  209.      */ 
  210.     public int defaults; 
  211.  
  212.  
  213.     /**
  214.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  215.      * set if you want the LED on for this notification.
  216.      * <ul>
  217.      * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
  218.      *      or 0 for both ledOnMS and ledOffMS.</li>
  219.      * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
  220.      * <li>To flash the LED, pass the number of milliseconds that it should
  221.      *      be on and off to ledOnMS and ledOffMS.</li>
  222.      * </ul>
  223.      * <p>
  224.      * Since hardware varies, you are not guaranteed that any of the values
  225.      * you pass are honored exactly.  Use the system defaults (TODO) if possible
  226.      * because they will be set to values that work on any given hardware.
  227.      * <p>
  228.      * The alpha channel must be set for forward compatibility.
  229.      *
  230.      */ 
  231.     public staticfinalint FLAG_SHOW_LIGHTS        =0x00000001
  232.  
  233.     /**
  234.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  235.      * set if this notification is in reference to something that is ongoing,
  236.      * like a phone call.  It should not be set if this notification is in
  237.      * reference to something that happened at a particular point in time,
  238.      * like a missed phone call.
  239.      */ 
  240.     public static finalint FLAG_ONGOING_EVENT      =0x00000002
  241.  
  242.     /**
  243.      * Bit to be bitwise-ored into the {@link #flags} field that if set,
  244.      * the audio will be repeated until the notification is
  245.      * cancelled or the notification window is opened.
  246.      */ 
  247.     public staticfinalint FLAG_INSISTENT          =0x00000004
  248.  
  249.     /**
  250.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  251.      * set if you want the sound and/or vibration play each time the
  252.      * notification is sent, even if it has not been canceled before that.
  253.      */ 
  254.     public static finalint FLAG_ONLY_ALERT_ONCE    =0x00000008
  255.  
  256.     /**
  257.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  258.      * set if the notification should be canceled when it is clicked by the
  259.      * user.
  260.      */ 
  261.     public staticfinalint FLAG_AUTO_CANCEL        =0x00000010
  262.  
  263.     /**
  264.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  265.      * set if the notification should not be canceled when the user clicks
  266.      * the Clear all button.
  267.      */ 
  268.     public static finalint FLAG_NO_CLEAR           =0x00000020
  269.  
  270.     /**
  271.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  272.      * set if this notification represents a currently running service.  This
  273.      * will normally be set for you by {@link Service#startForeground}.
  274.      */ 
  275.     public staticfinalint FLAG_FOREGROUND_SERVICE =0x00000040
  276.  
  277.     public int flags; 
  278.  
  279.     /**
  280.      * Constructs a Notification object with everything set to 0.
  281.      */ 
  282.     public Notification() 
  283.     { 
  284.         this.when = System.currentTimeMillis(); 
  285.     } 
  286.  
  287.     /**
  288.      * @deprecated use {@link #Notification(int,CharSequence,long)} and {@link #setLatestEventInfo}.
  289.      * @hide
  290.      */ 
  291.     public Notification(Context context,int icon, CharSequence tickerText,long when, 
  292.             CharSequence contentTitle, CharSequence contentText, Intent contentIntent) 
  293.     { 
  294.         this.when = when; 
  295.         this.icon = icon; 
  296.         this.tickerText = tickerText; 
  297.         setLatestEventInfo(context, contentTitle, contentText, 
  298.                 PendingIntent.getActivity(context, 0, contentIntent,0)); 
  299.     } 
  300.  
  301.     /**
  302.      * Constructs a Notification object with the information needed to
  303.      * have a status bar icon without the standard expanded view.
  304.      *
  305.      * @param icon          The resource id of the icon to put in the status bar.
  306.      * @param tickerText    The text that flows by in the status bar when the notification first
  307.      *                      activates.
  308.      * @param when          The time to show in the time field.  In the System.currentTimeMillis
  309.      *                      timebase.
  310.      */ 
  311.     public Notification(int icon, CharSequence tickerText,long when) 
  312.     { 
  313.         this.icon = icon; 
  314.         this.tickerText = tickerText; 
  315.         this.when = when; 
  316.     } 
  317.  
  318.     /**
  319.      * Unflatten the notification from a parcel.
  320.      */ 
  321.     public Notification(Parcel parcel) 
  322.     { 
  323.         int version = parcel.readInt(); 
  324.  
  325.         when = parcel.readLong(); 
  326.         icon = parcel.readInt(); 
  327.         number = parcel.readInt(); 
  328.         if (parcel.readInt() !=0) { 
  329.             contentIntent = PendingIntent.CREATOR.createFromParcel(parcel); 
  330.         } 
  331.         if (parcel.readInt() !=0) { 
  332.             deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel); 
  333.         } 
  334.         if (parcel.readInt() !=0) { 
  335.             tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 
  336.         } 
  337.         if (parcel.readInt() !=0) { 
  338.             contentView = RemoteViews.CREATOR.createFromParcel(parcel); 
  339.         } 
  340.         defaults = parcel.readInt(); 
  341.         flags = parcel.readInt(); 
  342.         if (parcel.readInt() !=0) { 
  343.             sound = Uri.CREATOR.createFromParcel(parcel); 
  344.         } 
  345.  
  346.         audioStreamType = parcel.readInt(); 
  347.         vibrate = parcel.createLongArray(); 
  348.         ledARGB = parcel.readInt(); 
  349.         ledOnMS = parcel.readInt(); 
  350.         ledOffMS = parcel.readInt(); 
  351.         iconLevel = parcel.readInt(); 
  352.  
  353.         if (parcel.readInt() !=0) { 
  354.             fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel); 
  355.         } 
  356.     } 
  357.  
  358.     public Notification clone() { 
  359.         Notification that = new Notification(); 
  360.  
  361.         that.when = this.when; 
  362.         that.icon = this.icon; 
  363.         that.number = this.number; 
  364.  
  365.         // PendingIntents are global, so there's no reason (or way) to clone them. 
  366.         that.contentIntent = this.contentIntent; 
  367.         that.deleteIntent = this.deleteIntent; 
  368.         that.fullScreenIntent = this.fullScreenIntent; 
  369.  
  370.         if (this.tickerText !=null) { 
  371.             that.tickerText = this.tickerText.toString(); 
  372.         } 
  373.         if (this.contentView !=null) { 
  374.             that.contentView = this.contentView.clone(); 
  375.         } 
  376.         that.iconLevel = that.iconLevel; 
  377.         that.sound = this.sound;// android.net.Uri is immutable 
  378.         that.audioStreamType = this.audioStreamType; 
  379.  
  380.         final long[] vibrate = this.vibrate; 
  381.         if (vibrate != null) { 
  382.             finalint N = vibrate.length; 
  383.             final long[] vib = that.vibrate =newlong[N]; 
  384.             System.arraycopy(vibrate, 0, vib,0, N); 
  385.         } 
  386.  
  387.         that.ledARGB = this.ledARGB; 
  388.         that.ledOnMS = this.ledOnMS; 
  389.         that.ledOffMS = this.ledOffMS; 
  390.         that.defaults = this.defaults; 
  391.          
  392.         that.flags = this.flags; 
  393.  
  394.         return that; 
  395.     } 
  396.  
  397.     public int describeContents() { 
  398.         return 0
  399.     } 
  400.  
  401.     /**
  402.      * Flatten this notification from a parcel.
  403.      */ 
  404.     public void writeToParcel(Parcel parcel, int flags) 
  405.     { 
  406.         parcel.writeInt(1); 
  407.  
  408.         parcel.writeLong(when); 
  409.         parcel.writeInt(icon); 
  410.         parcel.writeInt(number); 
  411.         if (contentIntent != null) { 
  412.             parcel.writeInt(1); 
  413.             contentIntent.writeToParcel(parcel, 0); 
  414.         } else
  415.             parcel.writeInt(0); 
  416.         } 
  417.         if (deleteIntent != null) { 
  418.             parcel.writeInt(1); 
  419.             deleteIntent.writeToParcel(parcel, 0); 
  420.         } else
  421.             parcel.writeInt(0); 
  422.         } 
  423.         if (tickerText != null) { 
  424.             parcel.writeInt(1); 
  425.             TextUtils.writeToParcel(tickerText, parcel, flags); 
  426.         } else
  427.             parcel.writeInt(0); 
  428.         } 
  429.         if (contentView != null) { 
  430.             parcel.writeInt(1); 
  431.             contentView.writeToParcel(parcel, 0); 
  432.         } else
  433.             parcel.writeInt(0); 
  434.         } 
  435.  
  436.         parcel.writeInt(defaults); 
  437.         parcel.writeInt(this.flags); 
  438.  
  439.         if (sound != null) { 
  440.             parcel.writeInt(1); 
  441.             sound.writeToParcel(parcel, 0); 
  442.         } else
  443.             parcel.writeInt(0); 
  444.         } 
  445.         parcel.writeInt(audioStreamType); 
  446.         parcel.writeLongArray(vibrate); 
  447.         parcel.writeInt(ledARGB); 
  448.         parcel.writeInt(ledOnMS); 
  449.         parcel.writeInt(ledOffMS); 
  450.         parcel.writeInt(iconLevel); 
  451.  
  452.         if (fullScreenIntent !=null) { 
  453.             parcel.writeInt(1); 
  454.             fullScreenIntent.writeToParcel(parcel, 0); 
  455.         } else
  456.             parcel.writeInt(0); 
  457.         } 
  458.     } 
  459.  
  460.     /**
  461.      * Parcelable.Creator that instantiates Notification objects
  462.      */ 
  463.     public staticfinal Parcelable.Creator<Notification> CREATOR 
  464.             = new Parcelable.Creator<Notification>() 
  465.     { 
  466.         public Notification createFromParcel(Parcel parcel) 
  467.         { 
  468.             returnnew Notification(parcel); 
  469.         } 
  470.  
  471.         public Notification[] newArray(int size) 
  472.         { 
  473.             return new Notification[size]; 
  474.         } 
  475.     }; 
  476.  
  477.     /**
  478.      * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
  479.      * layout.
  480.      *
  481.      * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
  482.      * in the view.</p>
  483.      * @param context       The context for your application / activity.
  484.      * @param contentTitle The title that goes in the expanded entry.
  485.      * @param contentText  The text that goes in the expanded entry.
  486.      * @param contentIntent The intent to launch when the user clicks the expanded notification.
  487.      * If this is an activity, it must include the
  488.      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
  489.      * that you take care of task management as described in
  490.      * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application Fundamentals: Activities and Tasks</a>.
  491.      */ 
  492.     public void setLatestEventInfo(Context context, 
  493.             CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) { 
  494.         RemoteViews contentView = new RemoteViews(context.getPackageName(), 
  495.                 com.android.internal.R.layout.status_bar_latest_event_content); 
  496.         if (this.icon !=0) { 
  497.             contentView.setImageViewResource(com.android.internal.R.id.icon,this.icon); 
  498.         } 
  499.         if (contentTitle != null) { 
  500.             contentView.setTextViewText(com.android.internal.R.id.title, contentTitle); 
  501.         } 
  502.         if (contentText !=null) { 
  503.             contentView.setTextViewText(com.android.internal.R.id.text, contentText); 
  504.         } 
  505.         if (this.when !=0) { 
  506.             contentView.setLong(com.android.internal.R.id.time,"setTime", when); 
  507.         } 
  508.  
  509.         this.contentView = contentView; 
  510.         this.contentIntent = contentIntent; 
  511.     } 
  512.  
  513.     @Override 
  514.     public String toString() { 
  515.         StringBuilder sb = new StringBuilder(); 
  516.         sb.append("Notification(vibrate="); 
  517.         if (this.vibrate !=null) { 
  518.             int N =this.vibrate.length-1
  519.             sb.append("["); 
  520.             for (int i=0; i<N; i++) { 
  521.                 sb.append(this.vibrate[i]); 
  522.                 sb.append(','); 
  523.             } 
  524.             if (N != -1) { 
  525.                 sb.append(this.vibrate[N]); 
  526.             } 
  527.             sb.append("]"); 
  528.         } else if ((this.defaults & DEFAULT_VIBRATE) !=0) { 
  529.             sb.append("default"); 
  530.         } else
  531.             sb.append("null"); 
  532.         } 
  533.         sb.append(",sound="); 
  534.         if (this.sound !=null) { 
  535.             sb.append(this.sound.toString()); 
  536.         } else if ((this.defaults & DEFAULT_SOUND) !=0) { 
  537.             sb.append("default"); 
  538.         } else
  539.             sb.append("null"); 
  540.         } 
  541.         sb.append(",defaults=0x"); 
  542.         sb.append(Integer.toHexString(this.defaults)); 
  543.         sb.append(",flags=0x"); 
  544.         sb.append(Integer.toHexString(this.flags)); 
  545.         sb.append(")"); 
  546.         return sb.toString(); 
  547.     } 
[java] view plain copy print ?
  1. /*
  2. * Copyright (C) 2007 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. *      http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */ 
  16.  
  17. package android.app; 
  18.  
  19. import java.util.Date; 
  20.  
  21. import android.app.PendingIntent; 
  22. import android.content.Context; 
  23. import android.content.Intent; 
  24. import android.media.AudioManager; 
  25. import android.net.Uri; 
  26. import android.os.Parcel; 
  27. import android.os.Parcelable; 
  28. import android.text.TextUtils; 
  29. import android.text.format.DateFormat; 
  30. import android.text.format.DateUtils; 
  31. import android.widget.RemoteViews; 
  32.  
  33. /**
  34. * A class that represents how a persistent notification is to be presented to
  35. * the user using the {@link android.app.NotificationManager}.
  36. *
  37. * <p>For a guide to creating notifications, see the
  38. * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Creating Status
  39. * Bar Notifications</a> document in the Dev Guide.</p>
  40. */ 
  41. public class Notificationimplements Parcelable 
  42.     /**
  43.      * Use all default values (where applicable).
  44.      */ 
  45.     public staticfinalint DEFAULT_ALL = ~0
  46.      
  47.     /**
  48.      * Use the default notification sound. This will ignore any given
  49.      * {@link #sound}.
  50.      *
  51.      * @see #defaults
  52.      */  
  53.     public staticfinalint DEFAULT_SOUND =1
  54.  
  55.     /**
  56.      * Use the default notification vibrate. This will ignore any given
  57.      * {@link #vibrate}. Using phone vibration requires the
  58.      * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
  59.      *
  60.      * @see #defaults
  61.      */  
  62.     public staticfinalint DEFAULT_VIBRATE =2
  63.      
  64.     /**
  65.      * Use the default notification lights. This will ignore the
  66.      * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
  67.      * {@link #ledOnMS}.
  68.      *
  69.      * @see #defaults
  70.      */  
  71.     public staticfinalint DEFAULT_LIGHTS =4
  72.      
  73.     /**
  74.      * The timestamp for the notification.  The icons and expanded views
  75.      * are sorted by this key.
  76.      */ 
  77.     public long when; 
  78.  
  79.     /**
  80.      * The resource id of a drawable to use as the icon in the status bar.
  81.      */ 
  82.     public int icon; 
  83.  
  84.     /**
  85.      * The number of events that this notification represents.  For example, in a new mail
  86.      * notification, this could be the number of unread messages.  This number is superimposed over
  87.      * the icon in the status bar.  If the number is 0 or negative, it is not shown in the status
  88.      * bar.
  89.      */ 
  90.     public int number; 
  91.  
  92.     /**
  93.      * The intent to execute when the expanded status entry is clicked.  If
  94.      * this is an activity, it must include the
  95.      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
  96.      * that you take care of task management as described in the <em>Activities and Tasks</em>
  97.      * section of the <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application
  98.      * Fundamentals</a> document.
  99.      */ 
  100.     public PendingIntent contentIntent; 
  101.  
  102.     /**
  103.      * The intent to execute when the status entry is deleted by the user
  104.      * with the "Clear All Notifications" button. This probably shouldn't
  105.      * be launching an activity since several of those will be sent at the
  106.      * same time.
  107.      */ 
  108.     public PendingIntent deleteIntent; 
  109.  
  110.     /**
  111.      * An intent to launch instead of posting the notification to the status bar.
  112.      * Only for use with extremely high-priority notifications demanding the user's
  113.      * <strong>immediate</strong> attention, such as an incoming phone call or
  114.      * alarm clock that the user has explicitly set to a particular time.
  115.      * If this facility is used for something else, please give the user an option
  116.      * to turn it off and use a normal notification, as this can be extremely
  117.      * disruptive.
  118.      */ 
  119.     public PendingIntent fullScreenIntent; 
  120.  
  121.     /**
  122.      * Text to scroll across the screen when this item is added to
  123.      * the status bar.
  124.      */ 
  125.     public CharSequence tickerText; 
  126.  
  127.     /**
  128.      * The view that will represent this notification in the expanded status bar.
  129.      */ 
  130.     public RemoteViews contentView; 
  131.  
  132.     /**
  133.      * If the icon in the status bar is to have more than one level, you can set this.  Otherwise,
  134.      * leave it at its default value of 0.
  135.      *
  136.      * @see android.widget.ImageView#setImageLevel
  137.      * @see android.graphics.drawable#setLevel
  138.      */ 
  139.     public int iconLevel; 
  140.  
  141.     /**
  142.      * The sound to play.
  143.      *
  144.      * <p>
  145.      * To play the default notification sound, see {@link #defaults}.
  146.      * </p>
  147.      */ 
  148.     public Uri sound; 
  149.  
  150.     /**
  151.      * Use this constant as the value for audioStreamType to request that
  152.      * the default stream type for notifications be used.  Currently the
  153.      * default stream type is STREAM_RING.
  154.      */ 
  155.     public staticfinalint STREAM_DEFAULT = -1
  156.  
  157.     /**
  158.      * The audio stream type to use when playing the sound.
  159.      * Should be one of the STREAM_ constants from
  160.      * {@link android.media.AudioManager}.
  161.      */ 
  162.     public int audioStreamType = STREAM_DEFAULT; 
  163.  
  164.      
  165.     /**
  166.      * The pattern with which to vibrate.
  167.      *
  168.      * <p>
  169.      * To vibrate the default pattern, see {@link #defaults}.
  170.      * </p>
  171.      *
  172.      * @see android.os.Vibrator#vibrate(long[],int)
  173.      */ 
  174.     public long[] vibrate; 
  175.  
  176.     /**
  177.      * The color of the led.  The hardware will do its best approximation.
  178.      *
  179.      * @see #FLAG_SHOW_LIGHTS
  180.      * @see #flags
  181.      */ 
  182.     public int ledARGB; 
  183.  
  184.     /**
  185.      * The number of milliseconds for the LED to be on while it's flashing.
  186.      * The hardware will do its best approximation.
  187.      *
  188.      * @see #FLAG_SHOW_LIGHTS
  189.      * @see #flags
  190.      */ 
  191.     public int ledOnMS; 
  192.  
  193.     /**
  194.      * The number of milliseconds for the LED to be off while it's flashing.
  195.      * The hardware will do its best approximation.
  196.      *
  197.      * @see #FLAG_SHOW_LIGHTS
  198.      * @see #flags
  199.      */ 
  200.     public int ledOffMS; 
  201.  
  202.     /**
  203.      * Specifies which values should be taken from the defaults.
  204.      * <p>
  205.      * To set, OR the desired from {@link #DEFAULT_SOUND},
  206.      * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
  207.      * values, use {@link #DEFAULT_ALL}.
  208.      * </p>
  209.      */ 
  210.     public int defaults; 
  211.  
  212.  
  213.     /**
  214.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  215.      * set if you want the LED on for this notification.
  216.      * <ul>
  217.      * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
  218.      *      or 0 for both ledOnMS and ledOffMS.</li>
  219.      * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
  220.      * <li>To flash the LED, pass the number of milliseconds that it should
  221.      *      be on and off to ledOnMS and ledOffMS.</li>
  222.      * </ul>
  223.      * <p>
  224.      * Since hardware varies, you are not guaranteed that any of the values
  225.      * you pass are honored exactly.  Use the system defaults (TODO) if possible
  226.      * because they will be set to values that work on any given hardware.
  227.      * <p>
  228.      * The alpha channel must be set for forward compatibility.
  229.      *
  230.      */ 
  231.     public staticfinalint FLAG_SHOW_LIGHTS        =0x00000001
  232.  
  233.     /**
  234.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  235.      * set if this notification is in reference to something that is ongoing,
  236.      * like a phone call.  It should not be set if this notification is in
  237.      * reference to something that happened at a particular point in time,
  238.      * like a missed phone call.
  239.      */ 
  240.     public staticfinalint FLAG_ONGOING_EVENT      =0x00000002
  241.  
  242.     /**
  243.      * Bit to be bitwise-ored into the {@link #flags} field that if set,
  244.      * the audio will be repeated until the notification is
  245.      * cancelled or the notification window is opened.
  246.      */ 
  247.     public staticfinalint FLAG_INSISTENT          =0x00000004
  248.  
  249.     /**
  250.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  251.      * set if you want the sound and/or vibration play each time the
  252.      * notification is sent, even if it has not been canceled before that.
  253.      */ 
  254.     public staticfinalint FLAG_ONLY_ALERT_ONCE    =0x00000008
  255.  
  256.     /**
  257.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  258.      * set if the notification should be canceled when it is clicked by the
  259.      * user.
  260.      */ 
  261.     public staticfinalint FLAG_AUTO_CANCEL        =0x00000010
  262.  
  263.     /**
  264.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  265.      * set if the notification should not be canceled when the user clicks
  266.      * the Clear all button.
  267.      */ 
  268.     public staticfinalint FLAG_NO_CLEAR           =0x00000020
  269.  
  270.     /**
  271.      * Bit to be bitwise-ored into the {@link #flags} field that should be
  272.      * set if this notification represents a currently running service.  This
  273.      * will normally be set for you by {@link Service#startForeground}.
  274.      */ 
  275.     public staticfinalint FLAG_FOREGROUND_SERVICE =0x00000040
  276.  
  277.     public int flags; 
  278.  
  279.     /**
  280.      * Constructs a Notification object with everything set to 0.
  281.      */ 
  282.     public Notification() 
  283.     { 
  284.         this.when = System.currentTimeMillis(); 
  285.     } 
  286.  
  287.     /**
  288.      * @deprecated use {@link #Notification(int,CharSequence,long)} and {@link #setLatestEventInfo}.
  289.      * @hide
  290.      */ 
  291.     public Notification(Context context,int icon, CharSequence tickerText,long when, 
  292.             CharSequence contentTitle, CharSequence contentText, Intent contentIntent) 
  293.     { 
  294.         this.when = when; 
  295.         this.icon = icon; 
  296.         this.tickerText = tickerText; 
  297.         setLatestEventInfo(context, contentTitle, contentText, 
  298.                 PendingIntent.getActivity(context, 0, contentIntent, 0)); 
  299.     } 
  300.  
  301.     /**
  302.      * Constructs a Notification object with the information needed to
  303.      * have a status bar icon without the standard expanded view.
  304.      *
  305.      * @param icon          The resource id of the icon to put in the status bar.
  306.      * @param tickerText    The text that flows by in the status bar when the notification first
  307.      *                      activates.
  308.      * @param when          The time to show in the time field.  In the System.currentTimeMillis
  309.      *                      timebase.
  310.      */ 
  311.     public Notification(int icon, CharSequence tickerText,long when) 
  312.     { 
  313.         this.icon = icon; 
  314.         this.tickerText = tickerText; 
  315.         this.when = when; 
  316.     } 
  317.  
  318.     /**
  319.      * Unflatten the notification from a parcel.
  320.      */ 
  321.     public Notification(Parcel parcel) 
  322.     { 
  323.         int version = parcel.readInt(); 
  324.  
  325.         when = parcel.readLong(); 
  326.         icon = parcel.readInt(); 
  327.         number = parcel.readInt(); 
  328.         if (parcel.readInt() !=0) { 
  329.             contentIntent = PendingIntent.CREATOR.createFromParcel(parcel); 
  330.         } 
  331.         if (parcel.readInt() != 0) { 
  332.             deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel); 
  333.         } 
  334.         if (parcel.readInt() !=0) { 
  335.             tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 
  336.         } 
  337.         if (parcel.readInt() != 0) { 
  338.             contentView = RemoteViews.CREATOR.createFromParcel(parcel); 
  339.         } 
  340.         defaults = parcel.readInt(); 
  341.         flags = parcel.readInt(); 
  342.         if (parcel.readInt() !=0) { 
  343.             sound = Uri.CREATOR.createFromParcel(parcel); 
  344.         } 
  345.  
  346.         audioStreamType = parcel.readInt(); 
  347.         vibrate = parcel.createLongArray(); 
  348.         ledARGB = parcel.readInt(); 
  349.         ledOnMS = parcel.readInt(); 
  350.         ledOffMS = parcel.readInt(); 
  351.         iconLevel = parcel.readInt(); 
  352.  
  353.         if (parcel.readInt() != 0) { 
  354.             fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel); 
  355.         } 
  356.     } 
  357.  
  358.     public Notification clone() { 
  359.         Notification that = new Notification(); 
  360.  
  361.         that.when = this.when; 
  362.         that.icon = this.icon; 
  363.         that.number = this.number; 
  364.  
  365.         // PendingIntents are global, so there's no reason (or way) to clone them. 
  366.         that.contentIntent = this.contentIntent; 
  367.         that.deleteIntent = this.deleteIntent; 
  368.         that.fullScreenIntent = this.fullScreenIntent; 
  369.  
  370.         if (this.tickerText !=null) { 
  371.             that.tickerText = this.tickerText.toString(); 
  372.         } 
  373.         if (this.contentView !=null) { 
  374.             that.contentView = this.contentView.clone(); 
  375.         } 
  376.         that.iconLevel = that.iconLevel; 
  377.         that.sound = this.sound; // android.net.Uri is immutable 
  378.         that.audioStreamType = this.audioStreamType; 
  379.  
  380.         final long[] vibrate =this.vibrate; 
  381.         if (vibrate != null) { 
  382.             final int N = vibrate.length; 
  383.             final long[] vib = that.vibrate =newlong[N]; 
  384.             System.arraycopy(vibrate, 0, vib, 0, N); 
  385.         } 
  386.  
  387.         that.ledARGB = this.ledARGB; 
  388.         that.ledOnMS = this.ledOnMS; 
  389.         that.ledOffMS = this.ledOffMS; 
  390.         that.defaults = this.defaults; 
  391.          
  392.         that.flags = this.flags; 
  393.  
  394.         return that; 
  395.     } 
  396.  
  397.     public int describeContents() { 
  398.         return 0
  399.     } 
  400.  
  401.     /**
  402.      * Flatten this notification from a parcel.
  403.      */ 
  404.     public void writeToParcel(Parcel parcel,int flags) 
  405.     { 
  406.         parcel.writeInt(1); 
  407.  
  408.         parcel.writeLong(when); 
  409.         parcel.writeInt(icon); 
  410.         parcel.writeInt(number); 
  411.         if (contentIntent != null) { 
  412.             parcel.writeInt(1); 
  413.             contentIntent.writeToParcel(parcel, 0); 
  414.         } else
  415.             parcel.writeInt(0); 
  416.         } 
  417.         if (deleteIntent != null) { 
  418.             parcel.writeInt(1); 
  419.             deleteIntent.writeToParcel(parcel, 0); 
  420.         } else
  421.             parcel.writeInt(0); 
  422.         } 
  423.         if (tickerText != null) { 
  424.             parcel.writeInt(1); 
  425.             TextUtils.writeToParcel(tickerText, parcel, flags); 
  426.         } else
  427.             parcel.writeInt(0); 
  428.         } 
  429.         if (contentView != null) { 
  430.             parcel.writeInt(1); 
  431.             contentView.writeToParcel(parcel, 0); 
  432.         } else
  433.             parcel.writeInt(0); 
  434.         } 
  435.  
  436.         parcel.writeInt(defaults); 
  437.         parcel.writeInt(this.flags); 
  438.  
  439.         if (sound != null) { 
  440.             parcel.writeInt(1); 
  441.             sound.writeToParcel(parcel, 0); 
  442.         } else
  443.             parcel.writeInt(0); 
  444.         } 
  445.         parcel.writeInt(audioStreamType); 
  446.         parcel.writeLongArray(vibrate); 
  447.         parcel.writeInt(ledARGB); 
  448.         parcel.writeInt(ledOnMS); 
  449.         parcel.writeInt(ledOffMS); 
  450.         parcel.writeInt(iconLevel); 
  451.  
  452.         if (fullScreenIntent !=null) { 
  453.             parcel.writeInt(1); 
  454.             fullScreenIntent.writeToParcel(parcel, 0); 
  455.         } else
  456.             parcel.writeInt(0); 
  457.         } 
  458.     } 
  459.  
  460.     /**
  461.      * Parcelable.Creator that instantiates Notification objects
  462.      */ 
  463.     public staticfinal Parcelable.Creator<Notification> CREATOR 
  464.             = new Parcelable.Creator<Notification>() 
  465.     { 
  466.         public Notification createFromParcel(Parcel parcel) 
  467.         { 
  468.             return new Notification(parcel); 
  469.         } 
  470.  
  471.         public Notification[] newArray(int size) 
  472.         { 
  473.             return new Notification[size]; 
  474.         } 
  475.     }; 
  476.  
  477.     /**
  478.      * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
  479.      * layout.
  480.      *
  481.      * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
  482.      * in the view.</p>
  483.      * @param context       The context for your application / activity.
  484.      * @param contentTitle The title that goes in the expanded entry.
  485.      * @param contentText  The text that goes in the expanded entry.
  486.      * @param contentIntent The intent to launch when the user clicks the expanded notification.
  487.      * If this is an activity, it must include the
  488.      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
  489.      * that you take care of task management as described in
  490.      * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application Fundamentals: Activities and Tasks</a>.
  491.      */ 
  492.     public void setLatestEventInfo(Context context, 
  493.             CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) { 
  494.         RemoteViews contentView = new RemoteViews(context.getPackageName(), 
  495.                 com.android.internal.R.layout.status_bar_latest_event_content); 
  496.         if (this.icon !=0) { 
  497.             contentView.setImageViewResource(com.android.internal.R.id.icon,this.icon); 
  498.         } 
  499.         if (contentTitle != null) { 
  500.             contentView.setTextViewText(com.android.internal.R.id.title, contentTitle); 
  501.         } 
  502.         if (contentText !=null) { 
  503.             contentView.setTextViewText(com.android.internal.R.id.text, contentText); 
  504.         } 
  505.         if (this.when !=0) { 
  506.             contentView.setLong(com.android.internal.R.id.time,"setTime", when); 
  507.         } 
  508.  
  509.         this.contentView = contentView; 
  510.         this.contentIntent = contentIntent; 
  511.     } 
  512.  
  513.     @Override 
  514.     public String toString() { 
  515.         StringBuilder sb = new StringBuilder(); 
  516.         sb.append("Notification(vibrate="); 
  517.         if (this.vibrate !=null) { 
  518.             int N = this.vibrate.length-1
  519.             sb.append("["); 
  520.             for (int i=0; i<N; i++) { 
  521.                 sb.append(this.vibrate[i]); 
  522.                 sb.append(','); 
  523.             } 
  524.             if (N != -1) { 
  525.                 sb.append(this.vibrate[N]); 
  526.             } 
  527.             sb.append("]"); 
  528.         } else if ((this.defaults & DEFAULT_VIBRATE) !=0) { 
  529.             sb.append("default"); 
  530.         } else
  531.             sb.append("null"); 
  532.         } 
  533.         sb.append(",sound="); 
  534.         if (this.sound !=null) { 
  535.             sb.append(this.sound.toString()); 
  536.         } else if ((this.defaults & DEFAULT_SOUND) !=0) { 
  537.             sb.append("default"); 
  538.         } else
  539.             sb.append("null"); 
  540.         } 
  541.         sb.append(",defaults=0x"); 
  542.         sb.append(Integer.toHexString(this.defaults)); 
  543.         sb.append(",flags=0x"); 
  544.         sb.append(Integer.toHexString(this.flags)); 
  545.         sb.append(")"); 
  546.         return sb.toString(); 
  547.     } 
/* * 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 Notification implements Parcelable { /** * Use all default values (where applicable). */ public static final int DEFAULT_ALL = ~0; /** * Use the default notification sound. This will ignore any given * {@link #sound}. * * @see #defaults */ public static final int DEFAULT_SOUND = 1; /** * Use the default notification vibrate. This will ignore any given * {@link #vibrate}. Using phone vibration requires the * {@link android.Manifest.permission#VIBRATE VIBRATE} permission. * * @see #defaults */ public static final int DEFAULT_VIBRATE = 2; /** * Use the default notification lights. This will ignore the * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or * {@link #ledOnMS}. * * @see #defaults */ public static final int DEFAULT_LIGHTS = 4; /** * The timestamp for the notification. The icons and expanded views * are sorted by this key. */ public long when; /** * The resource id of a drawable to use as the icon in the status bar. */ public int icon; /** * The number of events that this notification represents. For example, in a new mail * notification, this could be the number of unread messages. This number is superimposed over * the icon in the status bar. If the number is 0 or negative, it is not shown in the status * bar. */ public int number; /** * The intent to execute when the expanded status entry is clicked. If * this is an activity, it must include the * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires * that you take care of task management as described in the <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 static final int STREAM_DEFAULT = -1; /** * The audio stream type to use when playing the sound. * Should be one of the STREAM_ constants from * {@link android.media.AudioManager}. */ public int audioStreamType = STREAM_DEFAULT; /** * The pattern with which to vibrate. * * <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 static final int FLAG_SHOW_LIGHTS = 0x00000001; /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if this notification is in reference to something that is ongoing, * like a phone call. It should not be set if this notification is in * reference to something that happened at a particular point in time, * like a missed phone call. */ public static final int FLAG_ONGOING_EVENT = 0x00000002; /** * Bit to be bitwise-ored into the {@link #flags} field that if set, * the audio will be repeated until the notification is * cancelled or the notification window is opened. */ public static final int FLAG_INSISTENT = 0x00000004; /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if you want the sound and/or vibration play each time the * notification is sent, even if it has not been canceled before that. */ public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008; /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if the notification should be canceled when it is clicked by the * user. */ public static final int FLAG_AUTO_CANCEL = 0x00000010; /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if the notification should not be canceled when the user clicks * the Clear all button. */ public static final int FLAG_NO_CLEAR = 0x00000020; /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if this notification represents a currently running service. This * will normally be set for you by {@link Service#startForeground}. */ public static final int FLAG_FOREGROUND_SERVICE = 0x00000040; public int flags; /** * Constructs a Notification object with everything set to 0. */ public Notification() { this.when = System.currentTimeMillis(); } /** * @deprecated use {@link #Notification(int,CharSequence,long)} and {@link #setLatestEventInfo}. * @hide */ public Notification(Context context, int icon, CharSequence tickerText, long when, CharSequence contentTitle, CharSequence contentText, Intent contentIntent) { this.when = when; this.icon = icon; this.tickerText = tickerText; setLatestEventInfo(context, contentTitle, contentText, PendingIntent.getActivity(context, 0, contentIntent, 0)); } /** * Constructs a Notification object with the information needed to * have a status bar icon without the standard expanded view. * * @param icon The resource id of the icon to put in the status bar. * @param tickerText The text that flows by in the status bar when the notification first * activates. * @param when The time to show in the time field. In the System.currentTimeMillis * timebase. */ public Notification(int icon, CharSequence tickerText, long when) { this.icon = icon; this.tickerText = tickerText; this.when = when; } /** * Unflatten the notification from a parcel. */ public Notification(Parcel parcel) { int version = parcel.readInt(); when = parcel.readLong(); icon = parcel.readInt(); number = parcel.readInt(); if (parcel.readInt() != 0) { contentIntent = PendingIntent.CREATOR.createFromParcel(parcel); } if (parcel.readInt() != 0) { deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel); } if (parcel.readInt() != 0) { tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); } if (parcel.readInt() != 0) { contentView = RemoteViews.CREATOR.createFromParcel(parcel); } defaults = parcel.readInt(); flags = parcel.readInt(); if (parcel.readInt() != 0) { sound = Uri.CREATOR.createFromParcel(parcel); } audioStreamType = parcel.readInt(); vibrate = parcel.createLongArray(); ledARGB = parcel.readInt(); ledOnMS = parcel.readInt(); ledOffMS = parcel.readInt(); iconLevel = parcel.readInt(); if (parcel.readInt() != 0) { fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel); } } public Notification clone() { Notification that = new Notification(); that.when = this.when; that.icon = this.icon; that.number = this.number; // PendingIntents are global, so there's no reason (or way) to clone them. that.contentIntent = this.contentIntent; that.deleteIntent = this.deleteIntent; that.fullScreenIntent = this.fullScreenIntent; if (this.tickerText != null) { that.tickerText = this.tickerText.toString(); } if (this.contentView != null) { that.contentView = this.contentView.clone(); } that.iconLevel = that.iconLevel; that.sound = this.sound; // android.net.Uri is immutable that.audioStreamType = this.audioStreamType; final long[] vibrate = this.vibrate; if (vibrate != null) { final int N = vibrate.length; final long[] vib = that.vibrate = new long[N]; System.arraycopy(vibrate, 0, vib, 0, N); } that.ledARGB = this.ledARGB; that.ledOnMS = this.ledOnMS; that.ledOffMS = this.ledOffMS; that.defaults = this.defaults; that.flags = this.flags; return that; } public int describeContents() { return 0; } /** * Flatten this notification from a parcel. */ public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(1); parcel.writeLong(when); parcel.writeInt(icon); parcel.writeInt(number); if (contentIntent != null) { parcel.writeInt(1); contentIntent.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } if (deleteIntent != null) { parcel.writeInt(1); deleteIntent.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } if (tickerText != null) { parcel.writeInt(1); TextUtils.writeToParcel(tickerText, parcel, flags); } else { parcel.writeInt(0); } if (contentView != null) { parcel.writeInt(1); contentView.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } parcel.writeInt(defaults); parcel.writeInt(this.flags); if (sound != null) { parcel.writeInt(1); sound.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } parcel.writeInt(audioStreamType); parcel.writeLongArray(vibrate); parcel.writeInt(ledARGB); parcel.writeInt(ledOnMS); parcel.writeInt(ledOffMS); parcel.writeInt(iconLevel); if (fullScreenIntent != null) { parcel.writeInt(1); fullScreenIntent.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } } /** * Parcelable.Creator that instantiates Notification objects */ public static final Parcelable.Creator<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类中,真的找到了我需要的,

[java] view plain copy print ?
  1. /*
  2. * Copyright (C) 2007 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. *      http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */ 
  16.  
  17. package com.android.server; 
  18.  
  19. import com.android.internal.statusbar.StatusBarNotification; 
  20. import com.android.server.StatusBarManagerService; 
  21.  
  22. import android.app.ActivityManagerNative; 
  23. import android.app.IActivityManager; 
  24. import android.app.INotificationManager; 
  25. import android.app.ITransientNotification; 
  26. import android.app.Notification; 
  27. import android.app.NotificationManager; 
  28. import android.app.PendingIntent; 
  29. import android.app.StatusBarManager; 
  30. import android.content.BroadcastReceiver; 
  31. import android.content.ComponentName; 
  32. import android.content.ContentResolver; 
  33. import android.content.Context; 
  34. import android.content.Intent; 
  35. import android.content.IntentFilter; 
  36. import android.content.pm.ApplicationInfo; 
  37. import android.content.pm.PackageManager; 
  38. import android.content.pm.PackageManager.NameNotFoundException; 
  39. import android.content.res.Resources; 
  40. import android.database.ContentObserver; 
  41. import android.hardware.usb.UsbManager; 
  42. import android.media.AudioManager; 
  43. import android.net.Uri; 
  44. import android.os.BatteryManager; 
  45. import android.os.Bundle; 
  46. import android.os.Binder; 
  47. import android.os.Handler; 
  48. import android.os.IBinder; 
  49. import android.os.Message; 
  50. import android.os.Power; 
  51. import android.os.Process; 
  52. import android.os.RemoteException; 
  53. import android.os.SystemProperties; 
  54. import android.os.Vibrator; 
  55. import android.os.SystemClock; 
  56. import android.provider.Settings; 
  57. import android.telephony.TelephonyManager; 
  58. import android.text.TextUtils; 
  59. import android.util.EventLog; 
  60. import android.util.Slog; 
  61. import android.util.Log; 
  62. import android.view.accessibility.AccessibilityEvent; 
  63. import android.view.accessibility.AccessibilityManager; 
  64. import android.widget.Toast; 
  65.  
  66. import java.io.FileDescriptor; 
  67. import java.io.PrintWriter; 
  68. import java.util.ArrayList; 
  69. import java.util.Arrays; 
  70. import java.util.HashMap; 
  71.  
  72. /** {@hide} */ 
  73. public class NotificationManagerServiceextends INotificationManager.Stub 
  74.     private staticfinal String TAG ="NotificationService"
  75.     private staticfinalboolean DBG =false
  76.  
  77.     private staticfinalint MAX_PACKAGE_NOTIFICATIONS =50
  78.     private staticfinalint NOTIFICATION_REQUEST_INTERVAL =30000;// 30 seconds 
  79.     private staticfinalint MAX_PACKAGE_NOTIFICATION_REQUESTS =500
  80.  
  81.     // message codes 
  82.     private staticfinalint MESSAGE_TIMEOUT =2
  83.  
  84.     private staticfinalint LONG_DELAY =3500;// 3.5 seconds 
  85.     private staticfinalint SHORT_DELAY =2000;// 2 seconds 
  86.  
  87.     private staticfinallong[] DEFAULT_VIBRATE_PATTERN = {0,250,250,250}; 
  88.  
  89.     private staticfinalint DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 
  90.  
  91.     final Context mContext; 
  92.     final IActivityManager mAm; 
  93.     final IBinder mForegroundToken =new Binder(); 
  94.  
  95.     private WorkerHandler mHandler; 
  96.     private StatusBarManagerService mStatusBar; 
  97.     private LightsService mLightsService; 
  98.     private LightsService.Light mBatteryLight; 
  99.     private LightsService.Light mNotificationLight; 
  100.     private LightsService.Light mAttentionLight; 
  101.  
  102.     private int mDefaultNotificationColor; 
  103.     private int mDefaultNotificationLedOn; 
  104.     private int mDefaultNotificationLedOff; 
  105.  
  106.     private NotificationRecord mSoundNotification; 
  107.     private NotificationPlayer mSound; 
  108.     private boolean mSystemReady; 
  109.     private int mDisabledNotifications; 
  110.  
  111.     private NotificationRecord mVibrateNotification; 
  112.     private Vibrator mVibrator =new Vibrator(); 
  113.  
  114.     // for enabling and disabling notification pulse behavior 
  115.     private boolean mScreenOn =true
  116.     private boolean mInCall =false
  117.     private boolean mNotificationPulseEnabled; 
  118.     // This is true if we have received a new notification while the screen is off 
  119.     // (that is, if mLedNotification was set while the screen was off) 
  120.     // This is reset to false when the screen is turned on. 
  121.     private boolean mPendingPulseNotification; 
  122.  
  123.     // for adb connected notifications 
  124.     private boolean mAdbNotificationShown =false
  125.     private Notification mAdbNotification; 
  126.  
  127.     private final ArrayList<NotificationRecord> mNotificationList = 
  128.             new ArrayList<NotificationRecord>(); 
  129.  
  130.     private class PackageRequestInfo { 
  131.         int requestCount; 
  132.         long lastPostTime; 
  133.     } 
  134.  
  135.     private final HashMap<String, PackageRequestInfo> mPackageInfo = 
  136.             new HashMap<String, PackageRequestInfo>(); 
  137.  
  138.     private ArrayList<ToastRecord> mToastQueue; 
  139.  
  140.     private ArrayList<NotificationRecord> mLights =new ArrayList<NotificationRecord>(); 
  141.  
  142.     private boolean mBatteryCharging; 
  143.     private boolean mBatteryLow; 
  144.     private boolean mBatteryFull; 
  145.     private NotificationRecord mLedNotification; 
  146.  
  147.     private staticfinalint BATTERY_LOW_ARGB =0xFFFF0000;// Charging Low - red solid on 
  148.     private staticfinalint BATTERY_MEDIUM_ARGB =0xFFFFFF00;   // Charging - orange solid on 
  149.     private staticfinalint BATTERY_FULL_ARGB =0xFF00FF00;// Charging Full - green solid on 
  150.     private staticfinalint BATTERY_BLINK_ON =125
  151.     private staticfinalint BATTERY_BLINK_OFF =2875
  152.  
  153.     private static String idDebugString(Context baseContext, String packageName,int id) { 
  154.         Context c = null
  155.  
  156.         if (packageName !=null) { 
  157.             try
  158.                 c = baseContext.createPackageContext(packageName,0); 
  159.             } catch (NameNotFoundException e) { 
  160.                 c = baseContext; 
  161.             } 
  162.         } else
  163.             c = baseContext; 
  164.         } 
  165.  
  166.         String pkg; 
  167.         String type; 
  168.         String name; 
  169.  
  170.         Resources r = c.getResources(); 
  171.         try
  172.             return r.getResourceName(id); 
  173.         } catch (Resources.NotFoundException e) { 
  174.             return "<name unknown>"
  175.         } 
  176.     } 
  177.  
  178.     private staticfinalclass NotificationRecord 
  179.     { 
  180.         final String pkg; 
  181.         final String tag; 
  182.         final int id; 
  183.         final int uid; 
  184.         final int initialPid; 
  185.         ITransientNotification callback; 
  186.         int duration; 
  187.         final Notification notification; 
  188.         IBinder statusBarKey; 
  189.  
  190.         NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, 
  191.                 Notification notification) 
  192.         { 
  193.             this.pkg = pkg; 
  194.             this.tag = tag; 
  195.             this.id = id; 
  196.             this.uid = uid; 
  197.             this.initialPid = initialPid; 
  198.             this.notification = notification; 
  199.         } 
  200.  
  201.         void dump(PrintWriter pw, String prefix, Context baseContext) { 
  202.             pw.println(prefix + this); 
  203.             pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon) 
  204.                     + " / " + idDebugString(baseContext,this.pkg, notification.icon)); 
  205.             pw.println(prefix + "  contentIntent=" + notification.contentIntent); 
  206.             pw.println(prefix + "  deleteIntent=" + notification.deleteIntent); 
  207.             pw.println(prefix + "  tickerText=" + notification.tickerText); 
  208.             pw.println(prefix + "  contentView=" + notification.contentView); 
  209.             pw.println(prefix + "  defaults=0x" + Integer.toHexString(notification.defaults)); 
  210.             pw.println(prefix + "  flags=0x" + Integer.toHexString(notification.flags)); 
  211.             pw.println(prefix + "  sound=" + notification.sound); 
  212.             pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate)); 
  213.             pw.println(prefix + "  ledARGB=0x" + Integer.toHexString(notification.ledARGB) 
  214.                     + " ledOnMS=" + notification.ledOnMS 
  215.                     + " ledOffMS=" + notification.ledOffMS); 
  216.         } 
  217.  
  218.         @Override 
  219.         public final String toString() 
  220.         { 
  221.             return "NotificationRecord{" 
  222.                 + Integer.toHexString(System.identityHashCode(this)) 
  223.                 + " pkg=" + pkg 
  224.                 + " id=" + Integer.toHexString(id) 
  225.                 + " tag=" + tag + "}"
  226.         } 
  227.     } 
  228.  
  229.     private staticfinalclass ToastRecord 
  230.     { 
  231.         final int pid; 
  232.         final String pkg; 
  233.         final ITransientNotification callback; 
  234.         int duration; 
  235.  
  236.         ToastRecord(int pid, String pkg, ITransientNotification callback,int duration) 
  237.         { 
  238.             this.pid = pid; 
  239.             this.pkg = pkg; 
  240.             this.callback = callback; 
  241.             this.duration = duration; 
  242.         } 
  243.  
  244.         void update(int duration) { 
  245.             this.duration = duration; 
  246.         } 
  247.  
  248.         void dump(PrintWriter pw, String prefix) { 
  249.             pw.println(prefix + this); 
  250.         } 
  251.  
  252.         @Override 
  253.         public final String toString() 
  254.         { 
  255.             return "ToastRecord{" 
  256.                 + Integer.toHexString(System.identityHashCode(this)) 
  257.                 + " pkg=" + pkg 
  258.                 + " callback=" + callback 
  259.                 + " duration=" + duration; 
  260.         } 
  261.     } 
  262.  
  263.     private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks 
  264.             = new StatusBarManagerService.NotificationCallbacks() { 
  265.  
  266.         public void onSetDisabled(int status) { 
  267.             synchronized (mNotificationList) { 
  268.                 mDisabledNotifications = status; 
  269.                 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) !=0) { 
  270.                     // cancel whatever's going on 
  271.                     long identity = Binder.clearCallingIdentity(); 
  272.                     try
  273.                         mSound.stop(); 
  274.                     } 
  275.                     finally
  276.                         Binder.restoreCallingIdentity(identity); 
  277.                     } 
  278.  
  279.                     identity = Binder.clearCallingIdentity(); 
  280.                     try
  281.                         mVibrator.cancel(); 
  282.                     } 
  283.                     finally
  284.                         Binder.restoreCallingIdentity(identity); 
  285.                     } 
  286.                 } 
  287.             } 
  288.         } 
  289.  
  290.         public void onClearAll() { 
  291.             cancelAll(); 
  292.         } 
  293.  
  294.         public void onNotificationClick(String pkg, String tag,int id) { 
  295.             cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL, 
  296.                     Notification.FLAG_FOREGROUND_SERVICE); 
  297.         } 
  298.  
  299.         public void onPanelRevealed() { 
  300.             synchronized (mNotificationList) { 
  301.                 // sound 
  302.                 mSoundNotification = null
  303.                 long identity = Binder.clearCallingIdentity(); 
  304.                 try
  305.                     mSound.stop(); 
  306.                 } 
  307.                 finally
  308.                     Binder.restoreCallingIdentity(identity); 
  309.                 } 
  310.  
  311.                 // vibrate 
  312.                 mVibrateNotification = null
  313.                 identity = Binder.clearCallingIdentity(); 
  314.                 try
  315.                     mVibrator.cancel(); 
  316.                 } 
  317.                 finally
  318.                     Binder.restoreCallingIdentity(identity); 
  319.                 } 
  320.  
  321.                 // light 
  322.                 mLights.clear(); 
  323.                 mLedNotification = null
  324.                 updateLightsLocked(); 
  325.             } 
  326.         } 
  327.  
  328.         public void onNotificationError(String pkg, String tag,int id, 
  329.                 int uid, int initialPid, String message) { 
  330.             Slog.d(TAG, "onNotification error pkg=" + pkg +" tag=" + tag +" id=" + id 
  331.                     + "; will crashApplication(uid=" + uid +", pid=" + initialPid +")"); 
  332.             cancelNotification(pkg, tag, id, 0, 0); 
  333.             long ident = Binder.clearCallingIdentity(); 
  334.             try
  335.                 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 
  336.                         "Bad notification posted from package " + pkg 
  337.                         + ": " + message); 
  338.             } catch (RemoteException e) { 
  339.             } 
  340.             Binder.restoreCallingIdentity(ident); 
  341.         } 
  342.     }; 
  343.  
  344.     private BroadcastReceiver mIntentReceiver =new BroadcastReceiver() { 
  345.         @Override 
  346.         public void onReceive(Context context, Intent intent) { 
  347.             String action = intent.getAction(); 
  348.  
  349.             boolean queryRestart = false
  350.              
  351.             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { 
  352.                 boolean batteryCharging = (intent.getIntExtra("plugged",0) !=0); 
  353.                 int level = intent.getIntExtra("level", -1); 
  354.                 boolean batteryLow = (level >=0 && level <= Power.LOW_BATTERY_THRESHOLD); 
  355.                 int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN); 
  356.                 boolean batteryFull = (status == BatteryManager.BATTERY_STATUS_FULL || level >=90); 
  357.  
  358.                 if (batteryCharging != mBatteryCharging || 
  359.                         batteryLow != mBatteryLow || 
  360.                         batteryFull != mBatteryFull) { 
  361.                     mBatteryCharging = batteryCharging; 
  362.                     mBatteryLow = batteryLow; 
  363.                     mBatteryFull = batteryFull; 
  364.                     updateLights(); 
  365.                 } 
  366.             } else if (action.equals(UsbManager.ACTION_USB_STATE)) { 
  367.                 Bundle extras = intent.getExtras(); 
  368.                 boolean usbConnected = extras.getBoolean(UsbManager.USB_CONNECTED); 
  369.                 boolean adbEnabled = (UsbManager.USB_FUNCTION_ENABLED.equals( 
  370.                                     extras.getString(UsbManager.USB_FUNCTION_ADB))); 
  371.                 updateAdbNotification(usbConnected && adbEnabled); 
  372.             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) 
  373.                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 
  374.                     || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 
  375.                     || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 
  376.                 String pkgList[] = null
  377.                 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 
  378.                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 
  379.                 } else if (queryRestart) { 
  380.                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 
  381.                 } else
  382.                     Uri uri = intent.getData(); 
  383.                     if (uri == null) { 
  384.                         return
  385.                     } 
  386.                     String pkgName = uri.getSchemeSpecificPart(); 
  387.                     if (pkgName == null) { 
  388.                         return
  389.                     } 
  390.                     pkgList = new String[]{pkgName}; 
  391.                 } 
  392.                 if (pkgList !=null && (pkgList.length >0)) { 
  393.                     for (String pkgName : pkgList) { 
  394.                         cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart); 
  395.                     } 
  396.                 } 
  397.             } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 
  398.                 mScreenOn = true
  399.                 updateNotificationPulse(); 
  400.             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 
  401.                 mScreenOn = false
  402.                 updateNotificationPulse(); 
  403.             } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 
  404.                 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK)); 
  405.                 updateNotificationPulse(); 
  406.             } 
  407.         } 
  408.     }; 
  409.  
  410.     class SettingsObserverextends ContentObserver { 
  411.         SettingsObserver(Handler handler) { 
  412.             super(handler); 
  413.         } 
  414.  
  415.         void observe() { 
  416.             ContentResolver resolver = mContext.getContentResolver(); 
  417.             resolver.registerContentObserver(Settings.System.getUriFor( 
  418.                     Settings.System.NOTIFICATION_LIGHT_PULSE),false,this); 
  419.             update(); 
  420.         } 
  421.  
  422.         @Override public void onChange(boolean selfChange) { 
  423.             update(); 
  424.         } 
  425.  
  426.         public void update() { 
  427.             ContentResolver resolver = mContext.getContentResolver(); 
  428.             boolean pulseEnabled = Settings.System.getInt(resolver, 
  429.                         Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0
  430.             if (mNotificationPulseEnabled != pulseEnabled) { 
  431.                 mNotificationPulseEnabled = pulseEnabled; 
  432.                 updateNotificationPulse(); 
  433.             } 
  434.         } 
  435.     } 
  436.  
  437.     NotificationManagerService(Context context, StatusBarManagerService statusBar, 
  438.             LightsService lights) 
  439.     { 
  440.         super(); 
  441.         mContext = context; 
  442.         mLightsService = lights; 
  443.         mAm = ActivityManagerNative.getDefault(); 
  444.         mSound = new NotificationPlayer(TAG); 
  445.         mSound.setUsesWakeLock(context); 
  446.         mToastQueue = new ArrayList<ToastRecord>(); 
  447.         mHandler = new WorkerHandler(); 
  448.  
  449.         mStatusBar = statusBar; 
  450.         statusBar.setNotificationCallbacks(mNotificationCallbacks); 
  451.  
  452.         mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY); 
  453.         mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS); 
  454.         mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); 
  455.  
  456.         Resources resources = mContext.getResources(); 
  457.         mDefaultNotificationColor = resources.getColor( 
  458.                 com.android.internal.R.color.config_defaultNotificationColor); 
  459.         mDefaultNotificationLedOn = resources.getInteger( 
  460.                 com.android.internal.R.integer.config_defaultNotificationLedOn); 
  461.         mDefaultNotificationLedOff = resources.getInteger( 
  462.                 com.android.internal.R.integer.config_defaultNotificationLedOff); 
  463.  
  464.         // Don't start allowing notifications until the setup wizard has run once. 
  465.         // After that, including subsequent boots, init with notifications turned on. 
  466.         // This works on the first boot because the setup wizard will toggle this 
  467.         // flag at least once and we'll go back to 0 after that. 
  468.         if (0 == Settings.Secure.getInt(mContext.getContentResolver(), 
  469.                     Settings.Secure.DEVICE_PROVISIONED, 0)) { 
  470.             mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS; 
  471.         } 
  472.  
  473.         // register for battery changed notifications 
  474.         IntentFilter filter = new IntentFilter(); 
  475.         filter.addAction(Intent.ACTION_BATTERY_CHANGED); 
  476.         filter.addAction(UsbManager.ACTION_USB_STATE); 
  477.         filter.addAction(Intent.ACTION_SCREEN_ON); 
  478.         filter.addAction(Intent.ACTION_SCREEN_OFF); 
  479.         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 
  480.         mContext.registerReceiver(mIntentReceiver, filter); 
  481.         IntentFilter pkgFilter = new IntentFilter(); 
  482.         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 
  483.         pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 
  484.         pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 
  485.         pkgFilter.addDataScheme("package"); 
  486.         mContext.registerReceiver(mIntentReceiver, pkgFilter); 
  487.         IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 
  488.         mContext.registerReceiver(mIntentReceiver, sdFilter); 
  489.  
  490.         SettingsObserver observer = new SettingsObserver(mHandler); 
  491.         observer.observe(); 
  492.     } 
  493.  
  494.     void systemReady() { 
  495.         // no beeping until we're basically done booting 
  496.         mSystemReady = true
  497.     } 
  498.  
  499.     // Toasts 
  500.     // ============================================================================ 
  501.     public void enqueueToast(String pkg, ITransientNotification callback,int duration) 
  502.     { 
  503.         if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback +" duration=" + duration); 
  504.  
  505.         if (pkg == null || callback ==null) { 
  506.             Slog.e(TAG, "Not doing toast. pkg=" + pkg +" callback=" + callback); 
  507.             return
  508.         } 
  509.  
  510.         synchronized (mToastQueue) { 
  511.             int callingPid = Binder.getCallingPid(); 
  512.             long callingId = Binder.clearCallingIdentity(); 
  513.             try
  514.                 ToastRecord record; 
  515.                 int index = indexOfToastLocked(pkg, callback); 
  516.                 // If it's already in the queue, we update it in place, we don't 
  517.                 // move it to the end of the queue. 
  518.                 if (index >=0) { 
  519.                     record = mToastQueue.get(index); 
  520.                     record.update(duration); 
  521.                 } else
  522.                     // Limit the number of toasts that any given package except the android 
  523.                     // package can enqueue.  Prevents DOS attacks and deals with leaks. 
  524.                     if (!"android".equals(pkg)) { 
  525.                         int count = 0
  526.                         finalint N = mToastQueue.size(); 
  527.                         for (int i=0; i<N; i++) { 
  528.                              final ToastRecord r = mToastQueue.get(i); 
  529.                              if (r.pkg.equals(pkg)) { 
  530.                                  count++; 
  531.                                  if (count >= MAX_PACKAGE_NOTIFICATIONS) { 
  532.                                      Slog.e(TAG, "Package has already posted " + count 
  533.                                             + " toasts. Not showing more. Package=" + pkg); 
  534.                                      return
  535.                                  } 
  536.                              } 
  537.                         } 
  538.                     } 
  539.  
  540.                     record = new ToastRecord(callingPid, pkg, callback, duration); 
  541.                     mToastQueue.add(record); 
  542.                     index = mToastQueue.size() - 1
  543.                     keepProcessAliveLocked(callingPid); 
  544.                 } 
  545.                 // If it's at index 0, it's the current toast.  It doesn't matter if it's 
  546.                 // new or just been updated.  Call back and tell it to show itself. 
  547.                 // If the callback fails, this will remove it from the list, so don't 
  548.                 // assume that it's valid after this. 
  549.                 if (index == 0) { 
  550.                     showNextToastLocked(); 
  551.                 } 
  552.             } finally
  553.                 Binder.restoreCallingIdentity(callingId); 
  554.             } 
  555.         } 
  556.     } 
  557.  
  558.     public void cancelToast(String pkg, ITransientNotification callback) { 
  559.         Slog.i(TAG, "cancelToast pkg=" + pkg +" callback=" + callback); 
  560.  
  561.         if (pkg == null || callback ==null) { 
  562.             Slog.e(TAG, "Not cancelling notification. pkg=" + pkg +" callback=" + callback); 
  563.             return
  564.         } 
  565.  
  566.         synchronized (mToastQueue) { 
  567.             long callingId = Binder.clearCallingIdentity(); 
  568.             try
  569.                 int index = indexOfToastLocked(pkg, callback); 
  570.                 if (index >=0) { 
  571.                     cancelToastLocked(index); 
  572.                 } else
  573.                     Slog.w(TAG, "Toast already cancelled. pkg=" + pkg +" callback=" + callback); 
  574.                 } 
  575.             } finally
  576.                 Binder.restoreCallingIdentity(callingId); 
  577.             } 
  578.         } 
  579.     } 
  580.  
  581.     private void showNextToastLocked() { 
  582.         ToastRecord record = mToastQueue.get(0); 
  583.         while (record != null) { 
  584.             if (DBG) Slog.d(TAG,"Show pkg=" + record.pkg +" callback=" + record.callback); 
  585.             try
  586.                 record.callback.show(); 
  587.                 scheduleTimeoutLocked(record, false); 
  588.                 return
  589.             } catch (RemoteException e) { 
  590.                 Slog.w(TAG, "Object died trying to show notification " + record.callback 
  591.                         + " in package " + record.pkg); 
  592.                 // remove it from the list and let the process die 
  593.                 int index = mToastQueue.indexOf(record); 
  594.                 if (index >=0) { 
  595.                     mToastQueue.remove(index); 
  596.                 } 
  597.                 keepProcessAliveLocked(record.pid); 
  598.                 if (mToastQueue.size() >0) { 
  599.                     record = mToastQueue.get(0); 
  600.                 } else
  601.                     record = null
  602.                 } 
  603.             } 
  604.         } 
  605.     } 
  606.  
  607.     private void cancelToastLocked(int index) { 
  608.         ToastRecord record = mToastQueue.get(index); 
  609.         try
  610.             record.callback.hide(); 
  611.         } catch (RemoteException e) { 
  612.             Slog.w(TAG, "Object died trying to hide notification " + record.callback 
  613.                     + " in package " + record.pkg); 
  614.             // don't worry about this, we're about to remove it from 
  615.             // the list anyway 
  616.         } 
  617.         mToastQueue.remove(index); 
  618.         keepProcessAliveLocked(record.pid); 
  619.         if (mToastQueue.size() > 0) { 
  620.             // Show the next one. If the callback fails, this will remove 
  621.             // it from the list, so don't assume that the list hasn't changed 
  622.             // after this point. 
  623.             showNextToastLocked(); 
  624.         } 
  625.     } 
  626.  
  627.     private void scheduleTimeoutLocked(ToastRecord r,boolean immediate) 
  628.     { 
  629.         Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 
  630.         long delay = immediate ?0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY); 
  631.         mHandler.removeCallbacksAndMessages(r); 
  632.         mHandler.sendMessageDelayed(m, delay); 
  633.     } 
  634.  
  635.     private void handleTimeout(ToastRecord record) 
  636.     { 
  637.         if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 
  638.         synchronized (mToastQueue) { 
  639.             int index = indexOfToastLocked(record.pkg, record.callback); 
  640.             if (index >=0) { 
  641.                 cancelToastLocked(index); 
  642.             } 
  643.         } 
  644.     } 
  645.  
  646.     // lock on mToastQueue 
  647.     private int indexOfToastLocked(String pkg, ITransientNotification callback) 
  648.     { 
  649.         IBinder cbak = callback.asBinder(); 
  650.         ArrayList<ToastRecord> list = mToastQueue; 
  651.         int len = list.size(); 
  652.         for (int i=0; i<len; i++) { 
  653.             ToastRecord r = list.get(i); 
  654.             if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 
  655.                 return i; 
  656.             } 
  657.         } 
  658.         return -1
  659.     } 
  660.  
  661.     // lock on mToastQueue 
  662.     private void keepProcessAliveLocked(int pid) 
  663.     { 
  664.         int toastCount =0;// toasts from this pid 
  665.         ArrayList<ToastRecord> list = mToastQueue; 
  666.         int N = list.size(); 
  667.         for (int i=0; i<N; i++) { 
  668.             ToastRecord r = list.get(i); 
  669.             if (r.pid == pid) { 
  670.                 toastCount++; 
  671.             } 
  672.         } 
  673.         try
  674.             mAm.setProcessForeground(mForegroundToken, pid, toastCount >0); 
  675.         } catch (RemoteException e) { 
  676.             // Shouldn't happen. 
  677.         } 
  678.     } 
  679.  
  680.     private finalclass WorkerHandlerextends Handler 
  681.     { 
  682.         @Override 
  683.         public void handleMessage(Message msg) 
  684.         { 
  685.             switch (msg.what) 
  686.             { 
  687.                 case MESSAGE_TIMEOUT: 
  688.                     handleTimeout((ToastRecord)msg.obj); 
  689.                     break
  690.             } 
  691.         } 
  692.     } 
  693.  
  694.  
  695.     // Notifications 
  696.     // ============================================================================ 
  697.     public void enqueueNotification(String pkg,int id, Notification notification,int[] idOut) 
  698.     { 
  699.         enqueueNotificationWithTag(pkg, null/* tag */, id, notification, idOut); 
  700.     } 
  701.  
  702.     public void enqueueNotificationWithTag(String pkg, String tag,int id, Notification notification, 
  703.             int[] idOut) 
  704.     { 
  705.         enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(), 
  706.                 tag, id, notification, idOut); 
  707.     } 
  708.  
  709.     // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the 
  710.     // uid/pid of another application) 
  711.     public void enqueueNotificationInternal(String pkg,int callingUid,int callingPid, 
  712.             String tag, int id, Notification notification,int[] idOut) 
  713.     { 
  714.         checkIncomingCall(pkg); 
  715.  
  716.         // Limit the number of notifications that any given package except the android 
  717.         // package can enqueue.  Prevents DOS attacks and deals with leaks. 
  718.         if (!"android".equals(pkg)) { 
  719.             synchronized (mNotificationList) { 
  720.                 int count =0
  721.                 final int N = mNotificationList.size(); 
  722.                 for (int i=0; i<N; i++) { 
  723.                     final NotificationRecord r = mNotificationList.get(i); 
  724.                     if (r.pkg.equals(pkg)) { 
  725.                         count++; 
  726.                         if (count >= MAX_PACKAGE_NOTIFICATIONS) { 
  727.                             Slog.e(TAG, "Package has already posted " + count 
  728.                                     + " notifications.  Not showing more.  package=" + pkg); 
  729.                             return
  730.                         } 
  731.                     } 
  732.                 } 
  733.             } 
  734.         } 
  735.  
  736.         // Limit the number of notification requests, notify and cancel that 
  737.         // a package can post. The MAX_PACKAGE_NOTIFICATIONS doesn't work for 
  738.         // packages which notify and quickly cancel it and do this in an 
  739.         // iteration 
  740.         if (!"android".equals(pkg)) { 
  741.             synchronized (mPackageInfo) { 
  742.                 if (!mPackageInfo.containsKey(pkg)) { 
  743.                     final PackageRequestInfo pInfo =new PackageRequestInfo(); 
  744.                     pInfo.requestCount = 1
  745.                     pInfo.lastPostTime = SystemClock.elapsedRealtime(); 
  746.                     mPackageInfo.put(pkg,pInfo); 
  747.                 } 
  748.                 else
  749.                     final PackageRequestInfo pInfo = mPackageInfo.get(pkg); 
  750.                     finallong currentTime = SystemClock.elapsedRealtime(); 
  751.                     if ((currentTime - pInfo.lastPostTime) <= NOTIFICATION_REQUEST_INTERVAL) { 
  752.                          // Keep track of requests posted within last 30 seconds 
  753.                          pInfo.requestCount++; 
  754.                     } 
  755.                     else
  756.                          pInfo.requestCount = 1
  757.                          pInfo.lastPostTime = SystemClock.elapsedRealtime(); 
  758.                     } 
  759.  
  760.                     if (pInfo.requestCount >= MAX_PACKAGE_NOTIFICATION_REQUESTS) { 
  761.                         // 500 requests within a span of 30 seconds is high 
  762.                         if (pInfo.requestCount%MAX_PACKAGE_NOTIFICATION_REQUESTS ==0) { 
  763.                             Slog.e(TAG, "Package has already posted too many notifications. " 
  764.                                     + "Not showing more.  package=" + pkg); 
  765.                         } 
  766.                         return
  767.                     } 
  768.                 } 
  769.             } 
  770.         } 
  771.  
  772.         // This conditional is a dirty hack to limit the logging done on 
  773.         //     behalf of the download manager without affecting other apps. 
  774.         if (!pkg.equals("com.android.providers.downloads"
  775.                 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 
  776.             EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, notification.toString()); 
  777.         } 
  778.  
  779.         if (pkg == null || notification ==null) { 
  780.             throw new IllegalArgumentException("null not allowed: pkg=" + pkg 
  781.                     + " id=" + id + " notification=" + notification); 
  782.         } 
  783.         if (notification.icon != 0) { 
  784.             if (notification.contentView ==null) { 
  785.                 throw new IllegalArgumentException("contentView required: pkg=" + pkg 
  786.                         + " id=" + id +" notification=" + notification); 
  787.             } 
  788.             if (notification.contentIntent ==null) { 
  789.                 throw new IllegalArgumentException("contentIntent required: pkg=" + pkg 
  790.                         + " id=" + id +" notification=" + notification); 
  791.             } 
  792.         } 
  793.  
  794.         synchronized (mNotificationList) { 
  795.             NotificationRecord r = new NotificationRecord(pkg, tag, id, 
  796.                     callingUid, callingPid, notification); 
  797.             NotificationRecord old = null
  798.  
  799.             int index = indexOfNotificationLocked(pkg, tag, id); 
  800.             if (index < 0) { 
  801.                 mNotificationList.add(r); 
  802.             } else
  803.                 old = mNotificationList.remove(index); 
  804.                 mNotificationList.add(index, r); 
  805.                 // Make sure we don't lose the foreground service state. 
  806.                 if (old !=null) { 
  807.                     notification.flags |= 
  808.                         old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE; 
  809.                 } 
  810.             } 
  811.  
  812.             // Ensure if this is a foreground service that the proper additional 
  813.             // flags are set. 
  814.             if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) !=0) { 
  815.                 notification.flags |= Notification.FLAG_ONGOING_EVENT 
  816.                         | Notification.FLAG_NO_CLEAR; 
  817.             } 
  818.  
  819.             if (notification.icon !=0) { 
  820.                 StatusBarNotification n = new StatusBarNotification(pkg, id, tag, 
  821.                         r.uid, r.initialPid, notification); 
  822.                 if (old !=null && old.statusBarKey !=null) { 
  823.                     r.statusBarKey = old.statusBarKey; 
  824.                     long identity = Binder.clearCallingIdentity(); 
  825.                     try
  826.                         mStatusBar.updateNotification(r.statusBarKey, n); 
  827.                     } 
  828.                     finally
  829.                         Binder.restoreCallingIdentity(identity); 
  830.                     } 
  831.                 } else
  832.                     long identity = Binder.clearCallingIdentity(); 
  833.                     try
  834.                         r.statusBarKey = mStatusBar.addNotification(n); 
  835.                         mAttentionLight.pulse(); 
  836.                     } 
  837.                     finally
  838.                         Binder.restoreCallingIdentity(identity); 
  839.                     } 
  840.                 } 
  841.                 sendAccessibilityEvent(notification, pkg); 
  842.             } else
  843.                 if (old != null && old.statusBarKey !=null) { 
  844.                     long identity = Binder.clearCallingIdentity(); 
  845.                     try
  846.                         mStatusBar.removeNotification(old.statusBarKey); 
  847.                     } 
  848.                     finally
  849.                         Binder.restoreCallingIdentity(identity); 
  850.                     } 
  851.                 } 
  852.             } 
  853.  
  854.             // If we're not supposed to beep, vibrate, etc. then don't. 
  855.             if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) ==0
  856.                     && (!(old != null 
  857.                         && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) !=0 )) 
  858.                     && mSystemReady) { 
  859.  
  860.                 final AudioManager audioManager = (AudioManager) mContext 
  861.                 .getSystemService(Context.AUDIO_SERVICE); 
  862.                 // sound 
  863.                 final boolean useDefaultSound = 
  864.                     (notification.defaults & Notification.DEFAULT_SOUND) !=0
  865.                 if (useDefaultSound || notification.sound !=null) { 
  866.                     Uri uri; 
  867.                     if (useDefaultSound) { 
  868.                         uri = Settings.System.DEFAULT_NOTIFICATION_URI; 
  869.                     } else
  870.                         uri = notification.sound; 
  871.                     } 
  872.                     boolean looping = (notification.flags & Notification.FLAG_INSISTENT) !=0
  873.                     int audioStreamType; 
  874.                     if (notification.audioStreamType >=0) { 
  875.                         audioStreamType = notification.audioStreamType; 
  876.                     } else
  877.                         audioStreamType = DEFAULT_STREAM_TYPE; 
  878.                     } 
  879.                     mSoundNotification = r; 
  880.                     // do not play notifications if stream volume is 0 
  881.                     // (typically because ringer mode is silent). 
  882.                     if (audioManager.getStreamVolume(audioStreamType) !=0) { 
  883.                         long identity = Binder.clearCallingIdentity(); 
  884.                         try
  885.                             mSound.play(mContext, uri, looping, audioStreamType); 
  886.                         } 
  887.                         finally
  888.                             Binder.restoreCallingIdentity(identity); 
  889.                         } 
  890.                     } 
  891.                 } 
  892.  
  893.                 // vibrate 
  894.                 final boolean useDefaultVibrate = 
  895.                     (notification.defaults & Notification.DEFAULT_VIBRATE) !=0
  896.                 if ((useDefaultVibrate || notification.vibrate !=null
  897.                         && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) { 
  898.                     mVibrateNotification = r; 
  899.  
  900.                     mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN 
  901.                                                         : notification.vibrate, 
  902.                               ((notification.flags & Notification.FLAG_INSISTENT) !=0) ?0: -1); 
  903.                 } 
  904.             } 
  905.  
  906.             // this option doesn't shut off the lights 
  907.  
  908.             // light 
  909.             // the most recent thing gets the light 
  910.             mLights.remove(old); 
  911.             if (mLedNotification == old) { 
  912.                 mLedNotification = null
  913.             } 
  914.             //Slog.i(TAG, "notification.lights=" 
  915.             //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); 
  916.             if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) !=0) { 
  917.                 mLights.add(r); 
  918.                 updateLightsLocked(); 
  919.             } else
  920.                 if (old !=null 
  921.                         && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) !=0)) { 
  922.                     updateLightsLocked(); 
  923.                 } 
  924.             } 
  925.         } 
  926.  
  927.         idOut[0] = id; 
  928.     } 
  929.  
  930.     private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 
  931.         AccessibilityManager manager = AccessibilityManager.getInstance(mContext); 
  932.         if (!manager.isEnabled()) { 
  933.             return
  934.         } 
  935.  
  936.         AccessibilityEvent event = 
  937.             AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 
  938.         event.setPackageName(packageName); 
  939.         event.setClassName(Notification.class.getName()); 
  940.         event.setParcelableData(notification); 
  941.         CharSequence tickerText = notification.tickerText; 
  942.         if (!TextUtils.isEmpty(tickerText)) { 
  943.             event.getText().add(tickerText); 
  944.         } 
  945.  
  946.         manager.sendAccessibilityEvent(event); 
  947.     } 
  948.  
  949.     private void cancelNotificationLocked(NotificationRecord r) { 
  950.         // status bar 
  951.         if (r.notification.icon != 0) { 
  952.             long identity = Binder.clearCallingIdentity(); 
  953.             try
  954.                 mStatusBar.removeNotification(r.statusBarKey); 
  955.             } 
  956.             finally
  957.                 Binder.restoreCallingIdentity(identity); 
  958.             } 
  959.             r.statusBarKey = null
  960.         } 
  961.  
  962.         // sound 
  963.         if (mSoundNotification == r) { 
  964.             mSoundNotification = null
  965.             long identity = Binder.clearCallingIdentity(); 
  966.             try
  967.                 mSound.stop(); 
  968.             } 
  969.             finally
  970.                 Binder.restoreCallingIdentity(identity); 
  971.             } 
  972.         } 
  973.  
  974.         // vibrate 
  975.         if (mVibrateNotification == r) { 
  976.             mVibrateNotification = null
  977.             long identity = Binder.clearCallingIdentity(); 
  978.             try
  979.                 mVibrator.cancel(); 
  980.             } 
  981.             finally
  982.                 Binder.restoreCallingIdentity(identity); 
  983.             } 
  984.         } 
  985.  
  986.         // light 
  987.         mLights.remove(r); 
  988.         if (mLedNotification == r) { 
  989.             mLedNotification = null
  990.         } 
  991.     } 
  992.  
  993.     /**
  994.      * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
  995.      * and none of the {@code mustNotHaveFlags}.
  996.      */ 
  997.     private void cancelNotification(String pkg, String tag,int id,int mustHaveFlags, 
  998.             int mustNotHaveFlags) { 
  999.         EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags); 
  1000.  
  1001.         synchronized (mNotificationList) { 
  1002.             int index = indexOfNotificationLocked(pkg, tag, id); 
  1003.             if (index >= 0) { 
  1004.                 NotificationRecord r = mNotificationList.get(index); 
  1005.  
  1006.                 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 
  1007.                     return
  1008.                 } 
  1009.                 if ((r.notification.flags & mustNotHaveFlags) !=0) { 
  1010.                     return
  1011.                 } 
  1012.  
  1013.                 mNotificationList.remove(index); 
  1014.  
  1015.                 cancelNotificationLocked(r); 
  1016.                 updateLightsLocked(); 
  1017.             } 
  1018.         } 
  1019.     } 
  1020.  
  1021.     /**
  1022.      * Cancels all notifications from a given package that have all of the
  1023.      * {@code mustHaveFlags}.
  1024.      */ 
  1025.     boolean cancelAllNotificationsInt(String pkg,int mustHaveFlags, 
  1026.             int mustNotHaveFlags,boolean doit) { 
  1027.         EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags); 
  1028.  
  1029.         synchronized (mNotificationList) { 
  1030.             final int N = mNotificationList.size(); 
  1031.             boolean canceledSomething =false
  1032.             for (int i = N-1; i >=0; --i) { 
  1033.                 NotificationRecord r = mNotificationList.get(i); 
  1034.                 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 
  1035.                     continue
  1036.                 } 
  1037.                 if ((r.notification.flags & mustNotHaveFlags) !=0) { 
  1038.                     continue
  1039.                 } 
  1040.                 if (!r.pkg.equals(pkg)) { 
  1041.                     continue
  1042.                 } 
  1043.                 canceledSomething = true
  1044.                 if (!doit) { 
  1045.                     return true
  1046.                 } 
  1047.                 mNotificationList.remove(i); 
  1048.                 cancelNotificationLocked(r); 
  1049.             } 
  1050.             if (canceledSomething) { 
  1051.                 updateLightsLocked(); 
  1052.             } 
  1053.             return canceledSomething; 
  1054.         } 
  1055.     } 
  1056.  
  1057.  
  1058.     public void cancelNotification(String pkg,int id) { 
  1059.         cancelNotificationWithTag(pkg, null/* tag */, id); 
  1060.     } 
  1061.  
  1062.     public void cancelNotificationWithTag(String pkg, String tag,int id) { 
  1063.         checkIncomingCall(pkg); 
  1064.  
  1065.         // Limit the number of notification requests, notify and cancel that 
  1066.         // a package can post. The MAX_PACKAGE_NOTIFICATIONS doesn't work for 
  1067.         // packages which notify and quickly cancel it and do this in an 
  1068.         // iteration 
  1069.         synchronized (mPackageInfo) { 
  1070.             if (!"android".equals(pkg) && mPackageInfo.containsKey(pkg)) { 
  1071.                 final PackageRequestInfo pInfo = mPackageInfo.get(pkg); 
  1072.                 final long currentTime = SystemClock.elapsedRealtime(); 
  1073.                 if ((currentTime - pInfo.lastPostTime) <= NOTIFICATION_REQUEST_INTERVAL) { 
  1074.                     // Keep track of requests posted within last 30 seconds 
  1075.                     pInfo.requestCount++; 
  1076.                 } 
  1077.                 else
  1078.                     pInfo.requestCount = 1
  1079.                     pInfo.lastPostTime = SystemClock.elapsedRealtime(); 
  1080.                 } 
  1081.             } 
  1082.         } 
  1083.  
  1084.         // Don't allow client applications to cancel foreground service notis. 
  1085.         cancelNotification(pkg, tag, id, 0
  1086.                 Binder.getCallingUid() == Process.SYSTEM_UID 
  1087.                 ? 0 : Notification.FLAG_FOREGROUND_SERVICE); 
  1088.     } 
  1089.  
  1090.     public void cancelAllNotifications(String pkg) { 
  1091.         checkIncomingCall(pkg); 
  1092.  
  1093.         // Calling from user space, don't allow the canceling of actively 
  1094.         // running foreground services. 
  1095.         cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE,true); 
  1096.     } 
  1097.  
  1098.     void checkIncomingCall(String pkg) { 
  1099.         int uid = Binder.getCallingUid(); 
  1100.         if (uid == Process.SYSTEM_UID || uid ==0) { 
  1101.             return
  1102.         } 
  1103.         try
  1104.             ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo( 
  1105.                     pkg, 0); 
  1106.             if (ai.uid != uid) { 
  1107.                 throw new SecurityException("Calling uid " + uid +" gave package" 
  1108.                         + pkg + " which is owned by uid " + ai.uid); 
  1109.             } 
  1110.         } catch (PackageManager.NameNotFoundException e) { 
  1111.             throw new SecurityException("Unknown package " + pkg); 
  1112.         } 
  1113.     } 
  1114.  
  1115.     void cancelAll() { 
  1116.         synchronized (mNotificationList) { 
  1117.             final int N = mNotificationList.size(); 
  1118.             for (int i=N-1; i>=0; i--) { 
  1119.                 NotificationRecord r = mNotificationList.get(i); 
  1120.  
  1121.                 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT 
  1122.                                 | Notification.FLAG_NO_CLEAR)) ==0) { 
  1123.                     if (r.notification.deleteIntent !=null) { 
  1124.                         try
  1125.                             r.notification.deleteIntent.send(); 
  1126.                         } catch (PendingIntent.CanceledException ex) { 
  1127.                             // do nothing - there's no relevant way to recover, and 
  1128.                             //     no reason to let this propagate 
  1129.                             Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex); 
  1130.                         } 
  1131.                     } 
  1132.                     mNotificationList.remove(i); 
  1133.                     cancelNotificationLocked(r); 
  1134.                 } 
  1135.             } 
  1136.  
  1137.             updateLightsLocked(); 
  1138.         } 
  1139.     } 
  1140.  
  1141.     private void updateLights() { 
  1142.         synchronized (mNotificationList) { 
  1143.             updateLightsLocked(); 
  1144.         } 
  1145.     } 
  1146.  
  1147.     // lock on mNotificationList 
  1148.     private void updateLightsLocked() 
  1149.     { 
  1150.         // Battery low always shows, other states only show if charging. 
  1151.         if (mBatteryLow) { 
  1152.             if (mBatteryCharging) { 
  1153.                 mBatteryLight.setColor(BATTERY_LOW_ARGB); 
  1154.             } else
  1155.                 // Flash when battery is low and not charging 
  1156.                 mBatteryLight.setFlashing(BATTERY_LOW_ARGB, LightsService.LIGHT_FLASH_TIMED, 
  1157.                         BATTERY_BLINK_ON, BATTERY_BLINK_OFF); 
  1158.             } 
  1159.         } else if (mBatteryCharging) { 
  1160.             if (mBatteryFull) { 
  1161.                 mBatteryLight.setColor(BATTERY_FULL_ARGB); 
  1162.             } else
  1163.                 mBatteryLight.setColor(BATTERY_MEDIUM_ARGB); 
  1164.             } 
  1165.         } else
  1166.             mBatteryLight.turnOff(); 
  1167.         } 
  1168.  
  1169.         // clear pending pulse notification if screen is on 
  1170.         if (mScreenOn || mLedNotification ==null) { 
  1171.             mPendingPulseNotification = false
  1172.         } 
  1173.  
  1174.         // handle notification lights 
  1175.         if (mLedNotification == null) { 
  1176.             // get next notification, if any 
  1177.             int n = mLights.size(); 
  1178.             if (n > 0) { 
  1179.                 mLedNotification = mLights.get(n-1); 
  1180.             } 
  1181.             if (mLedNotification != null && !mScreenOn) { 
  1182.                 mPendingPulseNotification = true
  1183.             } 
  1184.         } 
  1185.  
  1186.         // we only flash if screen is off and persistent pulsing is enabled 
  1187.         // and we are not currently in a call 
  1188.         if (!mPendingPulseNotification || mScreenOn || mInCall) { 
  1189.             mNotificationLight.turnOff(); 
  1190.         } else
  1191.             int ledARGB = mLedNotification.notification.ledARGB; 
  1192.             int ledOnMS = mLedNotification.notification.ledOnMS; 
  1193.             int ledOffMS = mLedNotification.notification.ledOffMS; 
  1194.             if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) !=0) { 
  1195.                 ledARGB = mDefaultNotificationColor; 
  1196.                 ledOnMS = mDefaultNotificationLedOn; 
  1197.                 ledOffMS = mDefaultNotificationLedOff; 
  1198.             } 
  1199.             if (mNotificationPulseEnabled) { 
  1200.                 // pulse repeatedly 
  1201.                 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, 
  1202.                         ledOnMS, ledOffMS); 
  1203.             } else
  1204.                 // pulse only once 
  1205.                 mNotificationLight.pulse(ledARGB, ledOnMS); 
  1206.             } 
  1207.         } 
  1208.     } 
  1209.  
  1210.     // lock on mNotificationList 
  1211.     private int indexOfNotificationLocked(String pkg, String tag,int id) 
  1212.     { 
  1213.         ArrayList<NotificationRecord> list = mNotificationList; 
  1214.         final int len = list.size(); 
  1215.         for (int i=0; i<len; i++) { 
  1216.             NotificationRecord r = list.get(i); 
  1217.             if (tag == null) { 
  1218.                 if (r.tag !=null) { 
  1219.                     continue
  1220.                 } 
  1221.             } else
  1222.                 if (!tag.equals(r.tag)) { 
  1223.                     continue
  1224.                 } 
  1225.             } 
  1226.             if (r.id == id && r.pkg.equals(pkg)) { 
  1227.                 return i; 
  1228.             } 
  1229.         } 
  1230.         return -1
  1231.     } 
  1232.  
  1233.     // This is here instead of StatusBarPolicy because it is an important 
  1234.     // security feature that we don't want people customizing the platform 
  1235.     // to accidentally lose. 
  1236.     private void updateAdbNotification(boolean adbEnabled) { 
  1237.         if (adbEnabled) { 
  1238.             if ("0".equals(SystemProperties.get("persist.adb.notify"))) { 
  1239.                 return
  1240.             } 
  1241.             if (!mAdbNotificationShown) { 
  1242.                 NotificationManager notificationManager = (NotificationManager) mContext 
  1243.                         .getSystemService(Context.NOTIFICATION_SERVICE); 
  1244.                 if (notificationManager !=null) { 
  1245.                     Resources r = mContext.getResources(); 
  1246.                     CharSequence title = r.getText( 
  1247.                             com.android.internal.R.string.adb_active_notification_title); 
  1248.                     CharSequence message = r.getText( 
  1249.                             com.android.internal.R.string.adb_active_notification_message); 
  1250.  
  1251.                     if (mAdbNotification ==null) { 
  1252.                         mAdbNotification = new Notification(); 
  1253.                         mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb; 
  1254.                         mAdbNotification.when = 0
  1255.                         mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT; 
  1256.                         mAdbNotification.tickerText = title; 
  1257.                         mAdbNotification.defaults = 0; // please be quiet 
  1258.                         mAdbNotification.sound = null
  1259.                         mAdbNotification.vibrate = null
  1260.                     } 
  1261.  
  1262.                     Intent intent = new Intent( 
  1263.                             Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); 
  1264.                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 
  1265.                             Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 
  1266.                     // Note: we are hard-coding the component because this is 
  1267.                     // an important security UI that we don't want anyone 
  1268.                     // intercepting. 
  1269.                     intent.setComponent(new ComponentName("com.android.settings"
  1270.                             "com.android.settings.DevelopmentSettings")); 
  1271.                     PendingIntent pi = PendingIntent.getActivity(mContext,0
  1272.                             intent, 0); 
  1273.  
  1274.                     mAdbNotification.setLatestEventInfo(mContext, title, message, pi); 
  1275.  
  1276.                     mAdbNotificationShown = true
  1277.                     notificationManager.notify( 
  1278.                             com.android.internal.R.string.adb_active_notification_title, 
  1279.                             mAdbNotification); 
  1280.                 } 
  1281.             } 
  1282.  
  1283.         } else if (mAdbNotificationShown) { 
  1284.             NotificationManager notificationManager = (NotificationManager) mContext 
  1285.                     .getSystemService(Context.NOTIFICATION_SERVICE); 
  1286.             if (notificationManager !=null) { 
  1287.                 mAdbNotificationShown = false
  1288.                 notificationManager.cancel( 
  1289.                         com.android.internal.R.string.adb_active_notification_title); 
  1290.             } 
  1291.         } 
  1292.     } 
  1293.  
  1294.     private void updateNotificationPulse() { 
  1295.         synchronized (mNotificationList) { 
  1296.             updateLightsLocked(); 
  1297.         } 
  1298.     } 
  1299.  
  1300.     // ====================================================================== 
  1301.     @Override 
  1302.     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 
  1303.         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 
  1304.                 != PackageManager.PERMISSION_GRANTED) { 
  1305.             pw.println("Permission Denial: can't dump NotificationManager from from pid=" 
  1306.                     + Binder.getCallingPid() 
  1307.                     + ", uid=" + Binder.getCallingUid()); 
  1308.             return
  1309.         } 
  1310.  
  1311.         pw.println("Current Notification Manager state:"); 
  1312.  
  1313.         int N; 
  1314.  
  1315.         synchronized (mToastQueue) { 
  1316.             N = mToastQueue.size(); 
  1317.             if (N > 0) { 
  1318.                 pw.println("  Toast Queue:"); 
  1319.                 for (int i=0; i<N; i++) { 
  1320.                     mToastQueue.get(i).dump(pw, "    "); 
  1321.                 } 
  1322.                 pw.println("  "); 
  1323.             } 
  1324.  
  1325.         } 
  1326.  
  1327.         synchronized (mNotificationList) { 
  1328.             N = mNotificationList.size(); 
  1329.             if (N > 0) { 
  1330.                 pw.println("  Notification List:"); 
  1331.                 for (int i=0; i<N; i++) { 
  1332.                     mNotificationList.get(i).dump(pw, "    ", mContext); 
  1333.                 } 
  1334.                 pw.println("  "); 
  1335.             } 
  1336.  
  1337.             N = mLights.size(); 
  1338.             if (N > 0) { 
  1339.                 pw.println("  Lights List:"); 
  1340.                 for (int i=0; i<N; i++) { 
  1341.                     mLights.get(i).dump(pw, "    ", mContext); 
  1342.                 } 
  1343.                 pw.println("  "); 
  1344.             } 
  1345.  
  1346.             pw.println("  mSoundNotification=" + mSoundNotification); 
  1347.             pw.println("  mSound=" + mSound); 
  1348.             pw.println("  mVibrateNotification=" + mVibrateNotification); 
  1349.             pw.println("  mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications)); 
  1350.             pw.println("  mSystemReady=" + mSystemReady); 
  1351.         } 
  1352.     } 
/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server; import com.android.internal.statusbar.StatusBarNotification; import com.android.server.StatusBarManagerService; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.INotificationManager; import android.app.ITransientNotification; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.usb.UsbManager; import android.media.AudioManager; import android.net.Uri; import android.os.BatteryManager; import android.os.Bundle; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Power; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; import android.os.Vibrator; import android.os.SystemClock; import android.provider.Settings; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.EventLog; import android.util.Slog; import android.util.Log; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; /** {@hide} */ public class NotificationManagerService extends INotificationManager.Stub { private static final String TAG = "NotificationService"; private static final boolean DBG = false; private static final int MAX_PACKAGE_NOTIFICATIONS = 50; private static final int NOTIFICATION_REQUEST_INTERVAL = 30000; // 30 seconds private static final int MAX_PACKAGE_NOTIFICATION_REQUESTS = 500; // message codes private static final int MESSAGE_TIMEOUT = 2; private static final int LONG_DELAY = 3500; // 3.5 seconds private static final int SHORT_DELAY = 2000; // 2 seconds private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; final Context mContext; final IActivityManager mAm; final IBinder mForegroundToken = new Binder(); private WorkerHandler mHandler; private StatusBarManagerService mStatusBar; private LightsService mLightsService; private LightsService.Light mBatteryLight; private LightsService.Light mNotificationLight; private LightsService.Light mAttentionLight; private int mDefaultNotificationColor; private int mDefaultNotificationLedOn; private int mDefaultNotificationLedOff; private NotificationRecord mSoundNotification; private NotificationPlayer mSound; private boolean mSystemReady; private int mDisabledNotifications; private NotificationRecord mVibrateNotification; private Vibrator mVibrator = new Vibrator(); // for enabling and disabling notification pulse behavior private boolean mScreenOn = true; private boolean mInCall = false; private boolean mNotificationPulseEnabled; // This is true if we have received a new notification while the screen is off // (that is, if mLedNotification was set while the screen was off) // This is reset to false when the screen is turned on. private boolean mPendingPulseNotification; // for adb connected notifications private boolean mAdbNotificationShown = false; private Notification mAdbNotification; private final ArrayList<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 static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on private static final int BATTERY_BLINK_ON = 125; private static final int BATTERY_BLINK_OFF = 2875; private static String idDebugString(Context baseContext, String packageName, int id) { Context c = null; if (packageName != null) { try { c = baseContext.createPackageContext(packageName, 0); } catch (NameNotFoundException e) { c = baseContext; } } else { c = baseContext; } String pkg; String type; String name; Resources r = c.getResources(); try { return r.getResourceName(id); } catch (Resources.NotFoundException e) { return "<name unknown>"; } } private static final class NotificationRecord { final String pkg; final String tag; final int id; final int uid; final int initialPid; ITransientNotification callback; int duration; final Notification notification; IBinder statusBarKey; NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, Notification notification) { this.pkg = pkg; this.tag = tag; this.id = id; this.uid = uid; this.initialPid = initialPid; this.notification = notification; } void dump(PrintWriter pw, String prefix, Context baseContext) { pw.println(prefix + this); pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon) + " / " + idDebugString(baseContext, this.pkg, notification.icon)); pw.println(prefix + " contentIntent=" + notification.contentIntent); pw.println(prefix + " deleteIntent=" + notification.deleteIntent); pw.println(prefix + " tickerText=" + notification.tickerText); pw.println(prefix + " contentView=" + notification.contentView); pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults)); pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags)); pw.println(prefix + " sound=" + notification.sound); pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB) + " ledOnMS=" + notification.ledOnMS + " ledOffMS=" + notification.ledOffMS); } @Override public final String toString() { return "NotificationRecord{" + Integer.toHexString(System.identityHashCode(this)) + " pkg=" + pkg + " id=" + Integer.toHexString(id) + " tag=" + tag + "}"; } } private static final class ToastRecord { final int pid; final String pkg; final ITransientNotification callback; int duration; ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) { this.pid = pid; this.pkg = pkg; this.callback = callback; this.duration = duration; } void update(int duration) { this.duration = duration; } void dump(PrintWriter pw, String prefix) { pw.println(prefix + this); } @Override public final String toString() { return "ToastRecord{" + Integer.toHexString(System.identityHashCode(this)) + " pkg=" + pkg + " callback=" + callback + " duration=" + duration; } } private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks = new StatusBarManagerService.NotificationCallbacks() { public void onSetDisabled(int status) { synchronized (mNotificationList) { mDisabledNotifications = status; if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { // cancel whatever's going on long identity = Binder.clearCallingIdentity(); try { mSound.stop(); } finally { Binder.restoreCallingIdentity(identity); } identity = Binder.clearCallingIdentity(); try { mVibrator.cancel(); } finally { Binder.restoreCallingIdentity(identity); } } } } public void onClearAll() { cancelAll(); } public void onNotificationClick(String pkg, String tag, int id) { cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL, Notification.FLAG_FOREGROUND_SERVICE); } public void onPanelRevealed() { synchronized (mNotificationList) { // sound mSoundNotification = null; long identity = Binder.clearCallingIdentity(); try { mSound.stop(); } finally { Binder.restoreCallingIdentity(identity); } // vibrate mVibrateNotification = null; identity = Binder.clearCallingIdentity(); try { mVibrator.cancel(); } finally { Binder.restoreCallingIdentity(identity); } // light mLights.clear(); mLedNotification = null; updateLightsLocked(); } } public void onNotificationError(String pkg, String tag, int id, int uid, int initialPid, String message) { Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); cancelNotification(pkg, tag, id, 0, 0); long ident = Binder.clearCallingIdentity(); try { ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, "Bad notification posted from package " + pkg + ": " + message); } catch (RemoteException e) { } Binder.restoreCallingIdentity(ident); } }; private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); boolean queryRestart = false; if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0); int level = intent.getIntExtra("level", -1); boolean batteryLow = (level >= 0 && level <= Power.LOW_BATTERY_THRESHOLD); int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN); boolean batteryFull = (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90); if (batteryCharging != mBatteryCharging || batteryLow != mBatteryLow || batteryFull != mBatteryFull) { mBatteryCharging = batteryCharging; mBatteryLow = batteryLow; mBatteryFull = batteryFull; updateLights(); } } else if (action.equals(UsbManager.ACTION_USB_STATE)) { Bundle extras = intent.getExtras(); boolean usbConnected = extras.getBoolean(UsbManager.USB_CONNECTED); boolean adbEnabled = (UsbManager.USB_FUNCTION_ENABLED.equals( extras.getString(UsbManager.USB_FUNCTION_ADB))); updateAdbNotification(usbConnected && adbEnabled); } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals(Intent.ACTION_PACKAGE_RESTARTED) || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { String pkgList[] = null; if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } else if (queryRestart) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); } else { Uri uri = intent.getData(); if (uri == null) { return; } String pkgName = uri.getSchemeSpecificPart(); if (pkgName == null) { return; } pkgList = new String[]{pkgName}; } if (pkgList != null && (pkgList.length > 0)) { for (String pkgName : pkgList) { cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart); } } } else if (action.equals(Intent.ACTION_SCREEN_ON)) { mScreenOn = true; updateNotificationPulse(); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { mScreenOn = false; updateNotificationPulse(); } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK)); updateNotificationPulse(); } } }; class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { super(handler); } void observe() { ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.NOTIFICATION_LIGHT_PULSE), false, this); update(); } @Override public void onChange(boolean selfChange) { update(); } public void update() { ContentResolver resolver = mContext.getContentResolver(); boolean pulseEnabled = Settings.System.getInt(resolver, Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; if (mNotificationPulseEnabled != pulseEnabled) { mNotificationPulseEnabled = pulseEnabled; updateNotificationPulse(); } } } NotificationManagerService(Context context, StatusBarManagerService statusBar, LightsService lights) { super(); mContext = context; mLightsService = lights; mAm = ActivityManagerNative.getDefault(); mSound = new NotificationPlayer(TAG); mSound.setUsesWakeLock(context); mToastQueue = new ArrayList<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; final int 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 final class WorkerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_TIMEOUT: handleTimeout((ToastRecord)msg.obj); break; } } } // Notifications // ============================================================================ public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut) { enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut); } public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification, int[] idOut) { enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(), tag, id, notification, idOut); } // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the // uid/pid of another application) public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid, String tag, int id, Notification notification, int[] idOut) { checkIncomingCall(pkg); // Limit the number of notifications that any given package except the android // package can enqueue. Prevents DOS attacks and deals with leaks. if (!"android".equals(pkg)) { synchronized (mNotificationList) { int count = 0; final int N = mNotificationList.size(); for (int i=0; i<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); final long currentTime = SystemClock.elapsedRealtime(); if ((currentTime - pInfo.lastPostTime) <= NOTIFICATION_REQUEST_INTERVAL) { // Keep track of requests posted within last 30 seconds pInfo.requestCount++; } else { pInfo.requestCount = 1; pInfo.lastPostTime = SystemClock.elapsedRealtime(); } if (pInfo.requestCount >= MAX_PACKAGE_NOTIFICATION_REQUESTS) { // 500 requests within a span of 30 seconds is high if (pInfo.requestCount%MAX_PACKAGE_NOTIFICATION_REQUESTS == 0) { Slog.e(TAG, "Package has already posted too many notifications. " + "Not showing more. package=" + pkg); } return; } } } } // This conditional is a dirty hack to limit the logging done on // behalf of the download manager without affecting other apps. if (!pkg.equals("com.android.providers.downloads") || Log.isLoggable("DownloadManager", Log.VERBOSE)) { EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, notification.toString()); } if (pkg == null || notification == null) { throw new IllegalArgumentException("null not allowed: pkg=" + pkg + " id=" + id + " notification=" + notification); } if (notification.icon != 0) { if (notification.contentView == null) { throw new IllegalArgumentException("contentView required: pkg=" + pkg + " id=" + id + " notification=" + notification); } if (notification.contentIntent == null) { throw new IllegalArgumentException("contentIntent required: pkg=" + pkg + " id=" + id + " notification=" + notification); } } synchronized (mNotificationList) { NotificationRecord r = new NotificationRecord(pkg, tag, id, callingUid, callingPid, notification); NotificationRecord old = null; int index = indexOfNotificationLocked(pkg, tag, id); if (index < 0) { mNotificationList.add(r); } else { old = mNotificationList.remove(index); mNotificationList.add(index, r); // Make sure we don't lose the foreground service state. if (old != null) { notification.flags |= old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE; } } // Ensure if this is a foreground service that the proper additional // flags are set. if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR; } if (notification.icon != 0) { StatusBarNotification n = new StatusBarNotification(pkg, id, tag, r.uid, r.initialPid, notification); if (old != null && old.statusBarKey != null) { r.statusBarKey = old.statusBarKey; long identity = Binder.clearCallingIdentity(); try { mStatusBar.updateNotification(r.statusBarKey, n); } finally { Binder.restoreCallingIdentity(identity); } } else { long identity = Binder.clearCallingIdentity(); try { r.statusBarKey = mStatusBar.addNotification(n); mAttentionLight.pulse(); } finally { Binder.restoreCallingIdentity(identity); } } sendAccessibilityEvent(notification, pkg); } else { if (old != null && old.statusBarKey != null) { long identity = Binder.clearCallingIdentity(); try { mStatusBar.removeNotification(old.statusBarKey); } finally { Binder.restoreCallingIdentity(identity); } } } // If we're not supposed to beep, vibrate, etc. then don't. if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) && (!(old != null && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) && mSystemReady) { final AudioManager audioManager = (AudioManager) mContext .getSystemService(Context.AUDIO_SERVICE); // sound final boolean useDefaultSound = (notification.defaults & Notification.DEFAULT_SOUND) != 0; if (useDefaultSound || notification.sound != null) { Uri uri; if (useDefaultSound) { uri = Settings.System.DEFAULT_NOTIFICATION_URI; } else { uri = notification.sound; } boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; int audioStreamType; if (notification.audioStreamType >= 0) { audioStreamType = notification.audioStreamType; } else { audioStreamType = DEFAULT_STREAM_TYPE; } mSoundNotification = r; // do not play notifications if stream volume is 0 // (typically because ringer mode is silent). if (audioManager.getStreamVolume(audioStreamType) != 0) { long identity = Binder.clearCallingIdentity(); try { mSound.play(mContext, uri, looping, audioStreamType); } finally { Binder.restoreCallingIdentity(identity); } } } // vibrate final boolean useDefaultVibrate = (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; if ((useDefaultVibrate || notification.vibrate != null) && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) { mVibrateNotification = r; mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN : notification.vibrate, ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); } } // this option doesn't shut off the lights // light // the most recent thing gets the light mLights.remove(old); if (mLedNotification == old) { mLedNotification = null; } //Slog.i(TAG, "notification.lights=" // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { mLights.add(r); updateLightsLocked(); } else { if (old != null && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) { updateLightsLocked(); } } } idOut[0] = id; } private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { AccessibilityManager manager = AccessibilityManager.getInstance(mContext); if (!manager.isEnabled()) { return; } AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); event.setPackageName(packageName); event.setClassName(Notification.class.getName()); event.setParcelableData(notification); CharSequence tickerText = notification.tickerText; if (!TextUtils.isEmpty(tickerText)) { event.getText().add(tickerText); } manager.sendAccessibilityEvent(event); } private void cancelNotificationLocked(NotificationRecord r) { // status bar if (r.notification.icon != 0) { long identity = Binder.clearCallingIdentity(); try { mStatusBar.removeNotification(r.statusBarKey); } finally { Binder.restoreCallingIdentity(identity); } r.statusBarKey = null; } // sound if (mSoundNotification == r) { mSoundNotification = null; long identity = Binder.clearCallingIdentity(); try { mSound.stop(); } finally { Binder.restoreCallingIdentity(identity); } } // vibrate if (mVibrateNotification == r) { mVibrateNotification = null; long identity = Binder.clearCallingIdentity(); try { mVibrator.cancel(); } finally { Binder.restoreCallingIdentity(identity); } } // light mLights.remove(r); if (mLedNotification == r) { mLedNotification = null; } } /** * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} * and none of the {@code mustNotHaveFlags}. */ private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags, int mustNotHaveFlags) { EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags); synchronized (mNotificationList) { int index = indexOfNotificationLocked(pkg, tag, id); if (index >= 0) { NotificationRecord r = mNotificationList.get(index); if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { return; } if ((r.notification.flags & mustNotHaveFlags) != 0) { return; } mNotificationList.remove(index); cancelNotificationLocked(r); updateLightsLocked(); } } } /** * Cancels all notifications from a given package that have all of the * {@code mustHaveFlags}. */ boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags, int mustNotHaveFlags, boolean doit) { EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags); synchronized (mNotificationList) { final int N = mNotificationList.size(); boolean canceledSomething = false; for (int i = N-1; i >= 0; --i) { NotificationRecord r = mNotificationList.get(i); if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { continue; } if ((r.notification.flags & mustNotHaveFlags) != 0) { continue; } if (!r.pkg.equals(pkg)) { continue; } canceledSomething = true; if (!doit) { return true; } mNotificationList.remove(i); cancelNotificationLocked(r); } if (canceledSomething) { updateLightsLocked(); } return canceledSomething; } } public void cancelNotification(String pkg, int id) { cancelNotificationWithTag(pkg, null /* tag */, id); } public void cancelNotificationWithTag(String pkg, String tag, int id) { checkIncomingCall(pkg); // Limit the number of notification requests, notify and cancel that // a package can post. The MAX_PACKAGE_NOTIFICATIONS doesn't work for // packages which notify and quickly cancel it and do this in an // iteration synchronized (mPackageInfo) { if (!"android".equals(pkg) && mPackageInfo.containsKey(pkg)) { final PackageRequestInfo pInfo = mPackageInfo.get(pkg); final long currentTime = SystemClock.elapsedRealtime(); if ((currentTime - pInfo.lastPostTime) <= NOTIFICATION_REQUEST_INTERVAL) { // Keep track of requests posted within last 30 seconds pInfo.requestCount++; } else { pInfo.requestCount = 1; pInfo.lastPostTime = SystemClock.elapsedRealtime(); } } } // Don't allow client applications to cancel foreground service notis. cancelNotification(pkg, tag, id, 0, Binder.getCallingUid() == Process.SYSTEM_UID ? 0 : Notification.FLAG_FOREGROUND_SERVICE); } public void cancelAllNotifications(String pkg) { checkIncomingCall(pkg); // Calling from user space, don't allow the canceling of actively // running foreground services. cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true); } void checkIncomingCall(String pkg) { int uid = Binder.getCallingUid(); if (uid == Process.SYSTEM_UID || uid == 0) { return; } try { ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo( pkg, 0); if (ai.uid != uid) { throw new SecurityException("Calling uid " + uid + " gave package" + pkg + " which is owned by uid " + ai.uid); } } catch (PackageManager.NameNotFoundException e) { throw new SecurityException("Unknown package " + pkg); } } void cancelAll() { synchronized (mNotificationList) { final int N = mNotificationList.size(); for (int i=N-1; i>=0; i--) { NotificationRecord r = mNotificationList.get(i); if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR)) == 0) { if (r.notification.deleteIntent != null) { try { r.notification.deleteIntent.send(); } catch (PendingIntent.CanceledException ex) { // do nothing - there's no relevant way to recover, and // no reason to let this propagate Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex); } } mNotificationList.remove(i); cancelNotificationLocked(r); } } updateLightsLocked(); } } private void updateLights() { synchronized (mNotificationList) { updateLightsLocked(); } } // lock on mNotificationList private void updateLightsLocked() { // Battery low always shows, other states only show if charging. if (mBatteryLow) { if (mBatteryCharging) { mBatteryLight.setColor(BATTERY_LOW_ARGB); } else { // Flash when battery is low and not charging mBatteryLight.setFlashing(BATTERY_LOW_ARGB, LightsService.LIGHT_FLASH_TIMED, BATTERY_BLINK_ON, BATTERY_BLINK_OFF); } } else if (mBatteryCharging) { if (mBatteryFull) { mBatteryLight.setColor(BATTERY_FULL_ARGB); } else { mBatteryLight.setColor(BATTERY_MEDIUM_ARGB); } } else { mBatteryLight.turnOff(); } // clear pending pulse notification if screen is on if (mScreenOn || mLedNotification == null) { mPendingPulseNotification = false; } // handle notification lights if (mLedNotification == null) { // get next notification, if any int n = mLights.size(); if (n > 0) { mLedNotification = mLights.get(n-1); } if (mLedNotification != null && !mScreenOn) { mPendingPulseNotification = true; } } // we only flash if screen is off and persistent pulsing is enabled // and we are not currently in a call if (!mPendingPulseNotification || mScreenOn || mInCall) { mNotificationLight.turnOff(); } else { int ledARGB = mLedNotification.notification.ledARGB; int ledOnMS = mLedNotification.notification.ledOnMS; int ledOffMS = mLedNotification.notification.ledOffMS; if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { ledARGB = mDefaultNotificationColor; ledOnMS = mDefaultNotificationLedOn; ledOffMS = mDefaultNotificationLedOff; } if (mNotificationPulseEnabled) { // pulse repeatedly mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, ledOnMS, ledOffMS); } else { // pulse only once mNotificationLight.pulse(ledARGB, ledOnMS); } } } // lock on mNotificationList private int indexOfNotificationLocked(String pkg, String tag, int id) { ArrayList<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行有这段代码:

[java] view plain copy print ?
  1. // Don't start allowing notifications until the setup wizard has run once. 
  2.      // After that, including subsequent boots, init with notifications turned on. 
  3.      // This works on the first boot because the setup wizard will toggle this 
  4.      // flag at least once and we'll go back to 0 after that. 
  5.      if (0 == Settings.Secure.getInt(mContext.getContentResolver(), 
  6.                  Settings.Secure.DEVICE_PROVISIONED, 0)) { 
  7.          mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS; 
  8.      } 
// 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进行改变的:

[java] view plain copy print ?
  1. private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks 
  2.         = new StatusBarManagerService.NotificationCallbacks() { 
  3.  
  4.     public void onSetDisabled(int status) { 
  5.         synchronized (mNotificationList) { 
  6.             mDisabledNotifications = status; 
  7.             if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) !=0) { 
  8.                 // cancel whatever's going on 
  9.                 long identity = Binder.clearCallingIdentity(); 
  10.                 try
  11.                     mSound.stop(); 
  12.                 } 
  13.                 finally
  14.                     Binder.restoreCallingIdentity(identity); 
  15.                 } 
  16.  
  17.                 identity = Binder.clearCallingIdentity(); 
  18.                 try
  19.                     mVibrator.cancel(); 
  20.                 } 
  21.                 finally
  22.                     Binder.restoreCallingIdentity(identity); 
  23.                 } 
  24.             } 
  25.         } 
  26.     } 
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);

[java] view plain copy print ?
  1. mCompletionThread = new CreationAndCompletionThread(cmd); 
  2.                 synchronized(mCompletionThread) { 
  3.                     mCompletionThread.start(); 
  4.                     mCompletionThread.wait(); 
  5.                 } 
mCompletionThread = new CreationAndCompletionThread(cmd); synchronized(mCompletionThread) { mCompletionThread.start(); mCompletionThread.wait(); }

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

[java] view plain copy print ?
  1. <span style="font-size:13px;">publicvoid run() { 
  2.             Looper.prepare(); 
  3.             mLooper = Looper.myLooper(); 
  4.             synchronized(this) { 
  5.                 AudioManager audioManager = 
  6.                     (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE); 
  7.                 try
  8.                     MediaPlayer player = new MediaPlayer(); 
  9.                     player.setAudioStreamType(mCmd.stream); 
  10.                     player.setDataSource(mCmd.context, mCmd.uri); 
  11.                     player.setLooping(mCmd.looping); 
  12.                     player.prepare(); 
  13.                     if ((mCmd.uri !=null) && (mCmd.uri.getEncodedPath() !=null
  14.                             && (mCmd.uri.getEncodedPath().length() > 0)) { 
  15.                         if (mCmd.looping) { 
  16.                             audioManager.requestAudioFocus(null, mCmd.stream, 
  17.                                     AudioManager.AUDIOFOCUS_GAIN); 
  18.                         } else
  19.                             audioManager.requestAudioFocus(null, mCmd.stream, 
  20.                                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); 
  21.                         } 
  22.                     } 
  23.                     player.setOnCompletionListener(NotificationPlayer.this); 
  24.                     player.start(); 
  25.                     if (mPlayer !=null) { 
  26.                         mPlayer.release(); 
  27.                     } 
  28.                     mPlayer = player; 
  29.                 } 
  30.                 catch (Exception e) { 
  31.                     Log.w(mTag, "error loading sound for " + mCmd.uri, e); 
  32.                 } 
  33.                 mAudioManager = audioManager; 
  34.                 this.notify(); 
  35.             } 
  36.             Looper.loop(); 
  37.         }</span> 
<span style="font-size:13px;"> public void run() { Looper.prepare(); mLooper = Looper.myLooper(); synchronized(this) { AudioManager audioManager = (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE); try { MediaPlayer player = new MediaPlayer(); player.setAudioStreamType(mCmd.stream); player.setDataSource(mCmd.context, mCmd.uri); player.setLooping(mCmd.looping); player.prepare(); if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null) && (mCmd.uri.getEncodedPath().length() > 0)) { if (mCmd.looping) { audioManager.requestAudioFocus(null, mCmd.stream, AudioManager.AUDIOFOCUS_GAIN); } else { audioManager.requestAudioFocus(null, mCmd.stream, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); } } player.setOnCompletionListener(NotificationPlayer.this); player.start(); if (mPlayer != null) { mPlayer.release(); } mPlayer = player; } catch (Exception e) { Log.w(mTag, "error loading sound for " + mCmd.uri, e); } mAudioManager = audioManager; this.notify(); } Looper.loop(); }</span>

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

你可能感兴趣的:(android,String,Stream,null,callback,notifications)