• 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实现了亮度调节的动画效果,能够平滑的调整亮度。
  • 相关阅读:
    谈谈IE条件注释
    0916 编程实验一 词法分析程序
    C语言文法的理解
    复利究极算法
    0106递归下降语意分析
    0309 复利计算
    关于语法树和文法的评价
    10.22 词法分析程序实验心得
    0909 编译原理
    0302 关于就业方面的一些感想
  • 原文地址:https://www.cnblogs.com/liutimo/p/14287536.html
Copyright © 2020-2023  润新知