Android系统中的屏幕状态切换以及亮度设置
来源:互联网 发布:伊的家网络销售怎么样 编辑:程序博客网 时间:2024/05/16 09:58
Android系统的状态包括wake、earlysuspend以及suspend状态,其使用锁和定时器来进行状态的切换。
而在wake状态,屏幕首先是调至设定的亮度,如果没有其他动作,当经过一段时间后屏幕会变暗,再经过一段时间屏幕会关闭,于是屏幕的状态也包括3种:bright、dim、off。
在Android应用框架层中的PowerManagerService.java(framework/base/services/java/com/android/server/)中实现了上述屏幕状态的切换。下面对PowerManagerService.java如何切换屏幕状态进行分析。
在PowerManagerService的初始化函数init中,会进行必要参数的初始化,包括LightsService,BatteryService,Thread等等,然后会使用forceUserActivityLocked点亮屏幕。
在forceUserActivityLocked中主要是使用userActivity点亮屏幕
在userActivity方法中会收集所有锁的状态(mLocks存储了所有申请的锁),然后通过setPowerState方法来设置系统的状态,最后通过setTimeoutLocked来开启定时器
setPowerState方法会根据输入的状态调用setScreenStateLocked方法来设置系统状态
在setScreenStateLocked方法中会使用Power.setScreenState方法调用jni层中的函数,最终会传递至内核层,在内核层中执行相应的睡眠系统或唤醒系统
而setScreenStateLocked方法中,函数updateLightsLocked用来更新lights,后面将进行分析。
以上一小段介绍了userActivity唤醒系统的简单流程,而在PowerManagerService的初始化函数中,不仅会通过userActivity中的setPowerState来唤醒系统,同时也会使用userActivity中的setTimeoutLocked来开启一个定时器,用于切换屏幕的状态
在PowerManagerService的初始化函数中最终会通过setTimeoutLocked来启动定时器,当定时器到时间后就会执行mTimeoutTask任务,下面可以看看mTimeoutTask任务的定义:
TimeoutTask mTimeoutTask = new TimeoutTask();
mTimeoutTask定义为类型是TimeoutTask的对象,TimeoutTask如下所示:
于是在PowerManagerService中通过定时器来切换屏幕的状态,同时也会调用setPowerState方法来睡眠或唤醒系统,而具体的屏幕亮度是如何实现的呢?
在setPowerState中使用了updateLightsLocked来更新屏幕的状态。
在updateLightsLocked方法中使用了mScreenBrightness.setTargetLocked方法来设置屏幕的亮度,而mScreenBrightness是类型为BrightnessState的对象,以下是其定义:
private final BrightnessState mScreenBrightness = new BrightnessState(SCREEN_BRIGHT_BIT);
此类是通过逐渐减少step值的亮度来是的屏幕到达最终的亮度,下面分析setTargetLocked方法
其中mScreenOffHandler移除或开始的事件即为mScreenBrightness本身,此时会执行BrightnessState类中的run方法。
在run方法中,不论是逐渐调制目标亮度stepLocked,或者直接变成目标亮度jumpToTargetLocked,都会调用到setLightBrightness方法用于改变亮度,下面来分析setLightBrightness方法。
在setLightBrightness方法中会根据mask的值来相应的改变屏幕、键盘和按键的亮度,屏幕的亮度主要使用mLcdLight.setBrightness方法,其中mLcdLight是类型为LightsService.Light的对象,在PowerManagerService的初始化函数中进行了赋值:
mLcdLight = lights.getLight(LightsService.LIGHT_ID_BACKLIGHT);
LightsService.Light类型定义在framework/base/services/java/com/android/server/LightsService.java中,下面来分析其setBrightness方法:
在setBrightness方法中,主要是将亮度值扩展成32bit的color值来提供给setLightLocked处理。
setLightLocked会调用setLight_native来将亮度值传递至jni层,setLight_native在文件framework/base/services/jni/com_android_server_LightsService.cpp中实现。
而setLight_native方法使用devices->lights[light]->set_light来设定亮度值。在com_android_server_LightsService.cpp的初始化函数中,会对devices进行初始化,如下所示:
该方法使用hw_get_module来获取动态库模块,其中LIGHTS_HARDWARE_MODULE_ID在/hardware/libhardware/include/hardware/lights.h中声明:
#define LIGHTS_HARDWARE_MODULE_ID "lights"
所以在硬件抽象层,如果要编写lights的模块供上层使用,需要将自身命名为lights的模块,下面以hardware/msm7k/liblights/lights.c为例,在其中就声明了“lights”模块
然后在com_android_server_LightsService.cpp的初始化函数中使用get_device来打开相应的设备,其调用了module->methods->open的方法,而在hardware/msm7k/liblights/lights.c也定义了回调函数:
在open_lights方法中也映射了set_light方法,于是在com_android_server_LightsService.cpp的devices->lights[light]->set_light最终会调用hardware/msm7k/liblights/lights.c中set_light所映射的方法,set_light_backlight就是设置屏幕亮度的方法。
其中char const*const LCD_FILE = "/sys/class/leds/lcd-backlight/brightness";
该方法会使用write_int函数将亮度写入至路径为LCD_FILE的文件中,即传递至内核层。
用户空间
//////////////////////////////////////////////////////////////////////////////////////////
内核空间
在内核空间中的kernel/drivers/leds/led-class.c模块初始化函数中,使用了class_create创建了“leds”类目录,同时在这个模块中给出了led_classdev_register用于注册led设备,除此之外,还给出了这个class下的相关属性:
//////////////////////////////////////////////////////////////////////////////
leds-lm3530.c模块为例子(有regulator)
在kernel/drivers/leds/leds-lm3530.c模块中,声明了名称为“lcd-backlight”的led设备,
并使用了led_classdev_register将其注册进入led class中。
于是就会产生/sys/class/leds/lcd-backlight/brightness的目录,所以上层使用write_int(LCD_FILE, brightness)向内核写入亮度值会调用kernel/drivers/leds/led-class.c模块中的led_brightness_store方法。
该方法调用led_set_brightness设置屏幕亮度,其在kernel/drivers/leds/leds.h中
于是就调用了kernel/drivers/leds/leds-lm3530.c模块中的回调函数brightness_set,即lm3530_brightness_set函数
lm3530_brightness_set方法在打开屏幕时会使用regulator_enable开启电源管理芯片的regulator输出,在关闭屏幕时会调用regulator_disable关闭电源管理芯片的regulator输出,在调整屏幕亮度时使用i2c_smbus_write_byte_data向寄存器中写入数值来调整亮度。
///////////////////////////////////////////////////////////
msm_fb.c模块为例子(无regulator)
在kernel/drivers/staging/msm/msm_fb.c模块中,声明了名称为“lcd-backlight”的led设备,并使用了led_classdev_register将其注册进入led class中。
该方法调用led_set_brightness设置屏幕亮度,其在kernel/drivers/leds/leds.h中
于是就调用了kernel/drivers/staging/msm/msm_fb.c模块中的回调函数brightness_set,即msm_fb_set_bl_brightness函数
在msm_fb_set_bl_brightness函数中将亮度从0-255映射成0-bl_max,然后使用msm_fb_set_backlight设置屏幕亮度
在msm_fb_set_backlight方法中主要调用了各个具体设备的set_backlight回调函数,实现了lcd屏幕的亮度调节。
而在wake状态,屏幕首先是调至设定的亮度,如果没有其他动作,当经过一段时间后屏幕会变暗,再经过一段时间屏幕会关闭,于是屏幕的状态也包括3种:bright、dim、off。
在Android应用框架层中的PowerManagerService.java(framework/base/services/java/com/android/server/)中实现了上述屏幕状态的切换。下面对PowerManagerService.java如何切换屏幕状态进行分析。
在PowerManagerService的初始化函数init中,会进行必要参数的初始化,包括LightsService,BatteryService,Thread等等,然后会使用forceUserActivityLocked点亮屏幕。
void init(Context context, LightsService lights, IActivityManager activity, BatteryService battery) { mLightsService = lights; // LightsService mLightsService mContext = context; mActivityService = activity; mBatteryStats = BatteryStatsService.getService(); mBatteryService = battery; mLcdLight = lights.getLight(LightsService.LIGHT_ID_BACKLIGHT); // LightsService.Light mLcdLight mButtonLight = lights.getLight(LightsService.LIGHT_ID_BUTTONS); mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD); mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); ...... synchronized (mLocks) { updateNativePowerStateLocked(); forceUserActivityLocked(); // 强制点亮屏幕 mInitialized = true; }}
在forceUserActivityLocked中主要是使用userActivity点亮屏幕
private void forceUserActivityLocked() { if (isScreenTurningOffLocked()) { // cancel animation so userActivity will succeed mScreenBrightness.animating = false; } boolean savedActivityAllowed = mUserActivityAllowed; mUserActivityAllowed = true; userActivity(SystemClock.uptimeMillis(), false); // 使用userActivity点亮屏幕 mUserActivityAllowed = savedActivityAllowed;}public void userActivity(long time, boolean noChangeLights) { ...... userActivity(time, -1, noChangeLights, OTHER_EVENT, false);}
在userActivity方法中会收集所有锁的状态(mLocks存储了所有申请的锁),然后通过setPowerState方法来设置系统的状态,最后通过setTimeoutLocked来开启定时器
private void userActivity(long time, long timeoutOverride, boolean noChangeLights, int eventType, boolean force) { ...... if (!mAutoBrightnessButtonKeyboard) { // Turn on button (and keyboard) backlights on any event, so that they // don't suddenly disappear when the lock screen is unlocked (OTHER_EVENT), // and so capacitive buttons can be found on devices where they lack // identifying surface features. mUserState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT); } else { // don't clear button/keyboard backlights when the screen is touched. mUserState |= SCREEN_BRIGHT; } mWakeLockState = mLocks.reactivateScreenLocksLocked(); setPowerState(mUserState | mWakeLockState, noChangeLights, WindowManagerPolicy.OFF_BECAUSE_OF_USER); setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT); ......}
setPowerState方法会根据输入的状态调用setScreenStateLocked方法来设置系统状态
private void setPowerState(int newState, boolean noChangeLights, int reason){ ...... boolean oldScreenOn = (mPowerState & SCREEN_ON_BIT) != 0; // 记录系统当前屏幕状态 boolean newScreenOn = (newState & SCREEN_ON_BIT) != 0; // 记录新的屏幕状态 final boolean stateChanged = mPowerState != newState; // 记录状态是否改变 if (oldScreenOn != newScreenOn) { // 新的屏幕状态和当前屏幕状态不一致时if (newScreenOn) { // 新状态是开启屏幕 boolean reallyTurnScreenOn = true; if (mPreventScreenOn) { reallyTurnScreenOn = false; // 如果屏幕开启被阻止,则设置reallyTurnScreenOn为false } if (reallyTurnScreenOn) { err = setScreenStateLocked(true); // 使用setScreenStateLocked唤醒系统...... } else { setScreenStateLocked(false); // 使系统睡眠 // But continue as if we really did turn the screen on... err = 0; } if (err == 0) { sendNotificationLocked(true, -1); if (stateChanged) { updateLightsLocked(newState, 0); } mPowerState |= SCREEN_ON_BIT;} else { // 如果新状态是关闭屏幕,则使用screenOffFinishedAnimatingLocked方法使系统睡眠 if (stateChanged) { updateLightsLocked(newState, 0); } ...... if (!mScreenBrightness.animating) { err = screenOffFinishedAnimatingLocked(reason); // 该方法也是调用setScreenStateLocked方法睡眠系统 } else { err = 0; mLastTouchDown = 0; }} } else if (stateChanged) { // Screen on/off didn't change, but lights may have. updateLightsLocked(newState, 0); } ......}
在setScreenStateLocked方法中会使用Power.setScreenState方法调用jni层中的函数,最终会传递至内核层,在内核层中执行相应的睡眠系统或唤醒系统
private int setScreenStateLocked(boolean on) { ...... int err = Power.setScreenState(on); ......}
而setScreenStateLocked方法中,函数updateLightsLocked用来更新lights,后面将进行分析。
以上一小段介绍了userActivity唤醒系统的简单流程,而在PowerManagerService的初始化函数中,不仅会通过userActivity中的setPowerState来唤醒系统,同时也会使用userActivity中的setTimeoutLocked来开启一个定时器,用于切换屏幕的状态
private void setTimeoutLocked(long now, final long originalTimeoutOverride, int nextState) { long timeoutOverride = originalTimeoutOverride; ...... long when = 0; if (timeoutOverride <= 0) { // 时间设置<=0时,此时系统会使用缺省的定时时间开启计时器 switch (nextState) { case SCREEN_BRIGHT: // 新状态是BRIGHT,则when加上mKeylightDelay when = now + mKeylightDelay; break; case SCREEN_DIM: // 新状态是DIM,则when加上mDimDelay if (mDimDelay >= 0) { when = now + mDimDelay; break; } case SCREEN_OFF: // 新状态是OFF,则when加上mScreenOffDelay synchronized (mLocks) { when = now + mScreenOffDelay; } break; default: when = now; break; } } else { // 如果定时时间设定,即为timeoutOverride override: { if (timeoutOverride <= mScreenOffDelay) { when = now + timeoutOverride; nextState = SCREEN_OFF; break override; } timeoutOverride -= mScreenOffDelay; if (mDimDelay >= 0) { if (timeoutOverride <= mDimDelay) { when = now + timeoutOverride; nextState = SCREEN_DIM; break override; } timeoutOverride -= mDimDelay; } when = now + timeoutOverride; nextState = SCREEN_BRIGHT; } } mHandler.removeCallbacks(mTimeoutTask); // 移除旧的mTimeoutTask时间 mTimeoutTask.nextState = nextState; // 赋值状态 mTimeoutTask.remainingTimeoutOverride = timeoutOverride > 0 ? (originalTimeoutOverride - timeoutOverride) : -1; mHandler.postAtTime(mTimeoutTask, when); // 重新启动定时器,在when时间后执行mTimeoutTask任务 mNextTimeout = when;}
在PowerManagerService的初始化函数中最终会通过setTimeoutLocked来启动定时器,当定时器到时间后就会执行mTimeoutTask任务,下面可以看看mTimeoutTask任务的定义:
TimeoutTask mTimeoutTask = new TimeoutTask();
mTimeoutTask定义为类型是TimeoutTask的对象,TimeoutTask如下所示:
private class TimeoutTask implements Runnable{ int nextState; // access should be synchronized on mLocks long remainingTimeoutOverride; public void run() { synchronized (mLocks) { if (nextState == -1) { return; } mUserState = this.nextState; setPowerState(this.nextState | mWakeLockState); // 调用setPowerState来睡眠或唤醒系统 long now = SystemClock.uptimeMillis(); switch (this.nextState) // 更新定时器 { case SCREEN_BRIGHT: // 如果状态是BRIGHT,则更新定时器,是定时器在到期后执行SCREEN_DIM操作 if (mDimDelay >= 0) { setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM); break; } case SCREEN_DIM: // 如果状态是DIM,则更新定时器,是定时器在到期后执行SCREEN_OFF操作 setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF); break; } } }}
于是在PowerManagerService中通过定时器来切换屏幕的状态,同时也会调用setPowerState方法来睡眠或唤醒系统,而具体的屏幕亮度是如何实现的呢?
在setPowerState中使用了updateLightsLocked来更新屏幕的状态。
private void updateLightsLocked(int newState, int forceState) { final int oldState = mPowerState; // 将当前系统状态赋值于oldState ...... final int realDifference = (newState ^ oldState); // 判断新状态和旧状态的不同之处 final int difference = realDifference | forceState; if (difference == 0) { return; } int offMask = 0; int dimMask = 0; int onMask = 0; int preferredBrightness = getPreferredBrightness(); // 获取缺省的亮度值 if ((difference & KEYBOARD_BRIGHT_BIT) != 0) { // 如果是键盘亮度不同,则更新offMask和onMask if ((newState & KEYBOARD_BRIGHT_BIT) == 0) { offMask |= KEYBOARD_BRIGHT_BIT; } else { onMask |= KEYBOARD_BRIGHT_BIT; } } if ((difference & BUTTON_BRIGHT_BIT) != 0) { // 如果是按键亮度不同,则更新offMask和onMask if ((newState & BUTTON_BRIGHT_BIT) == 0) { offMask |= BUTTON_BRIGHT_BIT; } else { onMask |= BUTTON_BRIGHT_BIT; } } if ((difference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) { // 如果是屏幕开启或点亮状态位不同int nominalCurrentValue = -1; // 当前亮度if ((realDifference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) { switch (oldState & (SCREEN_BRIGHT_BIT|SCREEN_ON_BIT)) { // 判断旧的状态 case SCREEN_BRIGHT_BIT | SCREEN_ON_BIT: // 如果旧状态是开启并点亮屏幕 nominalCurrentValue = preferredBrightness; // 则将preferredBrightness赋给nominalCurrentValue break; case SCREEN_ON_BIT: // 如果旧状态仅仅是开启屏幕 nominalCurrentValue = mScreenDim; break; case 0: // 如果旧状态是关闭屏幕 nominalCurrentValue = Power.BRIGHTNESS_OFF; break; case SCREEN_BRIGHT_BIT: default: // not possible nominalCurrentValue = (int)mScreenBrightness.curValue; break; } }int brightness = preferredBrightness; int steps = ANIM_STEPS; if ((newState & SCREEN_BRIGHT_BIT) == 0) { // 如果新状态不是点亮屏幕,即变暗屏幕或关闭屏幕 // 此时会使用动画操作渐渐的使屏幕变暗或关闭,这里计算step // dim or turn off backlight, depending on if the screen is on // the scale is because the brightness ramp isn't linear and this biases // it so the later parts take longer. final float scale = 1.5f; float ratio = (((float)mScreenDim)/preferredBrightness); if (ratio > 1.0f) ratio = 1.0f; if ((newState & SCREEN_ON_BIT) == 0) { if ((oldState & SCREEN_BRIGHT_BIT) != 0) { // was bright steps = ANIM_STEPS; } else { // was dim steps = (int)(ANIM_STEPS*ratio*scale); } brightness = Power.BRIGHTNESS_OFF; // 如果新状态是关闭屏幕,则赋值brightness为Power.BRIGHTNESS_OFF } else { // brightness为新状态的亮度 if ((oldState & SCREEN_ON_BIT) != 0) { // was bright steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale); } else { // was dim steps = (int)(ANIM_STEPS*ratio); } if (mStayOnConditions != 0 && mBatteryService.isPowered(mStayOnConditions)) { // If the "stay on while plugged in" option is // turned on, then the screen will often not // automatically turn off while plugged in. To // still have a sense of when it is inactive, we // will then count going dim as turning off. mScreenOffTime = SystemClock.elapsedRealtime(); mAlwaysOnAndDimmed = true; } brightness = mScreenDim; // 如果新状态是变暗屏幕,则赋值brightness为mScreenDim } }if (!mSkippedScreenOn) { mScreenBrightness.setTargetLocked(brightness, steps, INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue); // 设置屏幕的亮度 } } // 以下根据bit位的不同调用setLightBrightness来设置亮度,包括屏幕、键盘和按键 if (offMask != 0) { setLightBrightness(offMask, Power.BRIGHTNESS_OFF); } if (dimMask != 0) { int brightness = mScreenBrightnessDim; if ((newState & BATTERY_LOW_BIT) != 0 && brightness > Power.BRIGHTNESS_LOW_BATTERY) { brightness = Power.BRIGHTNESS_LOW_BATTERY; } setLightBrightness(dimMask, brightness); } if (onMask != 0) { int brightness = getPreferredBrightness(); if ((newState & BATTERY_LOW_BIT) != 0 && brightness > Power.BRIGHTNESS_LOW_BATTERY) { brightness = Power.BRIGHTNESS_LOW_BATTERY; } setLightBrightness(onMask, brightness); }}
在updateLightsLocked方法中使用了mScreenBrightness.setTargetLocked方法来设置屏幕的亮度,而mScreenBrightness是类型为BrightnessState的对象,以下是其定义:
private final BrightnessState mScreenBrightness = new BrightnessState(SCREEN_BRIGHT_BIT);
此类是通过逐渐减少step值的亮度来是的屏幕到达最终的亮度,下面分析setTargetLocked方法
void setTargetLocked(int target, int stepsToTarget, int initialValue, int nominalCurrentValue) { if (!initialized) { // 如果正在进行且目标亮度相同则返回 initialized = true; curValue = (float)initialValue; } else if (targetValue == target) { return; } targetValue = target; delta = (targetValue - (nominalCurrentValue >= 0 ? nominalCurrentValue : curValue)) / stepsToTarget; // 计算delta值 animating = true; mScreenOffHandler.removeCallbacks(this); // 移除旧的事件 mScreenOffHandler.post(this); // 开始新的事件}
其中mScreenOffHandler移除或开始的事件即为mScreenBrightness本身,此时会执行BrightnessState类中的run方法。
public void run() { synchronized (mLocks) { final boolean turningOn = animating && (int)curValue == Power.BRIGHTNESS_OFF; // 是否是打开屏幕 final boolean turningOff = animating && targetValue == Power.BRIGHTNESS_OFF; // 是否是关闭屏幕 // Check for the electron beam for fully on/off transitions. // Otherwise, allow it to fade the brightness as normal. final boolean electrifying = ((mElectronBeamAnimationOff && turningOff) || (mElectronBeamAnimationOn && turningOn)); if (!electrifying && (mAnimateScreenLights || !turningOff)) { long now = SystemClock.uptimeMillis(); boolean more = mScreenBrightness.stepLocked(); // 使用stepLocked逐渐调至目标亮度 if (more) { mScreenOffHandler.postAtTime(this, now+(1000/60)); } } else { // It's pretty scary to hold mLocks for this long, and we should // redesign this, but it works for now. if (turningOff) { if (electrifying) { nativeStartSurfaceFlingerOffAnimation( mScreenOffReason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR ? 0 : mAnimationSetting); } mScreenBrightness.jumpToTargetLocked(); // 直接变成目标亮度 } else if (turningOn) { if (electrifying) { int delay=mContext.getResources().getInteger(com.android.internal.R.integer.config_screenOnAnimation); if(delay>0) { startElectronBeamDelayed(new Runnable() { @Override public void run() { startElectronBeamOnAnimation(); synchronized(mElectronBeamOnHandler) { mElectronBeamOnHandler.notifyAll(); } } },delay); } else { startElectronBeamOnAnimation(); } } else { mScreenBrightness.jumpToTargetLocked(); // 直接变成目标亮度 } } } }}
在run方法中,不论是逐渐调制目标亮度stepLocked,或者直接变成目标亮度jumpToTargetLocked,都会调用到setLightBrightness方法用于改变亮度,下面来分析setLightBrightness方法。
private void setLightBrightness(int mask, int value) { int brightnessMode = (mAutoBrightessEnabled ? LightsService.BRIGHTNESS_MODE_SENSOR : LightsService.BRIGHTNESS_MODE_USER); if ((mask & SCREEN_BRIGHT_BIT) != 0) { // 如果是屏幕亮度,则使用mLcdLight.setBrightness mLcdLight.setBrightness(value, brightnessMode); mLastLcdValue = value; } if ((mask & BUTTON_BRIGHT_BIT) != 0) { // 如果是按键亮度,则使用mButtonLight.setBrightness // Use sensor-determined brightness values when the button (or keyboard) // light is on, since users may want to specify a custom brightness setting // that disables the button (or keyboard) backlight entirely in low-ambient // light situations. mButtonLight.setBrightness(mLightSensorButtonBrightness >= 0 && value > 0 ? mLightSensorButtonBrightness : value); } if ((mask & KEYBOARD_BRIGHT_BIT) != 0) { // 如果是键盘亮度,则使用mKeyboardLight.setBrightness mKeyboardLight.setBrightness(mLightSensorKeyboardBrightness >= 0 && value > 0 ? mLightSensorKeyboardBrightness : value); }}
在setLightBrightness方法中会根据mask的值来相应的改变屏幕、键盘和按键的亮度,屏幕的亮度主要使用mLcdLight.setBrightness方法,其中mLcdLight是类型为LightsService.Light的对象,在PowerManagerService的初始化函数中进行了赋值:
mLcdLight = lights.getLight(LightsService.LIGHT_ID_BACKLIGHT);
LightsService.Light类型定义在framework/base/services/java/com/android/server/LightsService.java中,下面来分析其setBrightness方法:
public void setBrightness(int brightness, int brightnessMode) { synchronized (this) { int color = brightness & 0x000000ff; color = 0xff000000 | (color << 16) | (color << 8) | color; setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode); }}
在setBrightness方法中,主要是将亮度值扩展成32bit的color值来提供给setLightLocked处理。
private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) { if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) { mColor = color; mMode = mode; mOnMS = onMS; mOffMS = offMS; setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode); }}
setLightLocked会调用setLight_native来将亮度值传递至jni层,setLight_native在文件framework/base/services/jni/com_android_server_LightsService.cpp中实现。
static void setLight_native(JNIEnv *env, jobject clazz, int ptr, int light, int colorARGB, int flashMode, int onMS, int offMS, int brightnessMode){ Devices* devices = (Devices*)ptr; light_state_t state; if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) { return ; } memset(&state, 0, sizeof(light_state_t)); state.color = colorARGB; state.flashMode = flashMode; state.flashOnMS = onMS; state.flashOffMS = offMS; state.brightnessMode = brightnessMode; devices->lights[light]->set_light(devices->lights[light], &state);}
而setLight_native方法使用devices->lights[light]->set_light来设定亮度值。在com_android_server_LightsService.cpp的初始化函数中,会对devices进行初始化,如下所示:
static jint 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_KEYBOARD] = get_device(module, LIGHT_ID_KEYBOARD); devices->lights[LIGHT_INDEX_BUTTONS] = get_device(module, LIGHT_ID_BUTTONS); devices->lights[LIGHT_INDEX_BATTERY] = get_device(module, LIGHT_ID_BATTERY); devices->lights[LIGHT_INDEX_NOTIFICATIONS] = get_device(module, LIGHT_ID_NOTIFICATIONS); devices->lights[LIGHT_INDEX_ATTENTION] = get_device(module, LIGHT_ID_ATTENTION); devices->lights[LIGHT_INDEX_BLUETOOTH] = get_device(module, LIGHT_ID_BLUETOOTH); devices->lights[LIGHT_INDEX_WIFI] = get_device(module, LIGHT_ID_WIFI); } else { memset(devices, 0, sizeof(Devices)); } return (jint)devices;}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; }}
该方法使用hw_get_module来获取动态库模块,其中LIGHTS_HARDWARE_MODULE_ID在/hardware/libhardware/include/hardware/lights.h中声明:
#define LIGHTS_HARDWARE_MODULE_ID "lights"
所以在硬件抽象层,如果要编写lights的模块供上层使用,需要将自身命名为lights的模块,下面以hardware/msm7k/liblights/lights.c为例,在其中就声明了“lights”模块
const struct hw_module_t HAL_MODULE_INFO_SYM = { .tag = HARDWARE_MODULE_TAG, .version_major = 1, .version_minor = 0, .id = LIGHTS_HARDWARE_MODULE_ID, .name = "QCT MSM7K lights Module", .author = "Google, Inc.", .methods = &lights_module_methods,};
然后在com_android_server_LightsService.cpp的初始化函数中使用get_device来打开相应的设备,其调用了module->methods->open的方法,而在hardware/msm7k/liblights/lights.c也定义了回调函数:
static struct hw_module_methods_t lights_module_methods = { .open = open_lights,};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;}
在open_lights方法中也映射了set_light方法,于是在com_android_server_LightsService.cpp的devices->lights[light]->set_light最终会调用hardware/msm7k/liblights/lights.c中set_light所映射的方法,set_light_backlight就是设置屏幕亮度的方法。
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;}
其中char const*const LCD_FILE = "/sys/class/leds/lcd-backlight/brightness";
该方法会使用write_int函数将亮度写入至路径为LCD_FILE的文件中,即传递至内核层。
用户空间
//////////////////////////////////////////////////////////////////////////////////////////
内核空间
在内核空间中的kernel/drivers/leds/led-class.c模块初始化函数中,使用了class_create创建了“leds”类目录,同时在这个模块中给出了led_classdev_register用于注册led设备,除此之外,还给出了这个class下的相关属性:
// 模块初始化函数static int __init leds_init(void){ leds_class = class_create(THIS_MODULE, "leds"); if (IS_ERR(leds_class))return PTR_ERR(leds_class); leds_class->suspend = led_suspend; leds_class->resume = led_resume; leds_class->dev_attrs = led_class_attrs; return 0;}// led设备注册函数int led_classdev_register(struct device *parent, struct led_classdev *led_cdev){ led_cdev->dev = device_create(leds_class, parent, 0, led_cdev, "%s", led_cdev->name); if (IS_ERR(led_cdev->dev))return PTR_ERR(led_cdev->dev);#ifdef CONFIG_LEDS_TRIGGERS init_rwsem(&led_cdev->trigger_lock);#endif /* add to the list of leds */ down_write(&leds_list_lock); list_add_tail(&led_cdev->node, &leds_list); up_write(&leds_list_lock); if (!led_cdev->max_brightness)led_cdev->max_brightness = LED_FULL; led_update_brightness(led_cdev); init_timer(&led_cdev->blink_timer); led_cdev->blink_timer.function = led_timer_function; led_cdev->blink_timer.data = (unsigned long)led_cdev;#ifdef CONFIG_LEDS_TRIGGERS led_trigger_set_default(led_cdev);#endif printk(KERN_DEBUG "Registered led device: %s\n",led_cdev->name); return 0;}EXPORT_SYMBOL_GPL(led_classdev_register);// led属性static struct device_attribute led_class_attrs[] = {__ATTR(brightness, 0644, led_brightness_show, led_brightness_store), __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),#ifdef CONFIG_LEDS_TRIGGERS__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),#endif__ATTR_NULL,};
//////////////////////////////////////////////////////////////////////////////
leds-lm3530.c模块为例子(有regulator)
在kernel/drivers/leds/leds-lm3530.c模块中,声明了名称为“lcd-backlight”的led设备,
并使用了led_classdev_register将其注册进入led class中。
drvdata->mode = pdata->mode;drvdata->client = client;drvdata->pdata = pdata;drvdata->brightness = LED_OFF;drvdata->enable = false;drvdata->led_dev.name = LM3530_LED_DEV; // #define LM3530_LED_DEV "lcd-backlight"drvdata->led_dev.brightness_set = lm3530_brightness_set;i2c_set_clientdata(client, drvdata);drvdata->regulator = regulator_get(&client->dev, "vin");err = led_classdev_register(&client->dev, &drvdata->led_dev);
于是就会产生/sys/class/leds/lcd-backlight/brightness的目录,所以上层使用write_int(LCD_FILE, brightness)向内核写入亮度值会调用kernel/drivers/leds/led-class.c模块中的led_brightness_store方法。
static ssize_t led_brightness_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size){ struct led_classdev *led_cdev = dev_get_drvdata(dev); // 此处的led_cdev就是kernel/drivers/staging/msm/msm_fb.c中注册的backlight_led ssize_t ret = -EINVAL; char *after; unsigned long state = simple_strtoul(buf, &after, 10); size_t count = after - buf; if (isspace(*after))count++; if (count == size) {ret = count;if (state == LED_OFF) led_trigger_remove(led_cdev);led_set_brightness(led_cdev, state); // 设置屏幕亮度 } return ret;}
该方法调用led_set_brightness设置屏幕亮度,其在kernel/drivers/leds/leds.h中
static inline void led_set_brightness(struct led_classdev *led_cdev,enum led_brightness value){ if (value > led_cdev->max_brightness)value = led_cdev->max_brightness; led_cdev->brightness = value; if (!(led_cdev->flags & LED_SUSPENDED)) led_cdev->brightness_set(led_cdev, value); // 此处的led_cdev就是kernel/drivers/staging/msm/msm_fb.c中注册的backlight_led}
于是就调用了kernel/drivers/leds/leds-lm3530.c模块中的回调函数brightness_set,即lm3530_brightness_set函数
static void lm3530_brightness_set(struct led_classdev *led_cdev, enum led_brightness brt_val){ int err; struct lm3530_data *drvdata = container_of(led_cdev, struct lm3530_data, led_dev); switch (drvdata->mode) { case LM3530_BL_MODE_MANUAL: if (!drvdata->enable) { err = lm3530_init_registers(drvdata); // 如果没有使能则会先使能,在这个方法中会调用regulator_enable来开启regulator输出 if (err) { dev_err(&drvdata->client->dev, "Register Init failed: %d\n", err); break; } } /* set the brightness in brightness control register*/ err = i2c_smbus_write_byte_data(drvdata->client, LM3530_BRT_CTRL_REG, brt_val / 2); // 设置亮度 if (err) dev_err(&drvdata->client->dev, "Unable to set brightness: %d\n", err); else drvdata->brightness = brt_val / 2; if (brt_val == 0) { err = regulator_disable(drvdata->regulator); // 如果设置亮光为0,则会调用regulator_disable来关闭regulator输出 if (err) dev_err(&drvdata->client->dev, "Disable regulator failed\n"); drvdata->enable = false;}break; case LM3530_BL_MODE_ALS:break; case LM3530_BL_MODE_PWM:break; default:break; }}
lm3530_brightness_set方法在打开屏幕时会使用regulator_enable开启电源管理芯片的regulator输出,在关闭屏幕时会调用regulator_disable关闭电源管理芯片的regulator输出,在调整屏幕亮度时使用i2c_smbus_write_byte_data向寄存器中写入数值来调整亮度。
///////////////////////////////////////////////////////////
msm_fb.c模块为例子(无regulator)
在kernel/drivers/staging/msm/msm_fb.c模块中,声明了名称为“lcd-backlight”的led设备,并使用了led_classdev_register将其注册进入led class中。
static struct led_classdev backlight_led = {.name = "lcd-backlight",.brightness = MAX_BACKLIGHT_BRIGHTNESS,.brightness_set= msm_fb_set_bl_brightness,};于是就会产生/sys/class/leds/lcd-backlight/brightness的目录,所以上层使用write_int(LCD_FILE, brightness)向内核写入亮度值会调用kernel/drivers/leds/led-class.c模块中的led_brightness_store方法。
static ssize_t led_brightness_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size){ struct led_classdev *led_cdev = dev_get_drvdata(dev); // 此处的led_cdev就是kernel/drivers/staging/msm/msm_fb.c中注册的backlight_led ssize_t ret = -EINVAL; char *after; unsigned long state = simple_strtoul(buf, &after, 10); size_t count = after - buf; if (isspace(*after))count++; if (count == size) {ret = count;if (state == LED_OFF) led_trigger_remove(led_cdev);led_set_brightness(led_cdev, state); // 设置屏幕亮度 } return ret;}
该方法调用led_set_brightness设置屏幕亮度,其在kernel/drivers/leds/leds.h中
static inline void led_set_brightness(struct led_classdev *led_cdev,enum led_brightness value){ if (value > led_cdev->max_brightness)value = led_cdev->max_brightness; led_cdev->brightness = value; if (!(led_cdev->flags & LED_SUSPENDED)) led_cdev->brightness_set(led_cdev, value); // 此处的led_cdev就是kernel/drivers/staging/msm/msm_fb.c中注册的backlight_led}
于是就调用了kernel/drivers/staging/msm/msm_fb.c模块中的回调函数brightness_set,即msm_fb_set_bl_brightness函数
static void msm_fb_set_bl_brightness(struct led_classdev *led_cdev,enum led_brightness value){ struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent); int bl_lvl; if (value > MAX_BACKLIGHT_BRIGHTNESS)value = MAX_BACKLIGHT_BRIGHTNESS; /* This maps android backlight level 0 to 255 into driver backlight level 0 to bl_max with rounding */ bl_lvl = (2 * value * mfd->panel_info.bl_max + MAX_BACKLIGHT_BRIGHTNESS) /(2 * MAX_BACKLIGHT_BRIGHTNESS); if (!bl_lvl && value) bl_lvl = 1; msm_fb_set_backlight(mfd, bl_lvl, 1);}
在msm_fb_set_bl_brightness函数中将亮度从0-255映射成0-bl_max,然后使用msm_fb_set_backlight设置屏幕亮度
void msm_fb_set_backlight(struct msm_fb_data_type *mfd, __u32 bkl_lvl, u32 save){ struct msm_fb_panel_data *pdata; pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; if ((pdata) && (pdata->set_backlight)) {down(&mfd->sem);if ((bkl_lvl != mfd->bl_level) || (!save)) { u32 old_lvl; old_lvl = mfd->bl_level; mfd->bl_level = bkl_lvl; pdata->set_backlight(mfd); if (!save)mfd->bl_level = old_lvl; }up(&mfd->sem); }}
在msm_fb_set_backlight方法中主要调用了各个具体设备的set_backlight回调函数,实现了lcd屏幕的亮度调节。
crcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcr
Hufikyu的学习空间,欢迎大家提出问题,共同进步。
crcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcr
- Android系统中的屏幕状态切换以及亮度设置
- Android系统中的屏幕状态切换以及亮度设置
- android 设置系统屏幕亮度
- android 设置系统屏幕亮度
- android 设置系统屏幕亮度
- android 设置系统屏幕亮度
- android 设置系统屏幕亮度
- android 设置系统屏幕亮度
- android 设置系统屏幕亮度
- android 设置系统屏幕亮度
- android 设置系统屏幕亮度
- android 设置系统屏幕亮度
- android 设置系统屏幕亮度
- android 设置系统屏幕亮度
- android 设置屏幕亮度
- android设置屏幕亮度
- Android 设置屏幕亮度
- Android 设置屏幕亮度
- 外国人如何看“剩女”
- ubuntu中zend studio运行php文件时提示下载解决
- Date、String和Timestamp类型转换
- 两张表数据合并显示_还要分页
- HTML颜色参考
- Android系统中的屏幕状态切换以及亮度设置
- 第十一周 项目一:以点类为基类,派生出直线类(从基类中继承的点的信息表示直线的中点)
- C指针定义与内存分配
- 自定义View时, 使用Canvas、Bitmap时易犯的内存泄露问题
- 移植嵌入式 Boa web server到TI 达芬奇平台
- sql 分组查询实例
- InvalidateRect()与Invalidate()的用法
- 11周项目一
- 基于ACE Proactor框架下高并发、大容量吞吐程序设计既最近的一个产品开发总结