Android lights system

Android 中的燈光子系統採用的是硬件訪問服務框架,JNI 層的文件是 com_android_server_lights_LightsService.cpp (frameworks\base\services\core\jni)
Java 程序通過 JNI 調用 setLight_native 來控制背光、通知燈、電池燈燈。 

Java: frameworks/base/services/core/java/com/android/server/lights/LightsService.java
JNI:  frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp
Hal:  lights.c

默認配色:frameworks/base/core/res/res/values/config.xml
電池燈:frameworks/base/services/core/java/com/android/server/BatteryService.java
通知燈:frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

static light_device_t* get_device(hw_module_t* module, char const* name)
{
    int err;
    hw_device_t* device;
    err = module->methods->open(module, name, &device);
    if (err == 0) {
        return (light_device_t*)device;
    } else {
        return NULL;
    }
}

static jlong init_native(JNIEnv *env, jobject clazz)
{
    int err;
    hw_module_t* module;
    Devices* devices;
    
    devices = (Devices*)malloc(sizeof(Devices));

    err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
    if (err == 0) {
        devices->lights[LIGHT_INDEX_BACKLIGHT]
                = get_device(module, LIGHT_ID_BACKLIGHT);
        ...
        devices->lights[LIGHT_INDEX_BATTERY]
                = get_device(module, LIGHT_ID_BATTERY);
        devices->lights[LIGHT_INDEX_ATTENTION]
                = get_device(module, LIGHT_ID_ATTENTION);
        ...
    } else {
        memset(devices, 0, sizeof(Devices));
    }

    return (jlong)devices;
}

JNI 文件通過多次調用 open 函數,獲得多個不同的 light_device_t ,存入 devices->lights 數組,通過不同的 light_device_t 
調用 devices->lights[light]->set_light(devices->lights[light], &state);
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_BATTERY, name)) {
                set_light = set_light_battery;
        }
        else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
                set_light = set_light_notifications;
        }
        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,
}; 
以背光爲例:
char const*const LCD_BACKLIGHT_FILE     = "/dev/backlight-1wire";
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;
}
/* The actual lights controlling section */
static int set_light_backlight (struct light_device_t *dev, struct light_state_t const *state) {
        int brightness = rgb_to_brightness(state);
        ALOGV("%s brightness=%d color=0x%08x", __func__,brightness,state->color);
        pthread_mutex_lock(&g_lock);

        /* brightness 0-255 */
        /* LCD_BACKLIGHT_FILE能接收是0-127 */
        
        write_int (LCD_BACKLIGHT_FILE, brightness/2);
        
        pthread_mutex_unlock(&g_lock);
        return 0;
}
電池燈和通知燈使用的是同一個硬件(RGB三顆led),使用開發板上的3個普通led進行模擬
char const*const RED_LED_FILE                         = "/sys/class/leds/led1/brightness";
char const*const GREEN_LED_FILE                       = "/sys/class/leds/led2/brightness";
char const*const BLUE_LED_FILE                         = "/sys/class/leds/led3/brightness";
代碼不在分析

設置LED狀態:
struct light_state_t {
    /**
     * The color of the LED in ARGB.
     *
     * Do your best here.
     *   - If your light can only do red or green, if they ask for blue,
     *     you should do green.
     *   - If you can only do a brightness ramp, then use this formula:
     *      unsigned char brightness = ((77*((color>>16)&0x00ff))
     *              + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
     *   - If you can only do on or off, 0 is off, anything else is on.
     *
     * The high byte should be ignored.  Callers will set it to 0xff (which
     * would correspond to 255 alpha).
     */
    unsigned int color;  // 把燈設爲什麼顏色, 或 把LCD的亮度設爲什麼

    /**
     * See the LIGHT_FLASH_* constants
     */
    int flashMode; // 是否閃爍, LIGHT_FLASH_NONE表示不閃
    int flashOnMS; // 亮的時間
    int flashOffMS;// 滅的時間

    /**
     * Policy used by the framework to manage the light's brightness.
     * Currently the values are BRIGHTNESS_MODE_USER and BRIGHTNESS_MODE_SENSOR.
     */
    int brightnessMode;  // 表示LCD的背光亮度模式
};

完整 HAL 代碼
#define LOG_NDEBUG 0
#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 <sys/ioctl.h>
#include <sys/types.h>
#include <hardware/lights.h>

char const*const RED_LED_FILE                  = "/sys/class/leds/led1/brightness";
char const*const GREEN_LED_FILE                = "/sys/class/leds/led2/brightness";
char const*const BLUE_LED_FILE                 = "/sys/class/leds/led3/brightness";
char const*const RED_LED_FILE_TRIGGER          = "/sys/class/leds/led1/trigger";
char const*const GREEN_LED_FILE_TRIGGER        = "/sys/class/leds/led2/trigger";
char const*const BLUE_LED_FILE_TRIGGER         = "/sys/class/leds/led3/trigger";
char const*const RED_LED_FILE_DELAYON          = "/sys/class/leds/led1/delay_on";
char const*const GREEN_LED_FILE_DELAYON        = "/sys/class/leds/led2/delay_on";
char const*const BLUE_LED_FILE_DELAYON         = "/sys/class/leds/led3/delay_on";
char const*const RED_LED_FILE_DELAYOFF         = "/sys/class/leds/led1/delay_off";
char const*const GREEN_LED_FILE_DELAYOFF       = "/sys/class/leds/led2/delay_off";
char const*const BLUE_LED_FILE_DELAYOFF        = "/sys/class/leds/led3/delay_off";
char const*const LCD_BACKLIGHT_FILE            = "/dev/backlight-1wire";

/* Synchronization primities */
static pthread_once_t g_init = PTHREAD_ONCE_INIT;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
/* Mini-led state machine */
static struct light_state_t g_notification;
static struct light_state_t g_battery;


static int write_int (const char *path, int value) {
        int fd;
        static int already_warned = 0;
        fd = open(path, O_RDWR);
        if (fd < 0) {
                if (already_warned == 0) {
                        ALOGE("write_int failed to open %s\n", path);
                        already_warned = 1;
                }
                return -errno;
        }
        char buffer[20];
        int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
        int written = write (fd, buffer, bytes);
        close(fd);
        return written == -1 ? -errno : 0;
}
static int write_string (const char *path, const char *value) {
        int fd;
        static int already_warned = 0;
        fd = open(path, O_RDWR);
        if (fd < 0) {
                if (already_warned == 0) {
                        ALOGE("write_string failed to open %s\n", path);
                        already_warned = 1;
                }
                return -errno;
        }
        char buffer[20];
        int bytes = snprintf(buffer, sizeof(buffer), "%s\n", value);
        int written = write (fd, buffer, bytes);
        close(fd);
        return written == -1 ? -errno : 0;
}
/* Color tools */
static int is_lit (struct light_state_t const* state) {
        return state->color & 0x00ffffff;
}
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;
}
/* The actual lights controlling section */
static int set_light_backlight (struct light_device_t *dev, struct light_state_t const *state) {
        int brightness = rgb_to_brightness(state);
        ALOGV("%s brightness=%d color=0x%08x", __func__,brightness,state->color);
        pthread_mutex_lock(&g_lock);

        /* brightness 0-255 */
        /* LCD_BACKLIGHT_FILE能接收是0-127 */
        
        write_int (LCD_BACKLIGHT_FILE, brightness/2);
        
        pthread_mutex_unlock(&g_lock);
        return 0;
}
static void set_shared_light_locked (struct light_device_t *dev, struct light_state_t *state) {
        int r, g, b;
        int delayOn,delayOff;
        r = (state->color >> 16) & 0xFF;
        g = (state->color >> 8) & 0xFF;
        b = (state->color) & 0xFF;
        delayOn = state->flashOnMS;
        delayOff = state->flashOffMS;
        if (state->flashMode != LIGHT_FLASH_NONE) {
                write_string (RED_LED_FILE_TRIGGER, "timer");
                write_string (GREEN_LED_FILE_TRIGGER, "timer");
                write_string (BLUE_LED_FILE_TRIGGER, "timer");
                write_int (RED_LED_FILE_DELAYON, delayOn);
                write_int (GREEN_LED_FILE_DELAYON, delayOn);
                write_int (BLUE_LED_FILE_DELAYON, delayOn);
                write_int (RED_LED_FILE_DELAYOFF, delayOff);
                write_int (GREEN_LED_FILE_DELAYOFF, delayOff);
                write_int (BLUE_LED_FILE_DELAYOFF, delayOff);
        } else {
                write_string (RED_LED_FILE_TRIGGER, "none");
                write_string (GREEN_LED_FILE_TRIGGER, "none");
                write_string (BLUE_LED_FILE_TRIGGER, "none");
        }
        write_int (RED_LED_FILE, r);
        write_int (GREEN_LED_FILE, g);
        write_int (BLUE_LED_FILE, b);
}
static void handle_shared_battery_locked (struct light_device_t *dev) {
        if (is_lit (&g_notification)) {
                set_shared_light_locked (dev, &g_notification);
        } else {
                set_shared_light_locked (dev, &g_battery);
        }
}
static int set_light_battery (struct light_device_t *dev, struct light_state_t const* state) {

        ALOGV("%s flashMode=%d onMS = %d offMS = %d color=0x%08x", __func__,state->flashMode,state->flashOnMS,state->flashOffMS,state->color);

        pthread_mutex_lock (&g_lock);
        g_battery = *state;
        handle_shared_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) {
        ALOGV("%s flashMode=%d onMS = %d offMS = %d color=0x%08x", __func__,state->flashMode,state->flashOnMS,state->flashOffMS,state->color);
        pthread_mutex_lock (&g_lock);
        g_notification = *state;
        handle_shared_battery_locked(dev);
        pthread_mutex_unlock (&g_lock);
        return 0;
}
/* Initializations */
void init_globals () {
        pthread_mutex_init (&g_lock, NULL);
}
/* Glueing boilerplate */
static int close_lights (struct light_device_t *dev) {
        if (dev)
                free(dev);
        return 0;
}
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_BATTERY, name)) {
                set_light = set_light_battery;
        }
        else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
                set_light = set_light_notifications;
        }
        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,
};
struct hw_module_t HAL_MODULE_INFO_SYM = {
        .tag = HARDWARE_MODULE_TAG,
        .version_major = 1,
        .version_minor = 0,
        .id = LIGHTS_HARDWARE_MODULE_ID,
        .name = "Sony lights module",
        .author = "Diogo Ferreira <[email protected]>, Andreas Makris <[email protected]>",
        .methods = &lights_module_methods,
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章