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

更多相关文章

  1. XML解析和生成之--pull(android内置)
  2. Android(安卓)Launcher研究--手把手教你在Windows环境下下载Andr
  3. 【Android】SQLiteOpenHelper类(sql语句实现增删改查),封装SQLite
  4. Android原生与H5交互的实现
  5. 15个开发者最亲睐的Andr​​oid代码编辑器
  6. [Android] Android中动态添加Panel的框架代码
  7. Android(安卓)音视频深入 十三 OpenSL ES 制作音乐播放器,能暂停
  8. Android(安卓)Studio代码混淆小结
  9. Android调试方法大全

随机推荐

  1. android 媒体数据库刷新
  2. android - 为响应度而设计 - 开发文档翻
  3. Android(1.5及以上版本) 开机图片/文字/
  4. Android、JUnit深入浅出(一)——JUnit初步
  5. Android(安卓)7 新特性浅析
  6. Android(安卓)Handler
  7. 解决ScrollView中嵌套RecyclerVIew产生滑
  8. android静默安装的实现
  9. android 多任务多线程断点下载
  10. Opera Mobile 在 Android(安卓)x86 上运