Android按键灯,指示灯总结

伪原创,网络整理,作为自己学习的记录:

原文http://bbs.9ria.com/thread-262894-1-7.html


Android中有各种灯,背光灯,按键灯,指示灯,等等;前几天修改了这部分代码,整理下思路,其实都不难;
  首先,来说说指示灯(提示灯),即未接电话,未接短信的时候,会闪灯,这个其实就是NotificationManager这个类中的notify()方法来处理的;流程简单来过一下:
  Step 1:从应用层发送的notify(),到framework层被NotificationManager.java这个类接受了,来看看这个notify()这个方法:
  java代码

  1. public void notify(int id, Notification notification)

  2.   {
  3.   notify(null, id, notification);
  4.   }
复制代码
  java代码
  1. public void notify(String tag, int id, Notification 
  2. notification)

  3.   {
  4.   int[] idOut = new int[1];
  5.   INotificationManager service = getService();
  6.   String pkg = mContext.getPackageName();
  7.   if (notification.sound != null) {
  8.   notification.sound = notification.sound.getCanonicalUri();
  9.   }
  10.   if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + 
  11. ")");
  12.   try {
  13.   service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,
  14.   UserHandle.myUserId());
  15.   if (id != idOut[0]) {
  16.   Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + 
  17. idOut[0]);
  18.   }
  19.   } catch (RemoteException e) {
  20.   }
  21.   }
复制代码
  重点关注这个enqueueNotificationWithTag()这个方法,这个方法首先会取是否传递过来了声音的Uri,如果传递了,就保存下来,给等会播放用;


Step 2:enqueueNotificationWithTag()这个方法调用到了NotificationManagerService.java这个类中去了,来看看这个方法:
  java代码

  1. public void enqueueNotificationWithTag(String pkg, String 
  2. tag, int id, Notification notification,

  3.   int[] idOut, int userId)
  4.   {
  5.   enqueueNotificationInternal(pkg, Binder.getCallingUid(), 
  6. Binder.getCallingPid(),
  7.   tag, id, notification, idOut, userId);
  8.   }
复制代码
  java代码
// Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
  1.   // uid/pid of another application)
  2.   public void enqueueNotificationInternal(String pkg, int callingUid, int 
  3. callingPid,
  4.   String tag, int id, Notification notification, int[] idOut, int userId)
  5.   {
  6.   if (DBG) {
  7.   Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " 
  8. notification=" + notification + ", notification.ledARGB : "+ 
  9. notification.ledARGB);
  10.   }
  11.   checkCallerIsSystemOrSameApp(pkg);
  12.   final boolean isSystemNotification = ("android".equals(pkg));
  13.   userId = ActivityManager.handleIncomingUser(callingPid,
  14.   callingUid, userId, true, false, "enqueueNotification", pkg);
  15.   UserHandle user = new UserHandle(userId);
  16.   // Limit the number of notifications that any given package except the 
  17. android
  18.   // package can enqueue. Prevents DOS attacks and deals with leaks.
  19.   if (!isSystemNotification) {
  20.   synchronized (mNotificationList) {
  21.   int count = 0;
  22.   int eldestIdx = 0;
  23.   long eldestTime = 0;
  24.   final int N = mNotificationList.size();
  25.   for (int i=0; i 
  26.   NotificationRecord r = mNotificationList.get(i);
  27.   if (r.pkg.equals(pkg) && r.userId == userId) {
  28.   if (count == 0) {
  29.   eldestTime = r.notification.when;
  30.   eldestIdx = i;
  31.   }
  32.   else {
  33.   if (r.notification.when < eldestTime) {
  34.   eldestTime = r.notification.when;
  35.   eldestIdx = i;
  36.   }
  37.   }
  38.   count++;
  39.   if (count >= MAX_PACKAGE_NOTIFICATIONS) {
  40.   Slog.e(TAG, "Package has already posted " + count
  41.   + " notifications. Not showing more. package=" + pkg);
  42.   //return;
  43.   // [ALPS00447419] SystemUI OOM: remove eldest entry
  44.   r = mNotificationList.get(eldestIdx);
  45.   mNotificationList.remove(eldestIdx);
  46.   cancelNotificationLocked(r, true);
  47.   break;
  48.   }
  49.   }
  50.   }
  51.   }
  52.   }
  53.   // This conditional is a dirty hack to limit the logging done on
  54.   // behalf of the download manager without affecting other apps.
  55.   if (!pkg.equals("com.android.providers.downloads")
  56.   || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
  57.   EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, 
  58. userId,
  59.   notification.toString());
  60.   }
  61.   if (pkg == null || notification == null) {
  62.   throw new IllegalArgumentException("null not allowed: pkg=" + pkg
  63.   + " id=" + id + " notification=" + notification);
  64.   }
  65.   if (notification.icon != 0) {
  66.   if (notification.contentView == null) {
  67.   throw new IllegalArgumentException("contentView required: pkg=" + pkg
  68.   + " id=" + id + " notification=" + notification);
  69.   }
  70.   }
  71.   // === Scoring ===
  72.   // 0. Sanitize inputs
  73.   notification.priority = clamp(notification.priority, 
  74. Notification.PRIORITY_MIN, Notification.PRIORITY_MAX);
  75.   // Migrate notification flags to scores
  76.   if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
  77.   if (notification.priority < Notification.PRIORITY_MAX) 
  78. notification.priority = Notification.PRIORITY_MAX;
  79.   } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & 
  80. Notification.FLAG_ONGOING_EVENT)) {
  81.   if (notification.priority < Notification.PRIORITY_HIGH) 
  82. notification.priority = Notification.PRIORITY_HIGH;
  83.   }
  84.   // 1. initial score: buckets of 10, around the app
  85.   int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; 
  86. //[-20..20]
  87.   // 2. Consult external heuristics (TBD)
  88.   // 3. Apply local rules
  89.   // blocked apps
  90.   if (ENABLE_BLOCKED_NOTIFICATIONS && !isSystemNotification 
  91. && !areNotificationsEnabledForPackageInt(pkg)) {
  92.   score = JUNK_SCORE;
  93.   Slog.e(TAG, "Suppressing notification from package " + pkg + " by user 
  94. request.");
  95.   }
  96.   if (DBG) {
  97.   Slog.v(TAG, "Assigned score=" + score + " to " + notification);
  98.   }
  99.   if (score < SCORE_DISPLAY_THRESHOLD) {
  100.   // Notification will be blocked because the score is too low.
  101.   return;
  102.   }
  103.   // Should this notification make noise, vibe, or use the LED?
  104.   final boolean canInterrupt = (score >= 
  105. SCORE_INTERRUPTION_THRESHOLD);
  106.   synchronized (mNotificationList) {
  107.   NotificationRecord r = new NotificationRecord(pkg, tag, id,
  108.   callingUid, callingPid, userId,
  109.   score,
  110.   notification);
  111.   NotificationRecord old = null;
  112.   int index = indexOfNotificationLocked(pkg, tag, id, userId);
  113.   if (index < 0) {
  114.   mNotificationList.add(r);
  115.   } else {
  116.   old = mNotificationList.remove(index);
  117.   mNotificationList.add(index, r);
  118.   // Make sure we don't lose the foreground service state.
  119.   if (old != null) {
  120.   notification.flags |=
  121.   old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
  122.   }
  123.   }
  124.   // Ensure if this is a foreground service that the proper additional
  125.   // flags are set.
  126.   if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) 
  127. {
  128.   notification.flags |= Notification.FLAG_ONGOING_EVENT
  129.   | Notification.FLAG_NO_CLEAR;
  130.   }
  131.   final int currentUser;
  132.   final long token = Binder.clearCallingIdentity();
  133.   try {
  134.   currentUser = ActivityManager.getCurrentUser();
  135.   } finally {
  136.   Binder.restoreCallingIdentity(token);
  137.   }
  138.   if (notification.icon != 0) {
  139.   final StatusBarNotification n = new StatusBarNotification(
  140.   pkg, id, tag, r.uid, r.initialPid, score, notification, user);
  141.   if (old != null && old.statusBarKey != null) {
  142.   r.statusBarKey = old.statusBarKey;
  143.   long identity = Binder.clearCallingIdentity();
  144.   try {
  145.   mStatusBar.updateNotification(r.statusBarKey, n);
  146.   }
  147.   finally {
  148.   Binder.restoreCallingIdentity(identity);
  149.   }
  150.   } else {
  151.   long identity = Binder.clearCallingIdentity();
  152.   try {
  153.   r.statusBarKey = mStatusBar.addNotification(n);
  154.   if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
  155.   && canInterrupt) {
  156.   mAttentionLight.pulse();
  157.   }
  158.   }
  159.   finally {
  160.   Binder.restoreCallingIdentity(identity);
  161.   }
  162.   }
  163.   // Send accessibility events only for the current user.
  164.   if (currentUser == userId) {
  165.   sendAccessibilityEvent(notification, pkg);
  166.   }
  167.   } else {
  168.   Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
  169.   if (old != null && old.statusBarKey != null) {
  170.   long identity = Binder.clearCallingIdentity();
  171.   try {
  172.   mStatusBar.removeNotification(old.statusBarKey);
  173.   }
  174.   finally {
  175.   Binder.restoreCallingIdentity(identity);
  176.   }
  177.   }
  178.   }
  179.   // If we're not supposed to beep, vibrate, etc. then don't.
  180.   // ensure mms can send notification when a phone is calling
  181.   if ((((mDisabledNotifications & 
  182. StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) || 
  183. pkg.equals("com.android.mms"))
  184.   && (!(old != null
  185.   && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 
  186. 0 ))
  187.   && (r.userId == UserHandle.USER_ALL ||
  188.   (r.userId == userId && r.userId == currentUser))
  189.   && canInterrupt
  190.   && mSystemReady) {
  191.   ///M:
  192.   if (DBG) {
  193.   Log.d(TAG,"pakage="+pkg+",In NotificationMangerService, this notification 
  194. soud, leds and vibrate enable");
  195.   }
  196.   final AudioManager audioManager = (AudioManager) mContext
  197.   .getSystemService(Context.AUDIO_SERVICE);
  198.   // sound
  199.   final boolean useDefaultSound =
  200.   (notification.defaults & Notification.DEFAULT_SOUND) != 0;
  201.   ///M: log sound information
  202.   if (DBG) {
  203.   Log.d(TAG,"useDefaultSound="+useDefaultSound);
  204.   Log.d(TAG,"notification.sound="+notification.sound);
  205.   }
  206.   Uri soundUri = null;
  207.   boolean hasValidSound = false;
  208.   if (useDefaultSound) {
  209.   soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
  210.   // check to see if the default notification sound is silent
  211.   ContentResolver resolver = mContext.getContentResolver();
  212.   hasValidSound = Settings.System.getString(resolver,
  213.   Settings.System.NOTIFICATION_SOUND) != null;
  214.   } else if (notification.sound != null) {
  215.   soundUri = notification.sound;
  216.   hasValidSound = (soundUri != null);
  217.   }
  218.   if (hasValidSound) {
  219.   boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 
  220. 0;
  221.   int audioStreamType;
  222.   if (notification.audioStreamType >= 0) {
  223.   audioStreamType = notification.audioStreamType;
  224.   } else {
  225.   audioStreamType = DEFAULT_STREAM_TYPE;
  226.   }
  227.   mSoundNotification = r;
  228.   ///M: log sound information
  229.   if (DBG) {
  230.   Log.d(TAG,"looping="+looping);
  231.   Log.d(TAG,"audioStreamType="+audioStreamType);
  232.   Log.d(TAG,"StreamVolume="+audioManager.getStreamVolume(audioStreamType));
  233.   }
  234.   // do not play notifications if stream volume is 0
  235.   // (typically because ringer mode is silent) or if speech recognition is 
  236. active.
  237.   if ((audioManager.getStreamVolume(audioStreamType) != 0)
  238.   && !audioManager.isSpeechRecognitionActive()) {
  239.   final long identity = Binder.clearCallingIdentity();
  240.   try {
  241.   final IRingtonePlayer player = mAudioService.getRingtonePlayer();
  242.   if (player != null) {
  243.   ///M: [ALPS00461691]No notification sound when it detects wifi networks
  244.   if (user.getIdentifier() == UserHandle.USER_ALL) {
  245.   user = UserHandle.OWNER;
  246.   }
  247.   player.playAsync(soundUri, user, looping, audioStreamType);
  248.   }
  249.   } catch (RemoteException e) {
  250.   } finally {
  251.   Binder.restoreCallingIdentity(identity);
  252.   }
  253.   }
  254.   }
  255.   // vibrate
  256.   ///M: for device manager
  257.   if (DBG) {
  258.   Log.d(TAG,"mDmLock="+mDmLock);
  259.   }
  260.   if (mDmLock == false){
  261.   // Does the notification want to specify its own vibration?
  262.   final boolean hasCustomVibrate = notification.vibrate != null;
  263.   // new in 4.2: if there was supposed to be a sound and we're in vibrate 
  264. mode,
  265.   // and no other vibration is specified, we fall back to vibration
  266.   final boolean convertSoundToVibration =
  267.   !hasCustomVibrate
  268.   && hasValidSound
  269.   && (audioManager.getRingerMode() == 
  270. AudioManager.RINGER_MODE_VIBRATE);
  271.   // The DEFAULT_VIBRATE flag trumps any custom vibration AND the 
  272. fallback.
  273.   final boolean useDefaultVibrate =
  274.   (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
  275.   if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
  276.   && !(audioManager.getRingerMode() == 
  277. AudioManager.RINGER_MODE_SILENT)) {
  278.   mVibrateNotification = r;
  279.   if (DBG) {
  280.   Log.w(TAG, "set vibrate!");
  281.   }
  282.   if (useDefaultVibrate || convertSoundToVibration) {
  283.   // Escalate privileges so we can use the vibrator even if the notifying 
  284. app
  285.   // does not have the VIBRATE permission.
  286.   long identity = Binder.clearCallingIdentity();
  287.   try {
  288.   mVibrator.vibrate(useDefaultVibrate ? mDefaultVibrationPattern
  289.   : mFallbackVibrationPattern,
  290.   ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
  291.   } finally {
  292.   Binder.restoreCallingIdentity(identity);
  293.   }
  294.   } else if (notification.vibrate.length > 1) {
  295.   // If you want your own vibration pattern, you need the VIBRATE 
  296. permission
  297.   mVibrator.vibrate(notification.vibrate,
  298.   ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
  299.   }
  300.   }
  301.   } // mDmLock == false
  302.   }
  303.   // this option doesn't shut off the lights
  304.   // light
  305.   // the most recent thing gets the light
  306.   mLights.remove(old);
  307.   if (mLedNotification == old) {
  308.   mLedNotification = null;
  309.   }
  310.   //Slog.i(TAG, "notification.lights="
  311.   // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) 
  312. != 0));
  313.   if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
  314.   && canInterrupt) {
  315.   mLights.add(r);
  316.   updateLightsLocked();
  317.   } else {
  318.   if (old != null
  319.   && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 
  320. 0)) {
  321.   updateLightsLocked();
  322.   }
  323.   }
  324.   }
  325.   idOut[0] = id;
  326.   }
复制代码

  上面这个方法做的操作有点多,代码有300多行。其中有设计播放声音的地方:
  java代码
  1. "font-size:18px;"> player.playAsync(soundUri, user, 
  2. looping, audioStreamType);
复制代码
  有是否播放震动的地方:
  java代码
  1. mVibrator.vibrate(notification.vibrate,

  2.   ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
复制代码
  最后闪灯的地方在这个逻辑中:
  java代码
  1. if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) 
  2. != 0

  3.   && canInterrupt) {
  4.   mLights.add(r);
  5.   updateLightsLocked();
  6.   }
复制代码
  重点看updateLightsLocked()这个方法,这个方法里面有逻辑的操作;


Step 3:updateLightsLocked()这个方法的代码如下:
  java代码

  1. // lock on mNotificationList

  2.   private void updateLightsLocked()
  3.   {
  4.   // handle notification lights
  5.   if (mLedNotification == null) {
  6.   // get next notification, if any
  7.   int n = mLights.size();
  8.   if (n > 0) {
  9.   mLedNotification = mLights.get(n-1);
  10.   }
  11.   }
  12.   // Don't flash while we are in a call or screen is on
  13.   ///M: we need flash when screen is on
  14.   //mScreenOn add by lvmingfei for don't flash while screen is on in 
  15. 2013-09-20
  16.   if (mLedNotification == null || mInCall || mCallRinging)) {
  17.   mNotificationLight.turnOff();
  18.   } else {
  19.   int ledARGB = mLedNotification.notification.ledARGB;
  20.   int ledOnMS = mLedNotification.notification.ledOnMS;
  21.   int ledOffMS = mLedNotification.notification.ledOffMS;
  22.   if ((mLedNotification.notification.defaults & 
  23. Notification.DEFAULT_LIGHTS) != 0) {
  24.   ledARGB = mDefaultNotificationColor;
  25.   ledOnMS = mDefaultNotificationLedOn;
  26.   ledOffMS = mDefaultNotificationLedOff;
  27.   }
  28.   if (mNotificationPulseEnabled) {
  29.   // pulse repeatedly
  30.   ///M: log lights information
  31.   Log.d(TAG, "notification setFlashing ledOnMS = "+ledOnMS + " ledOffMS = "+ 
  32. ledOffMS + ", ledARGB :" + ledARGB);
  33.   mNotificationLight.setFlashing(ledARGB, 
  34. LightsService.LIGHT_FLASH_TIMED,
  35.   ledOnMS, ledOffMS);
  36.   ///M:
  37.   } else {
  38.   // pulse only once
  39.   mNotificationLight.pulse(ledARGB, ledOnMS);
  40.   }
  41.   }
  42.   }
复制代码
  这段代码中有如下操作,判断一些变量的状态,以决定时候关闭指示灯,还是闪光,还是只闪一次的操作;
  电话的状态是否是offhook,或来电的状态,会操作:java代码
  1. "font-size:18px;">mNotificationLight.turnOff();
复制代码
  下面这个条件也很重要:java代码
  1. "font-size:18px;">if 
  2. ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) !=0) 
  3. {
复制代码
  未解来电,或短信的时候这个值会设置为java代码
  1. "font-size:18px;">notification.defaults |= 
  2. Notification.DEFAULT_LIGHTS;
复制代码
  这个值是4,那么上面的这个判断就为true了,所以灯的颜色就不是由java代码
  1. "font-size:18px;">mLedNotification.notification.ledARGB;
复制代码
  这个值决定的了,而是由mDefaultNotificationColor这个值决定的,这个值的定义在
  java代码
  1. "font-size:18px;">mDefaultNotificationColor = 
  2. resources.getColor(

  3.   com.android.internal.R.color.config_defaultNotificationColor);
复制代码
  这个值的定义在framework/base/core/res/res/values/config.xml中定义的,
  java代码
  1. "font-size:18px;">#ff0000ff

  2.   这个表示是蓝灯;具体想改成其他值;
  3.   #ff0000ff 表示蓝灯
  4.   #ff00ff00 表示绿灯
  5.   #ffff0000 表示红灯
复制代码
  后面的逻辑就调用到LightsService.java中去了,这个类是管理灯的类,(背光灯,按键灯,指示灯等等);


拓展:
  如果想实现屏幕亮的时候,指示灯灭,屏幕灭的时候指示灯亮;可以监听ACTION_SCREEN_ON/ACTION_SCREEN_OFF的广播,搞一个全局的变量控制下;再调用updateNotificationPulse()这个方法,把变量的判断加载updateNotificationPulse()这个方法的灯亮灭判断的地方即可;
  其次,我们来看看返回键的灯,即按键灯;
  Step 1 :先来看看PowerManagerService.java这个类。按键灯的定义
  java代码

  1. "font-size:18px;"> private LightsService.Light 
  2. mButtonLight;
复制代码
  初始化方法:java代码
  1. "font-size:18px;">mButtonLight = 
  2. mLightsService.getLight(LightsService.LIGHT_ID_BUTTONS);
复制代码
  java代码
  1. "font-size:18px;"> if ( (newScreenState == 
  2. DisplayPowerRequest.SCREEN_STATE_BRIGHT) && (mWakefulness == 
  3. WAKEFULNESS_AWAKE) && !mIPOShutdown && !mShutdownFlag) {

  4.   if ( ( (mWakeLockSummary & WAKE_LOCK_BUTTON_BRIGHT) != 0 ) ||
  5.   ( (mUserActivitySummary & USER_ACTIVITY_BUTTON_BRIGHT) != 0) ) {
  6.   mButtonLight.setBrightness(mScreenBrightness);
  7.   Slog.i(TAG, "setBrightness mButtonLight, mScreenBrightness=" + 
  8. mScreenBrightness);
  9.   } else {
  10.   mButtonLight.turnOff();
  11.   Slog.i(TAG, "setBrightness mButtonLight 0 ===.");
  12.   }
  13.   } else {
  14.   mButtonLight.turnOff();
  15.   Slog.i(TAG, "setBrightness mButtonLight else 0.");
  16.   }
复制代码
  灯的点亮的方法setBrightness()
  灯关闭的方法turnOff()
  要想修改按键灯随p-sensor的灯的亮灭同步,可以参考Android4.2中Phone的P-sensor的应用的分析。
  然后再加上上述控制灯亮灭的方法就可实现同步;
  总结:灯的亮灭最后都会调用到LightsService.java这个类的,最后通过c代码调用底层的接口实现灯的颜色和闪烁的变化的;

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值