1. SystemUI
当我们拉动滑块调节背光时,最终通过BrightnessController@setBrighness
设置背光,BrightnessController
里面的逻辑先忽略。
//frameworks/base/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@Override
public void onChanged(ToggleSlider toggleSlider, boolean tracking, boolean automatic,
int value, boolean stopTracking) {
updateIcon(mAutomatic);
if (mExternalChange) return;
if (!mAutomatic) {
final int val = value + mMinimumBacklight;
//设置背光
setBrightness(val);
if (!tracking) {
AsyncTask.execute(new Runnable() {
public void run() {
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, val,
UserHandle.USER_CURRENT);
}
});
}
} else {
...
}
for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
cb.onBrightnessLevelChanged();
}
}
setBrightness
最终通过PowerManagerService
来设置背光。
private void setBrightness(int brightness) {
try {
mPower.setTemporaryScreenBrightnessSettingOverride(brightness);
} catch (RemoteException ex) {
}
}
2. PowerManagerService
通过SystemUI调节亮度进入PMS后,会讲亮度值记录在PMS@mTemporaryScreenBrightnessSettingOverride
成员变量中,然后将mDirty
或上DIRTY_SETTINGS
,最后调用PMS@updatePowerStateLocked
执行亮度更新操作,从函数命名来看,其所做的工作不仅仅只有调整亮度,但是我们只关心亮度调节流程。
private void updatePowerStateLocked() {
try {
// Phase 0: Basic state updates.
updateIsPoweredLocked(mDirty);
updateStayOnLocked(mDirty);
updateScreenBrightnessBoostLocked(mDirty);
// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
final long now = SystemClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
}
// Phase 2: Update display power state.
boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
// Phase 3: Update dream state (depends on display ready signal).
// 屏保相关
updateDreamLocked(dirtyPhase2, displayBecameReady);
// Phase 4: Send notifications, if needed.
finishWakefulnessChangeIfNeededLocked();
// Phase 5: Update suspend blocker.
// Because we might release the last suspend blocker here, we need to make sure
// we finished everything else first!
updateSuspendBlockerLocked();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
这段代码中,Phase 2
是亮度调节的重点。首先是updateDisplayPowerStateLocked
,其最终会调用到DMS@requestPowerState
请求更新显示设备电源状态,该函数的执行是异步的,返回true
表示状态更新完成,返回false
表示状态未更新。
这里,我们只要知道PMS讲显示设备电源信息打包成
DisplayPowerRequest
对象(记录在mDisplayPowerRequest
中),然后将其发送给DMS
处理。同时,PMS@mDisplayReady
用于记录DMS@requestPowerState
的返回值。
设置一次亮度时,setTemporaryScreenBrightnessSettingOverride
只会被调用一次,但是updatePowerStateLocked@updateDisplayPowerStateLocked
是异步操作,我们第一次调用updateDisplayPowerStateLocked
的返回基本都是false
,那么PMS
是如何获取异步返回的结果的?如果上次异步任务还没解决,又设置了一次亮度,PMS
如何处理?
3. DisplayManagerService
DisplayManagerInternal
实际上也是调用的DisplayPowerController@requestPowerState
实现的亮度调整。
//
private final class LocalService extends DisplayManagerInternal {
@Override
public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager) {
synchronized (mSyncRoot) {
//这个,我们后面会看到的,比较重要的东西
DisplayBlanker blanker = new DisplayBlanker() {
@Override
public void requestDisplayState(int state, int brightness) {
// The order of operations is important for legacy reasons.
if (state == Display.STATE_OFF) {
requestGlobalDisplayStateInternal(state, brightness);
}
callbacks.onDisplayStateChange(state);
if (state != Display.STATE_OFF) {
requestGlobalDisplayStateInternal(state, brightness);
}
}
};
mDisplayPowerController = new DisplayPowerController(
mContext, callbacks, handler, sensorManager, blanker);
}
}
@Override
public boolean requestPowerState(DisplayPowerRequest request,
boolean waitForNegativeProximity) {
return mDisplayPowerController.requestPowerState(request,
waitForNegativeProximity);
}
}
通过requestPowerState
函数来请求 DMS
调整亮度,该操作是异步的,最终在PMS@mHandlerThread
中线程中执行更新操作。
/**
* Requests a new power state.
* The controller makes a copy of the provided object and then
* begins adjusting the power state to match what was requested.
*
* @param request The requested power state.
* @param waitForNegativeProximity If true, issues a request to wait for
* negative proximity before turning the screen back on, assuming the screen
* was turned off by the proximity sensor.
* @return True if display is ready, false if there are important changes that must
* be made asynchronously (such as turning the screen on), in which case the caller
* should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
* then try the request again later until the state converges.
*/
public boolean requestPowerState(DisplayPowerRequest request,
boolean waitForNegativeProximity) {
//保护所有带Locked的变量,这里进入同步区域后说明前一个pengindEvent已经被处理,或者还没来得及被处理。````````````
synchronized (mLock) {
boolean changed = false;
if (waitForNegativeProximity
&& !mPendingWaitForNegativeProximityLocked) {
mPendingWaitForNegativeProximityLocked = true;
changed = true;
}
if (mPendingRequestLocked == null) {
mPendingRequestLocked = new DisplayPowerRequest(request);
changed = true;
} else if (!mPendingRequestLocked.equals(request)) {
mPendingRequestLocked.copyFrom(request);
changed = true;
}
if (changed) {
mDisplayReadyLocked = false;
}
if (changed && !mPendingRequestChangedLocked) {
mPendingRequestChangedLocked = true;
sendUpdatePowerStateLocked();
}
// mPendingRequestLocked 被更新, mDisplayReadyLocked 就赋值为false。更新操作异步完成后,重新赋值为true。所以,PMS需要多次执行该函数确认状态已经被更新
return mDisplayReadyLocked;
}
}
通过sendUpdatePowerStateLocked
来实现异步更新亮度:
private void sendUpdatePowerStateLocked() {
if (!mPendingUpdatePowerStateLocked) {
mPendingUpdatePowerStateLocked = true; //如果HandlerThread还没有处理该message,重复发送时,不做处理。
Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);//mHandler 对应的是 , 处理该消息的线程是 PMS中创建的ServiceThread
}
}
这里的mHandler
对应的是DisplayControllerHandler
,处理该Handler
的线程是 PMS中创建的ServiceThread
。
MSG_UPDATE_POWER_STATE
的处理过程,代码比较多,直接贴结论,实际实现在DisplayPowerController@updatePowerState
中,该函数通过调用animateScreenBrightness
来实现背光的平滑调整。
//frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private void animateScreenBrightness(int target, int rate) {
if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", target);
try {
mBatteryStats.noteScreenBrightness(target);
} catch (RemoteException ex) {
// same process
}
}
}
mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
//frameworks/base/services/core/java/com/android/server/display/DisplayPowerState.java
public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS =
new IntProperty<DisplayPowerState>("screenBrightness") {
@Override
public void setValue(DisplayPowerState object, int value) {
object.setScreenBrightness(value);
}
@Override
public Integer get(DisplayPowerState object) {
return object.getScreenBrightness();
}
};
从上面的代码可以看到,这里通过调用DisplayPowerState@setScreenBrightness
来实现背光调整,具体的平滑效果,由RampAnimator
实现,不care。
再执行完亮度处理操作后,才会更新
DisplayPowerController@mDisplayReadyLocked
,这个时候PMS再次执行requestPowerState
才会返回true。
DisplayPowerState
//frameworks/base/services/core/java/com/android/server/display/DisplayPowerState.java
public void setScreenBrightness(int brightness) {
if (mScreenBrightness != brightness) {
mScreenBrightness = brightness;
if (mScreenState != Display.STATE_OFF) {
mScreenReady = false;
scheduleScreenUpdate();
}
}
}
private final Runnable mScreenUpdateRunnable = new Runnable() {
@Override
public void run() {
mScreenUpdatePending = false;
int brightness = mScreenState != Display.STATE_OFF
&& mColorFadeLevel > 0f ? mScreenBrightness : 0;
if (mPhotonicModulator.setState(mScreenState, brightness)) {
if (DEBUG) {
Slog.d(TAG, "Screen ready");
}
mScreenReady = true;
invokeCleanListenerIfNeeded();
} else {
if (DEBUG) {
Slog.d(TAG, "Screen not ready");
}
}
}
};
setScreenBrightness
最终会执行到mScreenUpdateRunnable
中。mPhotonicModulator
,是一个单独的线程,其在DisplayPowerState
的构造函数中启动,由于背光更新可能是耗时操作,所以启动了这么一个单独的线程来执行,避免阻塞ServiceThread
。
//frameworks/base/services/core/java/com/android/server/display/DisplayPowerState.java@PhotonicModulator
public boolean setState(int state, int backlight) {
synchronized (mLock) {
boolean stateChanged = state != mPendingState;
boolean backlightChanged = backlight != mPendingBacklight;
if (stateChanged || backlightChanged) {
mPendingState = state;
mPendingBacklight = backlight;
boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress;
mStateChangeInProgress = stateChanged || mStateChangeInProgress;
mBacklightChangeInProgress = backlightChanged || mBacklightChangeInProgress;
if (!changeInProgress) {
mLock.notifyAll();
}
}
return !mStateChangeInProgress;
}
}
通过setState
将要更新的背光值保存到mPendingBacklight
中,再来看一下线程的主循环。
@Override
public void run() {
for (;;) {
// Get pending change.
final int state;
final boolean stateChanged;
final int backlight;
final boolean backlightChanged;
synchronized (mLock) {
state = mPendingState;
stateChanged = (state != mActualState);
backlight = mPendingBacklight;
backlightChanged = (backlight != mActualBacklight);
if (!stateChanged) {
// State changed applied, notify outer class.
postScreenUpdateThreadSafe();
mStateChangeInProgress = false;
}
if (!backlightChanged) {
mBacklightChangeInProgress = false;
}
if (!stateChanged && !backlightChanged) {
try {
mLock.wait();
} catch (InterruptedException ex) { }
continue;
}
mActualState = state;
mActualBacklight = backlight;
}
mBlanker.requestDisplayState(state, backlight);
}
}
Oh.通过mBlanker.requestDisplayState
设置背光,好复杂啊,设置个背光,一层套一层。。。。真恶心,,,,
DIsplayBlanker
通过该接口来更新实际的背光状态。
/**
* Interface used to update the actual display state.
*/
public interface DisplayBlanker {
void requestDisplayState(int state, int brightness);
}
只有一个实例,就是在DMS@systemReady
时创建。
//frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
DisplayBlanker blanker = new DisplayBlanker() {
@Override
public void requestDisplayState(int state, int brightness) {
// The order of operations is important for legacy reasons.
if (state == Display.STATE_OFF) {
requestGlobalDisplayStateInternal(state, brightness);
}
callbacks.onDisplayStateChange(state);
if (state != Display.STATE_OFF) {
requestGlobalDisplayStateInternal(state, brightness);
}
}
};
requestGlobalDisplayStateInternal
完成实际的背光调节操作。
//frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private void requestGlobalDisplayStateInternal(int state, int brightness) {
...
synchronized (mTempDisplayStateWorkQueue) {
// Update the display state within the lock.
// Note that we do not need to schedule traversals here although it
// may happen as a side-effect of displays changing state.
synchronized (mSyncRoot) {
...
mGlobalDisplayState = state;
mGlobalDisplayBrightness = brightness;
applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue);
}
// Setting the display power state can take hundreds of milliseconds
// to complete so we defer the most expensive part of the work until
// after we have exited the critical section to avoid blocking other
// threads for a long time.
for (int i = 0; i < mTempDisplayStateWorkQueue.size(); i++) {
mTempDisplayStateWorkQueue.get(i).run();
}
}
}
private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) {
final int count = mDisplayDevices.size();
for (int i = 0; i < count; i++) {
DisplayDevice device = mDisplayDevices.get(i);
Runnable runnable = updateDisplayStateLocked(device);
if (runnable != null) {
workQueue.add(runnable);
}
}
}
private Runnable updateDisplayStateLocked(DisplayDevice device) {
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
return device.requestDisplayStateLocked(mGlobalDisplayState, mGlobalDisplayBrightness);
}
return null;
}
这一段代码比较好理解吧。首先applyGlobalDisplayStateLocked
会返回一个List<Runnable>
链表,其size和屏幕个数对应,对一个Runable
对象对应一个屏幕的背光调节逻辑,这个Runnable
由DisplayDevice@requestDisplayStateLocked
函数返回。
DisplayDevice
显示设备状态发生变化时,执行requestDisplayStateLocked
返回一个Runnable
对象(完成状态更改的实际操作)。
//frameworks/base/services/core/java/com/android/server/display/DisplayDevice.java
/**
* Sets the display state, if supported.
*
* @param state The new display state.
* @param brightness The new display brightness.
* @return A runnable containing work to be deferred until after we have
* exited the critical section, or null if none.
*/
public Runnable requestDisplayStateLocked(int state, int brightness) {
return null;
}
以LocalDisplayDevice
为例:
// frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@Override
public Runnable requestDisplayStateLocked(final int state, final int brightness) {
//我们只关心屏幕背光状态,
final boolean stateChanged = (mState != state);
final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null;
if (stateChanged || brightnessChanged) {
final int displayId = mBuiltInDisplayId;
final IBinder token = getDisplayTokenLocked();
final int oldState = mState;
...
if (brightnessChanged) {
mBrightness = brightness;
}
...
// Defer actually setting the display state until after we have exited
// the critical section since it can take hundreds of milliseconds
// to complete.
return new Runnable() {
@Override
public void run() {
...
// Apply brightness changes given that we are in a non-suspended state.
if (brightnessChanged || vrModeChange) {
setDisplayBrightness(brightness);
}
...
}
...
private void setDisplayBrightness(int brightness) {
mBacklight.setBrightness(brightness);
}
};
}
return null;
}
这里可以看到,当背光发生变化时,会通过mBacklight.setBrightness
来设置背光,到了这里,设置背光的逻辑就很简单了。通过LightManager@setBrightness
->LightService@setBrightness
,最后在通过Hal层的Light Service来设置背光。前面的代码真的看着头痛,我就设置一个背光,怎么就把代码写得这么复杂。
4. Lights Service
Framework Lights Service
frameworks/base/services/core/java/com/android/server/lights/LightsService.java
实现很简单,最终通过setLight_native
完成一切。
//frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp
static void setLight_native(
JNIEnv* /* env */,
jobject /* clazz */,
jint light,
jint colorARGB,
jint flashMode,
jint onMS,
jint offMS,
jint brightnessMode) {
if (!validate(light, flashMode, brightnessMode)) {
return;
}
sp<ILight> hal = LightHal::associate();
if (hal == nullptr) {
return;
}
Type type = static_cast<Type>(light);
LightState state = constructState(
colorARGB, flashMode, onMS, offMS, brightnessMode);
{
android::base::Timer t;
Return<Status> ret = hal->setLight(type, state);
processReturn(ret, type, state);
if (t.duration() > 50ms) ALOGD("Excessive delay setting light");
}
}
嘚嘚,调用到了 HAL LIghts Service
HAL Lights Service
这个和SOC平台相关,以RK平台为例。
代码路径位于hardware/interfaces/light/2.0
。
先来看ILights.hal
文件。
package android.hardware.light@2.0;
interface ILight {
setLight(Type type, LightState state) generates (Status status);
getSupportedTypes() generates (vec<Type> types);
};
Lights是一个binderized模式的 HAL,简单的说,其核心逻辑还是实现在原来的Treble架构出现之前的HAL模块里面。
代码路径位于hardware/rockchip/liblights/lights.cpp
。
#define BACKLIGHT_PATH "/sys/class/backlight/rk28_bl/brightness"
#define BACKLIGHT_PATH1 "/sys/class/backlight/backlight/brightness" // for kernel 4.4
#define BUTTON_LED_PATH "sys/class/leds/rk29_key_led/brightness"
#define BATTERY_LED_PATH "sys/class/leds/battery_led/brightness"
static int rgb_to_brightness(struct light_state_t const *state)
{
unsigned int color = state->color & 0x00ffffff;
unsigned char brightness = ((77*((color>>16)&0x00ff)) +
(150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
return brightness;
}
int set_backlight_light(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);
err = write_int(BACKLIGHT_PATH1, brightness);
if (err !=0)
err = write_int(BACKLIGHT_PATH, brightness);
pthread_mutex_unlock(&g_lock);
return 0;
}
大功告成。
5. 梳理
流程如下,整个背光手动调节涉及到了三个进程。
这个图忽略了 LightService,其比较简单,直接 通过 JNI进入 native后调用 HAL LIght Service 执行亮度设置操作。
前面提到,PMS@updateDisplayPowerStateLocked
是异步更新显示设备电源状态的,那PMS
怎么判断DisplayPowerController
已经完成了更新的呢?
一个是在DisplayPowerController@updatePowerState()
中:
animateScreenBrightness(...)
// Notify the power manager when ready.
if (ready && mustNotify) {
// Send state change.
synchronized (mLock) {
if (!mPendingRequestChangedLocked) {
//此时 返回 true
mDisplayReadyLocked = true;
}
}
sendOnStateChangedWithWakelock();
}
执行完animateScreenBrightness
后,我们就可以认为亮度更新操作已经完成了,所以,此时将mDisplayReadyLocked
赋值为True
,然后通过sendOnStateChangedWithWakelock
执行PMS
注册在DisplayRequestController
中的回调来通知PMS
状态更新:
//frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private void sendOnStateChangedWithWakelock() {
mCallbacks.acquireSuspendBlocker();
mHandler.post(mOnStateChangedRunnable);
}
private final Runnable mOnStateChangedRunnable = new Runnable() {
@Override
public void run() {
mCallbacks.onStateChanged();
mCallbacks.releaseSuspendBlocker();
}
};
oh,mCallbacks
实现在哪呢?
public DisplayPowerController(Context context,
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker) {
mHandler = new DisplayControllerHandler(handler.getLooper());
mCallbacks = callbacks;
}
// 在LocalService@DisplayManagerInternal的构造函数中初始化。
private final class LocalService extends DisplayManagerInternal {
@Override
public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager) {
mDisplayPowerController = new DisplayPowerController(
mContext, callbacks, handler, sensorManager, blanker);
}
}
// initPowerManagement又在 PMS@systemReady中被调用
mDisplayManagerInternal.initPowerManagement(
mDisplayPowerCallbacks, mHandler, sensorManager);
所以,mCallbacks
的实际实现是mDisplayPowerCallbacks
。
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks =
new DisplayManagerInternal.DisplayPowerCallbacks() {
private int mDisplayState = Display.STATE_UNKNOWN;
@Override
public void onStateChanged() {
synchronized (mLock) {
mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED;
updatePowerStateLocked();
}
}
}
这里又回到了我们的updatePowerStateLocked
,这个时候,PMS又会通过执行updateDisplayPowerStateLocked
来获取上一次亮度变化的返回值。
6. 总结
实际项目中,产品使用HDMI来输出音视频信号,比如连接LED发送卡,但是客户希望我们能够通过android系统设置来调节他们LED屏幕的亮度,之前就很简单粗暴的做到了SystemUI
里面。现在看来,做到HAL Lightes Service里面才是最好的。优点如下:
- HAL Service编译后,能够直接push到设备运行,相比systemUI调试上方便不少。
- SystemUI中还要考虑设置亮度操作会不会造成UI卡顿,并且还要添加一堆接口来完成亮度设置操作。
DisplayPowerController
实现了亮度调节的动画效果,能够平滑的调整亮度。