二、 Brightness 的调节
现在通过一个具体的电源管理实例来了解从 andriod 上层到内核驱动层的整个调用流程。
如果你使用过 android 操作系统,无论是模拟器还是开发板亦或手机,对里面 setting 这个服务一定很熟悉吧。其中有一项是用于调节显示屏亮度的:
setting/sound & display settings/brightness 。
这个功能是怎么实现的呢。通过分析,我们可以清晰看到整个调用的流程,上层是如何 一步一步到达驱动层,把 LCD 屏幕亮度改变的。
1 、 android 层的调用流程。 1 )、 BrightnessPreference.java
/packages/apps/Settings/src/com/android/settings/
// Backlight range is from 0 - 255. Need to make sure that user // doesn't set the backlight to 0 and get stuck private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10; private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON; |
当系统检测到调节亮度的调节栏改变时,会重新设置屏幕的亮度:
public void (CompoundButton buttonView, boolean isChecked) { setMode(isChecked ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC : Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); if (!isChecked) { setBrightness(mSeekBar.getProgress() + MINIMUM_BACKLIGHT); } } private void setBrightness(int brightness) { try { IPowerManager power = IPowerManager.Stub.asInterface( ServiceManager.getService("power")); if (power != null) { power.setBacklightBrightness(brightness); } } catch (RemoteException doe) { } } |
首先, onCheckedChanged 会检查当前亮度调节的模式,如果是 AUTOMATIC 模式,则 isChecked 为 1 ( MANUAL 为 0 )。当 isChecked 为 0 时,即当前模式为手动调节模式,则会调用 setBrightness 调节亮度。
可以看到, setBrightness 先会通过调用 ServiceManager.getService("power") 获得 power manager service 这个服务的对象 power, 获取成功后会调用 power.setBacklightBrightness(brightness); 我们进入 PowerManagerService.java 看这个方法的具体内容。
2 )、 PowerManagerService.java
/frameworks/base/services/java/com/android/server/
手动调用的范围是 MINI 到 MAX 。而 setBrightness 传进来的参数为 MINI + MINIMUM_BACKLIGHT 到 MAX + MINIMUM_BACKLIGHT ,以确保 brightness 最小为 MINIMUM_BACKLIGHT 。在 setBrightness 方法的一开始又对 brightness 进行了修正,从而确保应用程序不能将屏幕全部熄灭。
// Don't let applications turn the screen all the way off brightness = Math.max(brightness, Power.BRIGHTNESS_DIM) |
setBacklightBrightness 这个方法会调节 lcd backlight 、 keyboard light 、和 buttons light 这三个设备的亮度,但都是通过调用 setLightBrightness_UNCHECKED 实现的。
mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, brightness, HardwareService.BRIGHTNESS_MODE_USER); mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, (mKeyboardVisible ? brightness : 0), HardwareService.BRIGHTNESS_MODE_USER); mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, brightness, HardwareService.BRIGHTNESS_MODE_USER); |
让我们先看一下第一个设备亮度的调节,注意参数 LIGHT_ID_BACKLIGHT ,它将决定选择操作哪一个设备。
3 )、 HardwareService.java
/frameworks/base/services/java/com/android/server/
void setLightBrightness_UNCHECKED(int light, int brightness, int brightnessMode) { int b = brightness & 0x000000ff; b = 0xff000000 | (b << 16) | (b << 8) | b; setLight_native(mNativePointer, light, b, LIGHT_FLASH_NONE, 0, 0, brightnessMode); } |
setLightBrightness_UNCHECKED 调用了本地方法 setLight_native ,同时将 brightness 做了相就的处理,处理后的数据作为 colorARGB 传入本地方法。
4 )、 com_android_server_HardwareService.java
/frameworks/base/services/jni/
setLight_native 函数中定义了一个类型为 struct light_state_t 的结构体,并对这个结构体的成员进行赋值,最后对一个函数指针进行了赋值:
devices->lights[light]->set_light(devices->lights[light],&state);
先来看看这个结构体:
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;
int flashMode; 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; }; |
再来看这个函数指针指向的函数。在 Lights.c 这个文件中的 open_lights 这个函数中,对传入参数的不同对 set_light 进行了赋值:
在这里, name 传入的是 LIGHT_ID_BACKLIGHT
if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) { set_light = set_light_backlight; } |
set_light_backlight 的函数原型如下:
static int set_light_backlight(struct light_device_t* dev, struct light_state_t const* state) |
再对比一下函数指针的赋值:
devices->lights[light]->set_light(devices->lights[light],&state);
可见,参数 devices->lights[light] , &state 分别传入了 dev 和 state 。
让我们来看一下 set_light_backlight 的内容:
5 )、 Lights.c
/hardware/msm7k/liblights/
这个文件已经不陌生了吧,上文刚刚提到呵。
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; } |
(1) 、在这里对 brightness 进行了特殊的赋值:
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; } |
之所以这样赋值在 struct light_state_t 的注释中有解释。
(2) 、 write_int 的参数 LCD_FILE
char const*const LCD_FILE = "/sys/class/leds/lcd-backlight/brightness"; |
这个路径是 kernel 中 sysfs 生成的设备节点,用户可以直接对这个设备节点进行操作。
sysfs ~~~~~
When a driver is registered, a sysfs directoryis created in its bus's directory. In this directory, the driver can export aninterface to userspace to control operation of the driver on a global basis;e.g. toggling debugging output in the driver.
A future feature of this directory will be a'devices' directory. This directory will contain symlinks to the directories ofdevices it supports.
(3) 、
err = write_int(LCD_FILE, brightness); write_int(char const* path, int value) { int fd; static int already_warned = 0;
fd = open(path, O_RDWR); 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 { if (already_warned == 0) { LOGE("write_int failed to open %s/n", path); already_warned = 1; } return -errno; } |
打开设备结点后,最终通过 write 将 brigthness 写入到了设备结点。