一、Android 呼吸灯的使用

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

package com.example.test;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.app.Activity;import android.app.Notification;import android.app.NotificationManager;public class MainActivity extends Activity {Button working;EditText ledOn;EditText ledOff;final int ID_LED=19871103;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ledOn = (EditText)findViewById(R.id.LedOn);ledOff = (EditText)findViewById(R.id.LedOff);working = (Button)findViewById(R.id.bu1);working.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubNotificationManager nm=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);Notification notification = new Notification();notification.ledARGB = 0xffff0000; //这里是颜色,我们可以尝试改变,理论上0xFFff0000是红色//notification.ledOnMS = 350;//notification.ledOffMS = 300;notification.ledOnMS = Integer.parseInt((ledOn.getText().toString()));notification.ledOffMS = Integer.parseInt((ledOff.getText().toString()));notification.flags = Notification.FLAG_SHOW_LIGHTS;nm.notify(ID_LED, notification);}});//nm.cancel(ID_LED);}}
      通过该程序,便能自由控制呼吸灯的占空比与频率。

二、Android上层呼吸灯的实现

     1、NotificationManager

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

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

public void notify(int id, Notification notification)107    {108        notify(null, id, notification);109    }110111    /**112     * Post a notification to be shown in the status bar. If a notification with113     * the same tag and id has already been posted by your application and has not yet been114     * canceled, it will be replaced by the updated information.115     *116     * @param tag A string identifier for this notification.  May be {@code null}.117     * @param id An identifier for this notification.  The pair (tag, id) must be unique118     *        within your application.119     * @param notification A {@link Notification} object describing what to120     *        show the user. Must not be null.121     */122    public void notify(String tag, int id, Notification notification)123    {124        int[] idOut = new int[1];125        INotificationManager service = getService();126        String pkg = mContext.getPackageName();127        if (notification.sound != null) {128            notification.sound = notification.sound.getCanonicalUri();129        }130        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");131        try {132            service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,133                    UserHandle.myUserId());134            if (id != idOut[0]) {135                Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);136            }137        } catch (RemoteException e) {138        }139    }
      抓取到关键点:enqueueNotificationWithTag。它位于类NotificationManagerService.java中,具体位置如下:

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

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

      

 public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,956            int[] idOut, int userId)957    {958        enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),959                tag, id, notification, idOut, userId);960    }961962    private final static int clamp(int x, int low, int high) {963        return (x < low) ? low : ((x > high) ? high : x);964    }965966    // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the967    // uid/pid of another application)968    public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,969            String tag, int id, Notification notification, int[] idOut, int userId)970    {971        if (DBG) {972            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);973        }974        checkCallerIsSystemOrSameApp(pkg);975        final boolean isSystemNotification = ("android".equals(pkg));976977        userId = ActivityManager.handleIncomingUser(callingPid,978                callingUid, userId, true, false, "enqueueNotification", pkg);979        UserHandle user = new UserHandle(userId);980981        // Limit the number of notifications that any given package except the android982        // package can enqueue.  Prevents DOS attacks and deals with leaks.983        if (!isSystemNotification) {
        很显然我们进入了:enqueueNotificationInternal,该函数太长,不复制了就,~_~。这个函数中实现了不少功能,如是否播放声音,是否震动。最后找到控制呼吸灯的位置在这个方法中:

1321            if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 01322                    && canInterrupt) {1323                mLights.add(r);1324                updateLightsLocked();1325            } else {1326                if (old != null1327                        && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {1328                    updateLightsLocked();1329                }1330            }
        很显然,关键点就是:updateLightsLocked()。进入之后会有一系列判断、赋值之类操作。之后进入:

               private LightsService.Light mNotificationLight;1602            if (mNotificationPulseEnabled) {1603                // pulse repeatedly1604            ///M: log lights information1605            Log.d(TAG, "notification setFlashing ledOnMS = "+ledOnMS + " ledOffMS = "+ ledOffMS);1606                mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,1607                        ledOnMS, ledOffMS);1608                ///M:1609            } else {1610                // pulse only once1611                mNotificationLight.pulse(ledARGB, ledOnMS);1612            }
         然后,我们开始进入LightsService。

       (4).LightsService

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

116        private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {117            if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {118                if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"119                        + Integer.toHexString(color));120                mColor = color;121                mMode = mode;122                mOnMS = onMS;123                mOffMS = offMS;124                setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);125            }126        }
       2.JNI

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

127    ALOGD("setLight_native: light=%d, colorARGB=0x%x, flashMode=%d, onMS=%d, offMS=%d, brightnessMode=%d",128light, colorARGB, flashMode, onMS, offMS, brightnessMode);129130#if defined(MTK_AAL_SUPPORT)131    if (light == LIGHT_INDEX_BACKLIGHT) {132        if (AALClient::getInstance().setBacklightColor(colorARGB & 0x00ffffff) == 0)133            return;134        ALOGW("Fail to set backlight from AAL service");135    }136#endif137138    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下的:

584    }585    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {586        set_light = set_light_notifications;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:
465    if (red) {466        blink_green(0, 0, 0);467        blink_blue(0, 0, 0);468        blink_red(red, onMS, offMS);469    }470    else if (green) {471        blink_red(0, 0, 0);472        blink_blue(0, 0, 0);473        blink_green(green, onMS, offMS);474    }475    else if (blue) {476        blink_red(0, 0, 0);477        blink_green(0, 0, 0);478        blink_blue(blue, onMS, offMS);479    }480    else {481        blink_red(0, 0, 0);482        blink_green(0, 0, 0);483        blink_blue(0, 0, 0);484    }
         进入了red函数之后,重点如下:
                 上层传下来的的level值为0,则直接关闭RED_LED_FILE(char const*const RED_LED_FILE = "/sys/class/leds/red/brightness")
 
248if (nowStatus == 0) { 249        write_int(RED_LED_FILE, 0);250}
         上层传下的来的参数onMS和offMS都有值,则呼吸灯闪烁:

251else if (nowStatus == 1) {252//        write_int(RED_LED_FILE, level); // default full brightness253write_str(RED_TRIGGER_FILE, "timer");    254while (((access(RED_DELAY_OFF_FILE, F_OK) == -1) || (access(RED_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) {255ALOGD("RED_DELAY_OFF_FILE doesn't exist or cannot write!!\n");256led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs257i++;258}259write_int(RED_DELAY_OFF_FILE, offMS);260write_int(RED_DELAY_ON_FILE, onMS);261}
         其他情况下,我们直接就点亮红色呼吸灯:
262else {263write_str(RED_TRIGGER_FILE, "none");264        write_int(RED_LED_FILE, 255); // default full brightness265}
       4.小结

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



 

更多相关文章

  1. C语言函数以及函数的使用
  2. Android进程so注入Hook java方法
  3. Android JNI使用方法
  4. Android 布局中的android:onClick的使用方法总结
  5. Android工程手动增加插件包方法
  6. android颜色值的表示方法android:background="#FFFFFFFF"的意思
  7. android设置横屏和竖屏的方法

随机推荐

  1. Android获取屏幕分辨率及DisplayMetrics
  2. Android消息机制
  3. android在EditText中插入表情图片
  4. android 自定义控件学习之三 控件布局常
  5. Android中LocationManager的简单使用,获
  6. Android(安卓)Zip文件解压缩代码
  7. Android中Uri的使用
  8. ANDROID 应用退出
  9. Android利用Fiddler进行网络数据抓包
  10. AndFix解析——(上)