Android 呼吸灯流程分析(一)

来源:互联网 发布:linux进程调度算法 编辑:程序博客网 时间:2024/04/29 01:40
 

Android 呼吸灯流程分析(一)

转自:http://blog.csdn.net/u011630458/article/details/22280841

一、Android 呼吸灯的使用

     在讲呼吸灯实现流程之前,我们先看一下如何使用它。
     Android提供了呼吸灯的接口,我们可以通过该接口,控制呼吸灯的闪烁频率和占空比。具体代码如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.example.test;  
  2.   
  3. import android.os.Bundle;  
  4. import android.view.View;  
  5. import android.widget.Button;  
  6. import android.widget.EditText;  
  7. import android.app.Activity;  
  8. import android.app.Notification;  
  9. import android.app.NotificationManager;  
  10.   
  11. public class MainActivity extends Activity {  
  12.     Button working;  
  13.     EditText ledOn;  
  14.     EditText ledOff;      
  15.       
  16.     final int ID_LED=19871103;  
  17.   
  18.     @Override  
  19.     protected void onCreate(Bundle savedInstanceState) {  
  20.         super.onCreate(savedInstanceState);  
  21.         setContentView(R.layout.activity_main);  
  22.           
  23.         ledOn = (EditText)findViewById(R.id.LedOn);  
  24.         ledOff = (EditText)findViewById(R.id.LedOff);  
  25.         working = (Button)findViewById(R.id.bu1);  
  26.         working.setOnClickListener(new View.OnClickListener() {  
  27.               
  28.             @Override  
  29.             public void onClick(View arg0) {  
  30.                 // TODO Auto-generated method stub  
  31.                 NotificationManager nm=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);  
  32.                 Notification notification = new Notification();  
  33.                 notification.ledARGB = 0xffff0000//这里是颜色,我们可以尝试改变,理论上0xFFff0000是红色  
  34. //              notification.ledOnMS = 350;  
  35. //              notification.ledOffMS = 300;  
  36.                 notification.ledOnMS = Integer.parseInt((ledOn.getText().toString()));  
  37.                 notification.ledOffMS = Integer.parseInt((ledOff.getText().toString()));  
  38.                 notification.flags = Notification.FLAG_SHOW_LIGHTS;  
  39.                 nm.notify(ID_LED, notification);  
  40.             }  
  41.         });  
  42.         //nm.cancel(ID_LED);  
  43.     }  
  44. }  
      通过该程序,便能自由控制呼吸灯的占空比与频率。

二、Android上层呼吸灯的实现

     1、NotificationManager

      (1).在apk中我们填充了结构notification,并调用了nm.notify。很显然,找到了关键点NotificationManager,对应文件为:
              frameworks/base/core/java/android/app/NotificationManager.java

      (2).在NotificationManager.java中找到了我们的调用方法notify。 对应如下: 

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public void notify(int id, Notification notification)  
  2. 107    {  
  3. 108        notify(null, id, notification);  
  4. 109    }  
  5. 110  
  6. 111    /**  
  7. 112     * Post a notification to be shown in the status bar. If a notification with  
  8. 113     * the same tag and id has already been posted by your application and has not yet been  
  9. 114     * canceled, it will be replaced by the updated information.  
  10. 115     *  
  11. 116     * @param tag A string identifier for this notification.  May be {@code null}.  
  12. 117     * @param id An identifier for this notification.  The pair (tag, id) must be unique  
  13. 118     *        within your application.  
  14. 119     * @param notification A {@link Notification} object describing what to  
  15. 120     *        show the user. Must not be null.  
  16. 121     */  
  17. 122    public void notify(String tag, int id, Notification notification)  
  18. 123    {  
  19. 124        int[] idOut = new int[1];  
  20. 125        INotificationManager service = getService();  
  21. 126        String pkg = mContext.getPackageName();  
  22. 127        if (notification.sound != null) {  
  23. 128            notification.sound = notification.sound.getCanonicalUri();  
  24. 129        }  
  25. 130        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");  
  26. 131        try {  
  27. 132            service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,  
  28. 133                    UserHandle.myUserId());  
  29. 134            if (id != idOut[0]) {  
  30. 135                Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);  
  31. 136            }  
  32. 137        } catch (RemoteException e) {  
  33. 138        }  
  34. 139    }  
      抓取到关键点:enqueueNotificationWithTag。它位于类NotificationManagerService.java中,具体位置如下:

      frameworks/base/services/java/com/android/server/NotificationManagerService.java
      (3).NotificationManagerService

       进入类NotificationManagerService.java,找到我们在NotificationManager.java中调用的方法:enqueueNotificationWithTag,如下:

      

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1.  public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,  
  2. 956            int[] idOut, int userId)  
  3. 957    {  
  4. 958        enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),  
  5. 959                tag, id, notification, idOut, userId);  
  6. 960    }  
  7. 961  
  8. 962    private final static int clamp(int x, int low, int high) {  
  9. 963        return (x < low) ? low : ((x > high) ? high : x);  
  10. 964    }  
  11. 965  
  12. 966    // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the  
  13. 967    // uid/pid of another application)  
  14. 968    public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,  
  15. 969            String tag, int id, Notification notification, int[] idOut, int userId)  
  16. 970    {  
  17. 971        if (DBG) {  
  18. 972            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);  
  19. 973        }  
  20. 974        checkCallerIsSystemOrSameApp(pkg);  
  21. 975        final boolean isSystemNotification = ("android".equals(pkg));  
  22. 976  
  23. 977        userId = ActivityManager.handleIncomingUser(callingPid,  
  24. 978                callingUid, userId, true, false, "enqueueNotification", pkg);  
  25. 979        UserHandle user = new UserHandle(userId);  
  26. 980  
  27. 981        // Limit the number of notifications that any given package except the android  
  28. 982        // package can enqueue.  Prevents DOS attacks and deals with leaks.  
  29. 983        if (!isSystemNotification) {  
        很显然我们进入了:enqueueNotificationInternal,该函数太长,不复制了就,~_~。这个函数中实现了不少功能,如是否播放声音,是否震动。最后找到控制呼吸灯的位置在这个方法中:

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 1321            if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0  
  2. 1322                    && canInterrupt) {  
  3. 1323                mLights.add(r);  
  4. 1324                updateLightsLocked();  
  5. 1325            } else {  
  6. 1326                if (old != null  
  7. 1327                        && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {  
  8. 1328                    updateLightsLocked();  
  9. 1329                }  
  10. 1330            }  
        很显然,关键点就是:updateLightsLocked()。进入之后会有一系列判断、赋值之类操作。之后进入:

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1.                private LightsService.Light mNotificationLight;  
  2.   
  3. 1602            if (mNotificationPulseEnabled) {  
  4. 1603                // pulse repeatedly  
  5. 1604                ///M: log lights information  
  6. 1605                Log.d(TAG, "notification setFlashing ledOnMS = "+ledOnMS + " ledOffMS = "+ ledOffMS);  
  7. 1606                mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,  
  8. 1607                        ledOnMS, ledOffMS);  
  9. 1608                ///M:  
  10. 1609            } else {  
  11. 1610                // pulse only once  
  12. 1611                mNotificationLight.pulse(ledARGB, ledOnMS);  
  13. 1612            }  
         然后,我们开始进入LightsService。

       (4).LightsService

       LightsService的位置如下:
                   frameworks/base/services/java/com/android/server/LightsService.java
      在LightsService中,通过方法:setFlashing 调用:setLightLocked,最终到达了JNI的setLight_native;

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 116        private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {  
  2. 117            if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {  
  3. 118                if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"  
  4. 119                        + Integer.toHexString(color));  
  5. 120                mColor = color;  
  6. 121                mMode = mode;  
  7. 122                mOnMS = onMS;  
  8. 123                mOffMS = offMS;  
  9. 124                setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);  
  10. 125            }  
  11. 126        }  
       2.JNI

      通过方法setLight_native,进入到了JNI层中,位置如下:
                      frameworks/base/services/jni/com_android_server_LightsService.cpp
      在方法:setLight_native中,一样的进行了相关的判断、接受上层赋值.之后根据参数,调用了对应的set_light:

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 127    ALOGD("setLight_native: light=%d, colorARGB=0x%x, flashMode=%d, onMS=%d, offMS=%d, brightnessMode=%d",  
  2. 128 light, colorARGB, flashMode, onMS, offMS, brightnessMode);  
  3. 129  
  4. 130#if defined(MTK_AAL_SUPPORT)  
  5. 131    if (light == LIGHT_INDEX_BACKLIGHT) {  
  6. 132        if (AALClient::getInstance().setBacklightColor(colorARGB & 0x00ffffff) == 0)  
  7. 133            return;  
  8. 134        ALOGW("Fail to set backlight from AAL service");  
  9. 135    }  
  10. 136#endif  
  11. 137  
  12. 138    devices->lights[light]->set_light(devices->lights[light], &state);  
      最后通过set_light转入了HAL层。

      3.HAL

     呼吸灯的HAL层对应位置如下:
                    mediatek/hardware/liblights/lights.c
    我们在NotificationManager下来的set_light对应为:
                    open_lights下的:

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 584    }  
  2. 585    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {  
  3. 586        set_light = set_light_notifications;  
  4. 587    }  
      进入set_light_notifications,通过如下调用:
                  set_light_notifications ----> handle_speaker_battery_locked ---->  set_speaker_light_locked
       在函数set_speaker_light_locked中,最后判断我们要控制的led是red,green还是blue,从我的范例上看,我传入的led参数为0xffff0000,对应为red。于是,进入如下的          red:
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 465    if (red) {  
  2. 466        blink_green(0, 0, 0);  
  3. 467        blink_blue(0, 0, 0);  
  4. 468        blink_red(red, onMS, offMS);  
  5. 469    }  
  6. 470    else if (green) {  
  7. 471        blink_red(0, 0, 0);  
  8. 472        blink_blue(0, 0, 0);  
  9. 473        blink_green(green, onMS, offMS);  
  10. 474    }  
  11. 475    else if (blue) {  
  12. 476        blink_red(0, 0, 0);  
  13. 477        blink_green(0, 0, 0);  
  14. 478        blink_blue(blue, onMS, offMS);  
  15. 479    }  
  16. 480    else {  
  17. 481        blink_red(0, 0, 0);  
  18. 482        blink_green(0, 0, 0);  
  19. 483        blink_blue(0, 0, 0);  
  20. 484    }  
         进入了red函数之后,重点如下:
                 上层传下来的的level值为0,则直接关闭RED_LED_FILE(char const*const RED_LED_FILE = "/sys/class/leds/red/brightness")
 
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 248 if (nowStatus == 0) {   
  2. 249         write_int(RED_LED_FILE, 0);  
  3. 250 }  
         上层传下的来的参数onMS和offMS都有值,则呼吸灯闪烁:

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 251 else if (nowStatus == 1) {  
  2. 252//           write_int(RED_LED_FILE, level); // default full brightness  
  3. 253     write_str(RED_TRIGGER_FILE, "timer");      
  4. 254     while (((access(RED_DELAY_OFF_FILE, F_OK) == -1) || (access(RED_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) {  
  5. 255         ALOGD("RED_DELAY_OFF_FILE doesn't exist or cannot write!!\n");  
  6. 256         led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs  
  7. 257         i++;  
  8. 258     }  
  9. 259     write_int(RED_DELAY_OFF_FILE, offMS);  
  10. 260     write_int(RED_DELAY_ON_FILE, onMS);  
  11. 261 }  
         其他情况下,我们直接就点亮红色呼吸灯:
[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. 262 else {  
  2. 263     write_str(RED_TRIGGER_FILE, "none");  
  3. 264         write_int(RED_LED_FILE, 255); // default full brightness  
  4. 265 }  
       4.小结

       到处Andoid上层的呼吸灯基本上就这个。
      在HAL中最后,点亮,关闭和闪烁呼吸灯,点亮和关闭呼吸灯都是直接操作设备接口: RED_LED_FILE = "/sys/class/leds/red/brightness";
      闪烁则相对复杂一些,接下来驱动部分就以闪烁为为范例进行讲解。

0 0
原创粉丝点击