AndroidO -- 背光手动调节流程

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对象对应一个屏幕的背光调节逻辑,这个RunnableDisplayDevice@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里面才是最好的。优点如下:

  1. HAL Service编译后,能够直接push到设备运行,相比systemUI调试上方便不少。
  2. SystemUI中还要考虑设置亮度操作会不会造成UI卡顿,并且还要添加一堆接口来完成亮度设置操作。
  3. DisplayPowerController实现了亮度调节的动画效果,能够平滑的调整亮度。
原文地址:https://www.cnblogs.com/liutimo/p/14287536.html