Android Light开发(二) 通知灯调用过程

  Android中有各种灯,背光灯,按键灯,指示灯,等等;前几天修改了这部分代码,整理下思路,其实都不难;

       首先,来说说指示灯(提示灯),即未接电话,未接短信的时候,会闪灯,这个其实就是NotificationManager这个类中的notify()方法来处理的;流程简单来过一下:


       Step 1:从应用层发送的notify(),到framework层被NotificationManager.java这个类接受了,来看看这个notify()这个方法:

[java]  view plain  copy
 print ?
  1. public void notify(int id, Notification notification)  
  2.     {  
  3.         notify(null, id, notification);  
  4.     }  

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


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

[java]  view plain  copy
 print ?
  1. public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,  
  2.             int[] idOut, int userId)  
  3.     {  
  4.         enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),  
  5.                 tag, id, notification, idOut, userId);  
  6.     }  

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

[java]  view plain  copy
 print ?
  1. <span style="font-size:18px;"> player.playAsync(soundUri, user, looping, audioStreamType);</span>  
有是否播放震动的地方:

[java]  view plain  copy
 print ?
  1. mVibrator.vibrate(notification.vibrate,  
  2.                            ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);  
最后闪灯的地方在这个逻辑中:

[java]  view plain  copy
 print ?
  1. if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0  
  2.                    && canInterrupt) {  
  3.                mLights.add(r);  
  4.                updateLightsLocked();  
  5.            }   
重点看updateLightsLocked()这个方法,这个方法里面有逻辑的操作;


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

[java]  view plain  copy
 print ?
  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.   
  13.         // Don't flash while we are in a call or screen is on  
  14.         ///M: we need flash when screen is on  
  15.         //mScreenOn add by lvmingfei for don't flash while screen is on in 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 & Notification.DEFAULT_LIGHTS) != 0) {  
  23.                 ledARGB = mDefaultNotificationColor;  
  24.                 ledOnMS = mDefaultNotificationLedOn;  
  25.                 ledOffMS = mDefaultNotificationLedOff;  
  26.             }  
  27.             if (mNotificationPulseEnabled) {  
  28.                 // pulse repeatedly  
  29.                 ///M: log lights information   
  30.                 Log.d(TAG, "notification setFlashing ledOnMS = "+ledOnMS + " ledOffMS = "+ ledOffMS + ", ledARGB :" + ledARGB);  
  31.                 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,  
  32.                         ledOnMS, ledOffMS);  
  33.                 ///M:   
  34.             } else {  
  35.                 // pulse only once  
  36.                 mNotificationLight.pulse(ledARGB, ledOnMS);  
  37.             }  
  38.         }  
  39.     }  
这段代码中有如下操作,判断一些变量的状态,以决定时候关闭指示灯,还是闪光,还是只闪一次的操作;
电话的状态是否是offhook,或来电的状态,会操作:
[java]  view plain  copy
 print ?
  1. <span style="font-size:18px;">mNotificationLight.turnOff();</span>  
下面这个条件也很重要:
[java]  view plain  copy
 print ?
  1. <span style="font-size:18px;">if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {</span>  
未解来电,或短信的时候这个值会设置为
[java]  view plain  copy
 print ?
  1. <span style="font-size:18px;">notification.defaults |= Notification.DEFAULT_LIGHTS;</span>  
这个值是4,那么上面的这个判断就为true了,所以灯的颜色就不是由
[java]  view plain  copy
 print ?
  1. <span style="font-size:18px;">mLedNotification.notification.ledARGB;</span>  
这个值决定的了,而是由mDefaultNotificationColor这个值决定的,这个值的定义在

[java]  view plain  copy
 print ?
  1. <span style="font-size:18px;">mDefaultNotificationColor = resources.getColor(  
  2.                 com.android.internal.R.color.config_defaultNotificationColor);</span>  
这个值的定义在framework/base/core/res/res/values/config.xml中定义的,

[java]  view plain  copy
 print ?
  1. <span style="font-size:18px;"><color name="config_defaultNotificationColor">#ff0000ff</color></span>  
这个表示是蓝灯;具体想改成其他值;

  1. #ff0000ff 表示蓝灯
  2. #ff00ff00 表示绿灯
  3. #ffff0000 表示红灯
后面的逻辑就调用到LightsService.java中去了,这个类是管理灯的类,(背光灯,按键灯,指示灯等等);


     拓展:

             如果想实现屏幕亮的时候,指示灯灭,屏幕灭的时候指示灯亮;可以监听ACTION_SCREEN_ON/ACTION_SCREEN_OFF的广播,搞一个全局的变量控制下;再调用updateNotificationPulse()这个方法,把变量的判断加载updateNotificationPulse()这个方法的灯亮灭判断的地方即可;


        其次,我们来看看返回键的灯,即按键灯;

        Step 1 :先来看看PowerManagerService.java这个类。按键灯的定义

[java]  view plain  copy
 print ?
  1. <span style="font-size:18px;"private LightsService.Light mButtonLight;</span>  
        初始化方法:
[java]  view plain  copy
 print ?
  1. <span style="font-size:18px;">mButtonLight = mLightsService.getLight(LightsService.LIGHT_ID_BUTTONS);</span>  
[java]  view plain  copy
 print ?
  1. <span style="font-size:18px;"if ( (newScreenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT) && (mWakefulness == WAKEFULNESS_AWAKE) && !mIPOShutdown && !mShutdownFlag) {  
  2.                 if ( ( (mWakeLockSummary & WAKE_LOCK_BUTTON_BRIGHT) != 0 ) ||  
  3.                         ( (mUserActivitySummary & USER_ACTIVITY_BUTTON_BRIGHT) != 0) ) {  
  4.                     mButtonLight.setBrightness(mScreenBrightness);  
  5.                     Slog.i(TAG, "setBrightness mButtonLight, mScreenBrightness=" + mScreenBrightness);  
  6.                 } else {  
  7.                     mButtonLight.turnOff();  
  8.                     Slog.i(TAG, "setBrightness mButtonLight 0 ===.");  
  9.                 }  
  10.             } else {  
  11.                 mButtonLight.turnOff();  
  12.                 Slog.i(TAG, "setBrightness mButtonLight else 0.");  
  13.             }</span>  
灯的点亮的方法setBrightness()

灯关闭的方法turnOff()

要想修改按键灯随p-sensor的灯的亮灭同步,可以参考Android4.2中Phone的P-sensor的应用的分析。

然后再加上上述控制灯亮灭的方法就可实现同步;


     总结:灯的亮灭最后都会调用到LightsService.java这个类的,最后通过c代码调用底层的接口实现灯的颜色和闪烁的变化的;

最终通过JNI调用vendor\mediatek\proprietary\hardware\liblights\lights.c

另外三色灯底层是写死的只能三色,要是自定义请修改lights.c


/* Copyright Statement:
 *
 * This software/firmware and related documentation ("MediaTek Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to MediaTek Inc. and/or its licensors. Without
 * the prior written permission of MediaTek inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of MediaTek Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 * 
 * MediaTek Inc. (C) 2010. All rights reserved.
 * 
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
 * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK
 * SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE
 * MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
 *
 * The following software/firmware and/or related documentation ("MediaTek
 * Software") have been modified by MediaTek Inc. All revisions are subject to
 * any receiver's applicable license agreements with MediaTek Inc.
 */

/*
 * Copyright (C) 2008 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.
 */


#define LOG_TAG "lights"


#include <cutils/log.h>

#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <time.h>

#include <sys/ioctl.h>
#include <sys/types.h>

#include <hardware/lights.h>

#define LIGHTS_DBG_ON
/******************************************************************************/

static pthread_once_t g_init = PTHREAD_ONCE_INIT;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
static int g_haveTrackballLight = 0;
static struct light_state_t g_notification;
static struct light_state_t g_battery;
static int g_backlight = 255;
static int g_trackball = -1;
static int g_buttons = 0;
static int g_attention = 0;

/* TRACKBALL BACKLIGHT */
char const*const TRACKBALL_FILE
        = "/sys/class/leds/jogball-backlight/brightness";

/* RED LED */
char const*const RED_LED_FILE
        = "/sys/class/leds/red/brightness";

char const*const RED_TRIGGER_FILE
        = "/sys/class/leds/red/trigger";

char const*const RED_DELAY_ON_FILE
        = "/sys/class/leds/red/delay_on";

char const*const RED_DELAY_OFF_FILE
        = "/sys/class/leds/red/delay_off";

/* GREEN LED */
char const*const GREEN_LED_FILE
        = "/sys/class/leds/green/brightness";

char const*const GREEN_TRIGGER_FILE
        = "/sys/class/leds/green/trigger";

char const*const GREEN_DELAY_ON_FILE
        = "/sys/class/leds/green/delay_on";

char const*const GREEN_DELAY_OFF_FILE
        = "/sys/class/leds/green/delay_off";

/* BLUE LED */
char const*const BLUE_LED_FILE
        = "/sys/class/leds/blue/brightness";

char const*const BLUE_TRIGGER_FILE
        = "/sys/class/leds/blue/trigger";

char const*const BLUE_DELAY_ON_FILE
        = "/sys/class/leds/blue/delay_on";

char const*const BLUE_DELAY_OFF_FILE
        = "/sys/class/leds/blue/delay_off";

/* LCD BACKLIGHT */
char const*const LCD_FILE
        = "/sys/class/leds/lcd-backlight/brightness";

/* KEYBOARD BACKLIGHT */
char const*const KEYBOARD_FILE
        = "/sys/class/leds/keyboard-backlight/brightness";

/* BUTTON BACKLIGHT */
char const*const BUTTON_FILE
        = "/sys/class/leds/button-backlight/brightness";

//ALPS0804285 add for delay
int led_wait_delay(int ms) 
{
	struct timespec req = {.tv_sec = 0, .tv_nsec = ms*1000000};
	struct timespec rem;
	int ret = nanosleep(&req, &rem);

	while(ret)
	{
		if(errno == EINTR)
		{
			req.tv_sec  = rem.tv_sec;
			req.tv_nsec = rem.tv_nsec;
			ret = nanosleep(&req, &rem);
		}
		else
		{
			perror("nanosleep");
			return errno;
		}
	}
	return 0;
}

/**
 * device methods
 */

void init_globals(void)
{
    // init the mutex
    pthread_mutex_init(&g_lock, NULL);

    // figure out if we have the trackball LED or not
    g_haveTrackballLight = (access(TRACKBALL_FILE, W_OK) == 0) ? 1 : 0;

}

static int
write_int(char const* path, int value)
{
    int fd;

#ifdef LIGHTS_INFO_ON
	ALOGD("write %d to %s", value, path);
#endif

    fd = open(path, O_RDWR);
	ALOGD("write_int open fd=%d\n", fd);
    if (fd >= 0) {
        char buffer[20];
        int bytes = sprintf(buffer, "%d\n", value);
        int amt = write(fd, buffer, bytes);
        close(fd);
        return amt == -1 ? -errno : 0;
    } else {
        return -errno;
    }
}

static int
write_str(char const* path, char *str)
{
    int fd;

#ifdef LIGHTS_INFO_ON
	ALOGD("write %s to %s", str, path);
#endif

    fd = open(path, O_WRONLY);
    if (fd >= 0) {
        char buffer[20];
        int bytes = sprintf(buffer, "%s", str);
        int amt = write(fd, buffer, bytes);
        close(fd);
        return amt == -1 ? -errno : 0;
    } else {
        return -errno;
    }
}

static int
is_lit(struct light_state_t const* state)
{
    return state->color & 0x00ffffff;
}

static int
blink_red(int level, int onMS, int offMS)
{
	static int preStatus = 0; // 0: off, 1: blink, 2: no blink
	int nowStatus;
	int i = 0;

	if (level == 0)
		nowStatus = 0;
	else if (onMS && offMS)
		nowStatus = 1;
	else
		nowStatus = 2;

	if (preStatus == nowStatus)
		return -1;

#ifdef LIGHTS_DBG_ON
	ALOGD("blink_red, level=%d, onMS=%d, offMS=%d\n", level, onMS, offMS);
#endif
	if (nowStatus == 0) {
        	write_int(RED_LED_FILE, 0);
	}
	else if (nowStatus == 1) {
//        	write_int(RED_LED_FILE, level); // default full brightness
		write_str(RED_TRIGGER_FILE, "timer");
		while (((access(RED_DELAY_OFF_FILE, F_OK) == -1) || (access(RED_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) {
			ALOGD("RED_DELAY_OFF_FILE doesn't exist or cannot write!!\n");
			led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs
			i++;
		}
		write_int(RED_DELAY_OFF_FILE, offMS);
		write_int(RED_DELAY_ON_FILE, onMS);
	}
	else {
		write_str(RED_TRIGGER_FILE, "none");
        	write_int(RED_LED_FILE, 255); // default full brightness
	}

	preStatus = nowStatus;

	return 0;
}

static int
blink_green(int level, int onMS, int offMS)
{
	static int preStatus = 0; // 0: off, 1: blink, 2: no blink
	int nowStatus;
	int i = 0;

	if (level == 0)
		nowStatus = 0;
	else if (onMS && offMS)
		nowStatus = 1;
	else
		nowStatus = 2;

	if (preStatus == nowStatus)
		return -1;

#ifdef LIGHTS_DBG_ON
	ALOGD("blink_green, level=%d, onMS=%d, offMS=%d\n", level, onMS, offMS);
#endif
	if (nowStatus == 0) {
        	write_int(GREEN_LED_FILE, 0);
	}
	else if (nowStatus == 1) {
//        	write_int(GREEN_LED_FILE, level); // default full brightness
		write_str(GREEN_TRIGGER_FILE, "timer");
		while (((access(GREEN_DELAY_OFF_FILE, F_OK) == -1) || (access(GREEN_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) {
			ALOGD("GREEN_DELAY_OFF_FILE doesn't exist or cannot write!!\n");
			led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs
			i++;
		}
		write_int(GREEN_DELAY_OFF_FILE, offMS);
		write_int(GREEN_DELAY_ON_FILE, onMS);
	}
	else {
		write_str(GREEN_TRIGGER_FILE, "none");
        	write_int(GREEN_LED_FILE, 255); // default full brightness
	}

	preStatus = nowStatus;

	return 0;
}

static int
blink_blue(int level, int onMS, int offMS)
{
	static int preStatus = 0; // 0: off, 1: blink, 2: no blink
	int nowStatus;
	int i = 0;

	if (level == 0)
		nowStatus = 0;
	else if (onMS && offMS)
		nowStatus = 1;
	else
		nowStatus = 2;

	if (preStatus == nowStatus)
		return -1;

#ifdef LIGHTS_DBG_ON
	ALOGD("blink_blue, level=%d, onMS=%d, offMS=%d\n", level, onMS, offMS);
#endif
	if (nowStatus == 0) {
        	write_int(BLUE_LED_FILE, 0);
	}
	else if (nowStatus == 1) {
//        	write_int(BLUE_LED_FILE, level); // default full brightness
		write_str(BLUE_TRIGGER_FILE, "timer");
		while (((access(BLUE_DELAY_OFF_FILE, F_OK) == -1) || (access(BLUE_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) {
			ALOGD("BLUE_DELAY_OFF_FILE doesn't exist or cannot write!!\n");
			led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs
			i++;
		}
		write_int(BLUE_DELAY_OFF_FILE, offMS);
		write_int(BLUE_DELAY_ON_FILE, onMS);
	}
	else {
		write_str(BLUE_TRIGGER_FILE, "none");
        	write_int(BLUE_LED_FILE, 255); // default full brightness
	}

	preStatus = nowStatus;

	return 0;
}

static int
handle_trackball_light_locked(struct light_device_t* dev)
{
    int mode = g_attention;

    if (mode == 7 && g_backlight) {
        mode = 0;
    }
    ALOGV("%s g_backlight = %d, mode = %d, g_attention = %d\n",
        __func__, g_backlight, mode, g_attention);

    // If the value isn't changing, don't set it, because this
    // can reset the timer on the breathing mode, which looks bad.
    if (g_trackball == mode) {
        return 0;
    }

    return write_int(TRACKBALL_FILE, mode);
}

static int
rgb_to_brightness(struct light_state_t const* state)
{
    int color = state->color & 0x00ffffff;
    return ((77*((color>>16)&0x00ff))
            + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
}

static int
set_light_backlight(struct light_device_t* dev,
        struct light_state_t const* state)
{
    int err = 0;
    int brightness = rgb_to_brightness(state);
    pthread_mutex_lock(&g_lock);
    g_backlight = brightness;
    err = write_int(LCD_FILE, brightness);
    if (g_haveTrackballLight) {
        handle_trackball_light_locked(dev);
    }
    pthread_mutex_unlock(&g_lock);
    return err;
}

static int
set_light_keyboard(struct light_device_t* dev,
        struct light_state_t const* state)
{
    int err = 0;
    int on = is_lit(state);
    pthread_mutex_lock(&g_lock);
    err = write_int(KEYBOARD_FILE, on?255:0);
    pthread_mutex_unlock(&g_lock);
    return err;
}

static int
set_light_buttons(struct light_device_t* dev,
        struct light_state_t const* state)
{
    int err = 0;
    int on = is_lit(state);
    pthread_mutex_lock(&g_lock);
    g_buttons = on;
    err = write_int(BUTTON_FILE, on?255:0);
    pthread_mutex_unlock(&g_lock);
    return err;
}

static int
set_speaker_light_locked(struct light_device_t* dev,
        struct light_state_t const* state)
{
    int len;
    int alpha, red, green, blue;
    int onMS, offMS;
    unsigned int colorRGB;

    switch (state->flashMode) {
        case LIGHT_FLASH_TIMED:
            onMS = state->flashOnMS;
            offMS = state->flashOffMS;
            break;
        case LIGHT_FLASH_NONE:
        default:
            onMS = 0;
            offMS = 0;
            break;
    }

    colorRGB = state->color;

#ifdef LIGHTS_DBG_ON
    ALOGD("set_led_state colorRGB=%08X, onMS=%d, offMS=%d\n",
            colorRGB, onMS, offMS);
#endif

    alpha = (colorRGB >> 24) & 0xFF;
    if (alpha) {
    	red = (colorRGB >> 16) & 0xFF;
    	green = (colorRGB >> 8) & 0xFF;
    	blue = colorRGB & 0xFF;
    } else { // alpha = 0 means turn the LED off
    	red = green = blue = 0;
    }

    if (red) {
        blink_green(0, 0, 0);
        blink_blue(0, 0, 0);
        blink_red(red, onMS, offMS);
    }
    else if (green) {
        blink_red(0, 0, 0);
        blink_blue(0, 0, 0);
        blink_green(green, onMS, offMS);
    }
    else if (blue) {
        blink_red(0, 0, 0);
        blink_green(0, 0, 0);
        blink_blue(blue, onMS, offMS);
    }
    else {
        blink_red(0, 0, 0);
        blink_green(0, 0, 0);
        blink_blue(0, 0, 0);
    }

    return 0;
}

static void
handle_speaker_battery_locked(struct light_device_t* dev)
{
    if (is_lit(&g_battery)) {
        set_speaker_light_locked(dev, &g_battery);
    } else {
    	set_speaker_light_locked(dev, &g_battery); /*Turkey workaround: notification and Low battery case, IPO bootup, NLED cannot blink*/
        set_speaker_light_locked(dev, &g_notification);
    }
}

static int
set_light_battery(struct light_device_t* dev,
        struct light_state_t const* state)
{
    pthread_mutex_lock(&g_lock);
    g_battery = *state;
    if (g_haveTrackballLight) {
        set_speaker_light_locked(dev, state);
    }
    handle_speaker_battery_locked(dev);
    pthread_mutex_unlock(&g_lock);
    return 0;
}

static int
set_light_notifications(struct light_device_t* dev,
        struct light_state_t const* state)
{
    pthread_mutex_lock(&g_lock);
    g_notification = *state;
    ALOGV("set_light_notifications g_trackball=%d color=0x%08x",
            g_trackball, state->color);
    if (g_haveTrackballLight) {
        handle_trackball_light_locked(dev);
    }
    handle_speaker_battery_locked(dev);
    pthread_mutex_unlock(&g_lock);
    return 0;
}

static int
set_light_attention(struct light_device_t* dev,
        struct light_state_t const* state)
{
    pthread_mutex_lock(&g_lock);
    ALOGV("set_light_attention g_trackball=%d color=0x%08x",
            g_trackball, state->color);
    if (state->flashMode == LIGHT_FLASH_HARDWARE) {
        g_attention = state->flashOnMS;
    } else if (state->flashMode == LIGHT_FLASH_NONE) {
        g_attention = 0;
    }
    if (g_haveTrackballLight) {
        handle_trackball_light_locked(dev);
    }
    pthread_mutex_unlock(&g_lock);
    return 0;
}


/** Close the lights device */
static int
close_lights(struct light_device_t *dev)
{
    if (dev) {
        free(dev);
    }
    return 0;
}


/******************************************************************************/

/**
 * module methods
 */

/** Open a new instance of a lights device using name */
static int open_lights(const struct hw_module_t* module, char const* name,
        struct hw_device_t** device)
{
    int (*set_light)(struct light_device_t* dev,
            struct light_state_t const* state);

    if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
        set_light = set_light_backlight;
    }
    else if (0 == strcmp(LIGHT_ID_KEYBOARD, name)) {
        set_light = set_light_keyboard;
    }
    else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
        set_light = set_light_buttons;
    }
    else if (0 == strcmp(LIGHT_ID_BATTERY, name)) {
        set_light = set_light_battery;
    }
    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
        set_light = set_light_notifications;
    }
    else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) {
        set_light = set_light_attention;
    }
    else {
        return -EINVAL;
    }

    pthread_once(&g_init, init_globals);

    struct light_device_t *dev = malloc(sizeof(struct light_device_t));
    memset(dev, 0, sizeof(*dev));

    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 0;
    dev->common.module = (struct hw_module_t*)module;
    dev->common.close = (int (*)(struct hw_device_t*))close_lights;
    dev->set_light = set_light;

    *device = (struct hw_device_t*)dev;
    return 0;
}


static struct hw_module_methods_t lights_module_methods = {
    .open =  open_lights,
};

/*
 * The lights Module
 */
struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    //.version_major = 1,
    //.version_minor = 0,
    .id = LIGHTS_HARDWARE_MODULE_ID,
    .name = "MTK lights Module",
    .author = "MediaTek",
    .methods = &lights_module_methods,
};


你可能感兴趣的:(led,light)