Android5.1 背光控制分析

本文主要講述android5.1系統對背光的處理,從初始化到點擊自動背光模式再到UI的同步處理,文章分爲三點講述:

1.背光的初始化

2.自動背光的調用過程:從點擊setting中自動調節亮度開始

3.systemUI和setting對背光模式的同步

一:初始化

1.1控制背光服務的啓動

大部分的服務的起點都在systemserver,背光的服務也不例外。首先啓動一些基礎的服務:

private void run() {
        // Create the system service manager.
        mSystemServiceManager = new SystemServiceManager(mSystemContext);
        LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
        // Start services.
        try {
            startOtherServices();
        }
}

創建SystemServiceManager,並將該對象放入一個靜態類LocalServices中,這樣在systemserver進程中可以直接調用各種靜態類中的服務了。在startOtherService中會調用startBootsstrapService方法:

private void startBootstrapServices() {

        // Display manager is needed to provide display metrics before package manager
        // starts up.
        mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);

}

進入mSystemServiceManager.startService中,

public <T extends SystemService> T startService(Class<T> serviceClass) {
        final String name = serviceClass.getName();
        // 創建該服務,創建的服務都是SystemService的子類,需要實現onStart方法。
        final T service;
        try {
            Constructor<T> constructor = serviceClass.getConstructor(Context.class);
            service = constructor.newInstance(mContext);
        } 
        // 註冊服務,將該服務放到一個列表
        mServices.add(service);
        // 調用SystemService子類的onStart方法,在該方法中去註冊服務到servermanager中
	try {
		service.onStart();
	}         
	return service;   
}

進入DisplayManagerService的構造方法中,可以看出只是構造一個包含handler的環境。

    public DisplayManagerService(Context context) {
        super(context);
        mContext = context;
        mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
        mUiHandler = UiThread.getHandler();
        mDisplayAdapterListener = new DisplayAdapterListener();
        mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
    }

將該服務添加到SystemServiceManager的內部列表中後,就會服務的onStart方法:

 public void onStart() {
        mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
        publishBinderService(Context.DISPLAY_SERVICE, new BinderService(),
                true /*allowIsolated*/);//將BinderService服務添加到servicemanager中
        publishLocalService(DisplayManagerInternal.class, new LocalService());//將LocalService服務添加到LocalServices中
    }

可以深入兩個方法看看,其實現都在SystemService中,這個類是常見系統服務的父類如PowerManagerService等。

   protected final void publishBinderService(String name, IBinder service,
            boolean allowIsolated) {
        ServiceManager.addService(name, service, allowIsolated);
    }
    /**
     * Publish the service so it is only accessible to the system process.
     *從註釋可以看出,只讓系統進程能獲取到,說明這些服務不是binder服務,
     *如何讓系統服務獲取呢,就是添加到LocalServices中,這是個靜態類,系統進程通過靜態類來添加和獲取
     */
    protected final <T> void publishLocalService(Class<T> type, T service) {
        LocalServices.addService(type, service);
    }

這個LocalServices其實就是一個靜態類,負責提供內部服務:

public final class LocalServices {
    private static final ArrayMap<Class<?>, Object> sLocalServiceObjects =
            new ArrayMap<Class<?>, Object>();
    public static <T> void addService(Class<T> type, T service) {
        synchronized (sLocalServiceObjects) {
            sLocalServiceObjects.put(type, service);
        }
    }
}

其內部也有個Map,負責保存存入LocalServices的類,這些類一般是服務。現在可以知道,在DisplayManagerService的onStart方法中,創建了一個LocalService,並將其加入到LocalServices的列表中,來看看LocalService是什麼。

 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) {
                        // The order of operations is important for legacy reasons.
                        if (state == Display.STATE_OFF) {
                            requestGlobalDisplayStateInternal(state);
                        }
                        callbacks.onDisplayStateChange(state);
                        if (state != Display.STATE_OFF) {
                            requestGlobalDisplayStateInternal(state);
                        }
                    }
                };
                mDisplayPowerController = new DisplayPowerController(
                        mContext, callbacks, handler, sensorManager, blanker);
            }
        }
.....
}

LocalService是DisplayManagerInternal的子類,這個DisplayManagerInternal其實是個接口類,LocalService負責實現這幾個接口。

至此初始化的第一部分結束,主要是構造服務和環境。

1.2PowerManagerService.SystemReady方法

系統服務起來後會調用其systemReady方法:

public void systemReady(IAppOpsService appOps) {
        synchronized (mLock) {
            mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
            mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
            mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
            mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
            SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
            mSettingsObserver = new SettingsObserver(mHandler);
            mLightsManager = getLocalService(LightsManager.class);
            // Initialize display power management.
            mDisplayManagerInternal.initPowerManagement(mDisplayPowerCallbacks, mHandler, sensorManager);
            // Register for settings changes.
            resolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.SCREEN_BRIGHTNESS),
                    false, mSettingsObserver, UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.SCREEN_BRIGHTNESS_MODE),
                    false, mSettingsObserver, UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
                    false, mSettingsObserver, UserHandle.USER_ALL);
            mSystemReadyDone.set(true);
         ......
         updatePowerStateLocked}
}

1.2.1 initPowerManagement

可以看出在systemready方法中會獲取sensormanager,和lightservice等,最重要的是調用initPowerManagement,從上面的分析可以知道mDisplayManagerInternal其實就是DisplayManagerService.LocalService,其initPowerManagerment方法中創建了DisplayPowerController。

public DisplayPowerController(Context context,
            DisplayPowerCallbacks callbacks, Handler handler,
            SensorManager sensorManager, DisplayBlanker blanker) {
        mHandler = new DisplayControllerHandler(handler.getLooper());
        mCallbacks = callbacks;
        mBatteryStats = BatteryStatsService.getService();
        mLights = LocalServices.getService(LightsManager.class);
        mSensorManager = sensorManager;
        mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
        mBlanker = blanker;
        mContext = context;
//獲取系統關於背光的一些基礎參數
        final Resources resources = context.getResources();
        final int screenBrightnessSettingMinimum = clampAbsoluteBrightness(resources.getInteger(
                com.android.internal.R.integer.config_screenBrightnessSettingMinimum));

        mScreenBrightnessDozeConfig = clampAbsoluteBrightness(resources.getInteger(
                com.android.internal.R.integer.config_screenBrightnessDoze));

        mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger(
                com.android.internal.R.integer.config_screenBrightnessDim));

        mScreenBrightnessDarkConfig = clampAbsoluteBrightness(resources.getInteger(
                com.android.internal.R.integer.config_screenBrightnessDark));

        int screenBrightnessRangeMinimum = Math.min(Math.min(
                screenBrightnessSettingMinimum, mScreenBrightnessDimConfig),
                mScreenBrightnessDarkConfig);

        mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON;
//此處很重要,系統如有背光傳感器,則要配置其爲true,表明可以使用背光
        mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
                com.android.internal.R.bool.config_automatic_brightness_available);

        mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);

        if (mUseSoftwareAutoBrightnessConfig) {
            int[] lux = resources.getIntArray(
                    com.android.internal.R.array.config_autoBrightnessLevels);
            int[] screenBrightness = resources.getIntArray(
                    com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
            int lightSensorWarmUpTimeConfig = resources.getInteger(
                    com.android.internal.R.integer.config_lightSensorWarmupTime);
            final float dozeScaleFactor = resources.getFraction(
                    com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
                    1, 1);
//此處也很重要,如果有背光需要實現兩個數組,來確定光感值和系統亮度的關係
            Spline screenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
            if (screenAutoBrightnessSpline == null) {
                mUseSoftwareAutoBrightnessConfig = false;
            } else {
                int bottom = clampAbsoluteBrightness(screenBrightness[0]);
               
                if (bottom < screenBrightnessRangeMinimum) {
                    screenBrightnessRangeMinimum = bottom;
                }
                mAutomaticBrightnessController = new AutomaticBrightnessController(this,
                        handler.getLooper(), sensorManager, screenAutoBrightnessSpline,
                        lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
                        mScreenBrightnessRangeMaximum, dozeScaleFactor);
            }
        }

        mScreenBrightnessRangeMinimum = screenBrightnessRangeMinimum;

        mColorFadeFadesConfig = resources.getBoolean(
                com.android.internal.R.bool.config_animateScreenLights);

        if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
            mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
            if (mProximitySensor != null) {
                mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
                        TYPICAL_PROXIMITY_THRESHOLD);
            }
        }

    }

上面一大串,最重要的是創建一個AutomaticBrightnessController,這個類主要功能是計算自動背光的值,計算好以後配合一個動畫類用來實現背光的平穩過度和一個DisplayPowerState類用來控制顯示開關和背光和一個light服務用來直接調用底層native方法控制背光。關於計算方法和參數的設置等,在此不討論,主要討論流程,進入AutoBrightnessController構造函數。

 public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
            SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
            int brightnessMin, int brightnessMax, float dozeScaleFactor) {
        mCallbacks = callbacks;
        mTwilight = LocalServices.getService(TwilightManager.class);
        mSensorManager = sensorManager;
        mScreenAutoBrightnessSpline = autoBrightnessSpline;
        mScreenBrightnessRangeMinimum = brightnessMin;
        mScreenBrightnessRangeMaximum = brightnessMax;
        mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
        mDozeScaleFactor = dozeScaleFactor;
        mHandler = new AutomaticBrightnessHandler(looper);
        mAmbientLightRingBuffer = new AmbientLightRingBuffer();
        mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
    }
主要是初始化環境,包括獲取背光控制傳感器。

二:開機背光控制和自動調節過程

2.1 開機背光控制

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);
}
private boolean updateDisplayPowerStateLocked(int dirty) {
            //根據設置和默認值確定亮度值.
            int screenBrightness = mScreenBrightnessSettingDefault;
            float screenAutoBrightnessAdjustment = 0.0f;
            boolean autoBrightness = (mScreenBrightnessModeSetting ==
                    Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
            if (autoBrightness) {
                screenBrightness = mScreenBrightnessSettingDefault;
                if (isValidAutoBrightnessAdjustment(
                        mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) {
                    screenAutoBrightnessAdjustment =
                            mTemporaryScreenAutoBrightnessAdjustmentSettingOverride;
                } else if (isValidAutoBrightnessAdjustment(
                        mScreenAutoBrightnessAdjustmentSetting)) {
                    screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting;
                }
            }
            screenAutoBrightnessAdjustment = Math.max(Math.min(
                    screenAutoBrightnessAdjustment, 1.0f), -1.0f);

            // Update display power request.
            mDisplayPowerRequest.screenBrightness = screenBrightness;
            mDisplayPowerRequest.screenAutoBrightnessAdjustment =
                    screenAutoBrightnessAdjustment;
            mDisplayPowerRequest.useAutoBrightness = autoBrightness;
            mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
            mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled;
            mDisplayPowerRequest.boostScreenBrightness = mScreenBrightnessBoostInProgress;
            mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                    mRequestWaitForNegativeProximity);
            mRequestWaitForNegativeProximity = false;
        }
        return mDisplayReady && !oldDisplayReady;
    }

調用DisplayManagerService.LocalService的requestPowerState的方法:

      @Override
        public boolean requestPowerState(DisplayPowerRequest request,
                boolean waitForNegativeProximity) {
            return mDisplayPowerController.requestPowerState(request,
                    waitForNegativeProximity);
        }


 public boolean requestPowerState(DisplayPowerRequest request,boolean waitForNegativeProximity) {
        synchronized (mLock) {
            boolean changed = false;
            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();
            }

            return mDisplayReadyLocked;
        }
    }

    private void sendUpdatePowerStateLocked() {
        if (!mPendingUpdatePowerStateLocked) {
            mPendingUpdatePowerStateLocked = true;
            Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
            msg.setAsynchronous(true);
            mHandler.sendMessage(msg);
        }
    }


 public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_UPDATE_POWER_STATE:
                    updatePowerState();
                    break;

updatePowerState方法很重要,後面我們還會碰到它:

private void updatePowerState() {
        synchronized (mLock) {
            mPendingUpdatePowerStateLocked = false;
            if (mPowerRequest == null) {
                mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
                mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked;
                mPendingWaitForNegativeProximityLocked = false;
                mPendingRequestChangedLocked = false;
                mustInitialize = true;
            } else if (mPendingRequestChangedLocked) {
                autoBrightnessAdjustmentChanged = (mPowerRequest.screenAutoBrightnessAdjustment
                        != mPendingRequestLocked.screenAutoBrightnessAdjustment);
                mPowerRequest.copyFrom(mPendingRequestLocked);
                mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
                mPendingWaitForNegativeProximityLocked = false;
                mPendingRequestChangedLocked = false;
                mDisplayReadyLocked = false;
            }

            mustNotify = !mDisplayReadyLocked;
        }

        // Initialize things the first time the power state is changed.
        if (mustInitialize) {
            initialize();
        }

        // Compute the basic display state using the policy.
        // We might override this below based on other factors.
        int state;
        int brightness = PowerManager.BRIGHTNESS_DEFAULT;
        boolean performScreenOffTransition = false;
        switch (mPowerRequest.policy) {
            case DisplayPowerRequest.POLICY_OFF:
                state = Display.STATE_OFF;
                performScreenOffTransition = true;
                break;
            case DisplayPowerRequest.POLICY_DOZE:
                if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {
                    state = mPowerRequest.dozeScreenState;
                } else {
                    state = Display.STATE_DOZE;
                }
                if (!mAllowAutoBrightnessWhileDozingConfig) {
                    brightness = mPowerRequest.dozeScreenBrightness;
                }
                break;
            case DisplayPowerRequest.POLICY_DIM:
            case DisplayPowerRequest.POLICY_BRIGHT:
            default:
                state = Display.STATE_ON;
                break;
        }

        // Animate the screen state change unless already animating.
        // The transition may be deferred, so after this point we will use the
        // actual state instead of the desired one.
        animateScreenStateChange(state, performScreenOffTransition);
        state = mPowerState.getScreenState();

        // Configure auto-brightness.
        boolean autoBrightnessEnabled = false;
        if (mAutomaticBrightnessController != null) {
            final boolean autoBrightnessEnabledInDoze = mAllowAutoBrightnessWhileDozingConfig
                    && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND);
            autoBrightnessEnabled = mPowerRequest.useAutoBrightness
                    && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
                    && brightness < 0;
            mAutomaticBrightnessController.configure(autoBrightnessEnabled,
                    mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON);
        }
        // Apply auto-brightness.
        boolean slowChange = false;
        if (brightness < 0) {
            if (autoBrightnessEnabled) {
                brightness = mAutomaticBrightnessController.getAutomaticScreenBrightness();
            }
            if (brightness >= 0) {
                // Use current auto-brightness value and slowly adjust to changes.
                brightness = clampScreenBrightness(brightness);
                if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
                    slowChange = true; // slowly adapt to auto-brightness
                }
                mAppliedAutoBrightness = true;
            } else {
                mAppliedAutoBrightness = false;
            }
        } else {
            mAppliedAutoBrightness = false;
        }

        // Apply manual brightness.
        // Use the current brightness setting from the request, which is expected
        // provide a nominal default value for the case where auto-brightness
        // is not ready yet.
        if (brightness < 0) {
            if(mPowerRequest.screenBrightness != 0 || ActionsConfig.ACTIONS_FEATURE_HDMI_ONOFF_DEFAULT_ON == 0){
                // ActionsCode(authro:songzhining, comment: fix brighten bug from dim to off state)
                if (mPowerRequest.policy == DisplayPowerRequest.POLICY_OFF) {
                    brightness = clampScreenBrightness(mPowerState.getScreenBrightness());
                } else {
                    brightness = clampScreenBrightness(mPowerRequest.screenBrightness);
                }
            }else{
                brightness = 0;
            }
        }
        
        // Animate the screen brightness when the screen is on or dozing.
        // Skip the animation when the screen is off or suspended.
        if (!mPendingScreenOff) {
            if (state == Display.STATE_ON || state == Display.STATE_DOZE) {
                animateScreenBrightness(brightness,
                        slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
            } else {
                animateScreenBrightness(brightness, 0);
            }
        }

        // Determine whether the display is ready for use in the newly requested state.
        // Note that we do not wait for the brightness ramp animation to complete before
        // reporting the display is ready because we only need to ensure the screen is in the
        // right power state even as it continues to converge on the desired brightness.
        final boolean ready = mPendingScreenOnUnblocker == null
                && !mColorFadeOnAnimator.isStarted()
                && !mColorFadeOffAnimator.isStarted()
                && mPowerState.waitUntilClean(mCleanListener);
        final boolean finished = ready
                && !mScreenBrightnessRampAnimator.isAnimating();

        // Grab a wake lock if we have unfinished business.
        if (!finished && !mUnfinishedBusiness) {
            if (DEBUG) {
                Slog.d(TAG, "Unfinished business...");
            }
            mCallbacks.acquireSuspendBlocker();
            mUnfinishedBusiness = true;
        }

        // Notify the power manager when ready.
        if (ready && mustNotify) {
            // Send state change.
            synchronized (mLock) {
                if (!mPendingRequestChangedLocked) {
                    mDisplayReadyLocked = true;

                    if (DEBUG) {
                        Slog.d(TAG, "Display ready!");
                    }
                }
            }
            sendOnStateChangedWithWakelock();
        }

        // Release the wake lock when we have no unfinished business.
        if (finished && mUnfinishedBusiness) {
            if (DEBUG) {
                Slog.d(TAG, "Finished business...");
            }
            mUnfinishedBusiness = false;
            mCallbacks.releaseSuspendBlocker();
        }
    }

在updatepowerstate中首先通過initialize方法初始化控制背光的環境,即創建相應的類包括動畫控制類等;然後通過configure方法配置是否打開關閉自動背光,也就是要不要打開光感;最後通過animateScreenBrightness來將得到的brightness值配置到底層。

2.1.1 首先看看initialize方法:

private void initialize() {
        // Initialize the power state object for the default display.
        // In the future, we might manage multiple displays independently.
        mPowerState = new DisplayPowerState(mBlanker,
                mLights.getLight(LightsManager.LIGHT_ID_BACKLIGHT),
                new ColorFade(Display.DEFAULT_DISPLAY));
        mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
                mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
        mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
    }

創建一個DisplayPowerState,作爲控制屏幕的類,這個類是控制屏幕亮滅和亮度的接口。RampAnimator這個類是泛型



 public DisplayPowerState(DisplayBlanker blanker, Light backlight, ColorFade electronBeam) {
        mHandler = new Handler(true /*async*/);
        mChoreographer = Choreographer.getInstance();
        mBlanker = blanker;
        mBacklight = backlight;
        mColorFade = electronBeam;
        mPhotonicModulator = new PhotonicModulator();
        mPhotonicModulator.start();

        // At boot time, we know that the screen is on and the electron beam
        // animation is not playing.  We don't know the screen's brightness though,
        // so prepare to set it to a known state when the state is next applied.
        // Although we set the brightness to full on here, the display power controller
        // will reset the brightness to a new level immediately before the changes
        // actually have a chance to be applied.
        mScreenState = Display.STATE_ON;
        mScreenBrightness = PowerManager.BRIGHTNESS_ON;
        scheduleScreenUpdate();

        mColorFadePrepared = false;
        mColorFadeLevel = 1.0f;
        mColorFadeReady = true;
    }


    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();
        }
    };



final class RampAnimator<T> {
    private final T mObject;//泛型
    private final IntProperty<T> mProperty;
    private final Choreographer mChoreographer;
    private int mCurrentValue;
    private int mTargetValue;
    private int mRate;
    private boolean mAnimating;
    private float mAnimatedValue; // higher precision copy of mCurrentValue
    private long mLastFrameTimeNanos;
    private boolean mFirstTime = true;

    private Listener mListener;

    public RampAnimator(T object, IntProperty<T> property) {
        mObject = object;
        mProperty = property;
        mChoreographer = Choreographer.getInstance();
    }
}


2.1.2 configure方法


    public void configure(boolean enable, float adjustment, boolean dozing) {
        // While dozing, the application processor may be suspended which will prevent us from
        // receiving new information from the light sensor. On some devices, we may be able to
        // switch to a wake-up light sensor instead but for now we will simply disable the sensor
        // and hold onto the last computed screen auto brightness.  We save the dozing flag for
        // debugging purposes.
        mDozing = dozing;
        boolean changed = setLightSensorEnabled(enable && !dozing);
        changed |= setScreenAutoBrightnessAdjustment(adjustment);
        if (changed) {
            updateAutoBrightness(false /*sendUpdate*/);//這個值只是更新亮度值,並沒有將這個值設置下去。
        }
    }

首先打開使能光感,然後更新自動背光亮度,並將獲取到的光感值進行處理,處理過程後面討論。

private boolean setLightSensorEnabled(boolean enable) {
        if (enable) {
            if (!mLightSensorEnabled) {
                mLightSensorEnabled = true;
                mLightSensorEnableTime = SystemClock.uptimeMillis();
                mSensorManager.registerListener(mLightSensorListener, mLightSensor,
                        LIGHT_SENSOR_RATE_MILLIS * 1000, mHandler);//申請光感
                return true;
            }
        } else {
            if (mLightSensorEnabled) {
                mLightSensorEnabled = false;
                mAmbientLuxValid = false;
                mRecentLightSamples = 0;
                mAmbientLightRingBuffer.clear();
                mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
                mSensorManager.unregisterListener(mLightSensorListener);
            }
        }
        return false;
    }

2.1.3 animateScreenBrightness


    private void animateScreenBrightness(int target, int rate) {
        if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
            try {
                mBatteryStats.noteScreenBrightness(target);
            } catch (RemoteException ex) {
                // same process
            }
        }
    }

調用animateTo方法,從上面的分析可知mScreenBrightRampAnimator是一個泛型,看起animateTo方法:

 public boolean animateTo(int target, int rate) {
        // Immediately jump to the target the first time.此時應該是第一次調用,會走這個分支
        if (mFirstTime || rate <= 0) {
            if (mFirstTime || target != mCurrentValue) {
                mFirstTime = false;
                mRate = 0;
                mTargetValue = target;
                mCurrentValue = target;
                mProperty.setValue(mObject, target);
                if (mAnimating) {
                    mAnimating = false;
                    cancelAnimationCallback();
                }
                if (mListener != null) {
                    mListener.onAnimationEnd();
                }
                return true;
            }
            return false;
        }

        // Adjust the rate based on the closest target.
        // If a faster rate is specified, then use the new rate so that we converge
        // more rapidly based on the new request.
        // If a slower rate is specified, then use the new rate only if the current
        // value is somewhere in between the new and the old target meaning that
        // we will be ramping in a different direction to get there.
        // Otherwise, continue at the previous rate.
        if (!mAnimating
                || rate > mRate
                || (target <= mCurrentValue && mCurrentValue <= mTargetValue)
                || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
            mRate = rate;
        }

        final boolean changed = (mTargetValue != target);
        mTargetValue = target;

        // Start animating.
        if (!mAnimating && target != mCurrentValue) {
            mAnimating = true;
            mAnimatedValue = mCurrentValue;
            mLastFrameTimeNanos = System.nanoTime();
            postAnimationCallback();
        }

        return changed;
    }

從註釋可以,第一次會直接設置值,並不會有一個亮度漸變的動畫過程。從mScreenBrightRampAnimator的構造方法可知,這個mProperty就是DisplayPowerState.SCREEN_BRIGHTNESS,所以mProperty.setValue(mObject, target);其實就是調用

object.setScreenBrightness(value);即DisplayPowerState的setScreenBrightness(value);方法。


    /**
     * Sets the display brightness.
     *
     * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest).
     */
    public void setScreenBrightness(int brightness) {
        if (mScreenBrightness != brightness) {
            mScreenBrightness = brightness;
            if (mScreenState != Display.STATE_OFF) {
                mScreenReady = false;
                scheduleScreenUpdate();
            }
        }
    }

    private void scheduleScreenUpdate() {
        if (!mScreenUpdatePending) {
            mScreenUpdatePending = true;
            postScreenUpdateThreadSafe();
        }
    }

    private void postScreenUpdateThreadSafe() {
        mHandler.removeCallbacks(mScreenUpdateRunnable);
        mHandler.post(mScreenUpdateRunnable);
    }

    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)) {
                mScreenReady = true;
                invokeCleanListenerIfNeeded();
        }
    };

 public boolean setState(int state, int backlight) {
            synchronized (mLock) {
                if (state != mPendingState || backlight != mPendingBacklight) {
                    mPendingState = state;//屏的狀態並未改變
                    mPendingBacklight = backlight;//本次設置是改變背光值
                    if (!mChangeInProgress) {
                        mChangeInProgress = true;
                        mLock.notifyAll();
                    }
                }
                return !mChangeInProgress;
            }
        }
從DisplayPowerState的構造方法可知會創建一個PhotonicModulator,並調用其start方法,可知其繼承了Thread類。


 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);//改變
                    mActualState = state;
                    mActualBacklight = backlight;
                }

                // Apply pending change.
                boolean suspending = Display.isSuspendedState(state);
                if (stateChanged && !suspending) {
                    requestDisplayState(state);
                }
                if (backlightChanged) {
                    setBrightness(backlight);
                }
                if (stateChanged && suspending) {
                    requestDisplayState(state);
                }
            }
        }

調用setBrightness方法:

 private void setBrightness(int backlight) {
            Trace.traceBegin(Trace.TRACE_TAG_POWER, "setBrightness(" + backlight + ")");
            try {
                mBacklight.setBrightness(backlight);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_POWER);
            }
        }

使用Light類的setBrightness方法來控制背光的大小。到此實現了背光的控制。

總結下DisplayPowerState是一個控制背光和亮滅屏的類,通過PhotonicModulator類在另一個線程中實現功能。PhotonicModulator的run方法一直在檢查背光和屏狀態有沒有改變,如果有改變則調用相關方法來實現,實現方式爲獲取Light來控制亮度;而DisplayPowerState向外界提供setValue方法來設置背光值,讓其改變,這個外界其實就是RampAnimator泛型。

至此,背光的開機控制已經實現,下面來分析背光如何根據光感的值來自動調節。

2.2背光自動調節過程


在使能光感的方法中註冊了一個回調方法

    private final SensorEventListener mLightSensorListener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            if (mLightSensorEnabled) {
                final long time = SystemClock.uptimeMillis();
                final float lux = event.values[0];
                handleLightSensorEvent(time, lux);
            }
        }
    };
    private void handleLightSensorEvent(long time, float lux) {
        mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
        applyLightSensorMeasurement(time, lux);
        updateAmbientLux(time);
    }
private void updateAmbientLux(long time) {
        // If the light sensor was just turned on then immediately update our initial
        // estimate of the current ambient light level.光感剛剛被初始化的時候走下面分支
        if (!mAmbientLuxValid) {
            .....
        }
        long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
        long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
        float ambientLux = calculateAmbientLux(time);
        if (ambientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
                || ambientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
            setAmbientLux(ambientLux);
            updateAutoBrightness(true);
            nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
            nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
        }
        long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
        nextTransitionTime =
                nextTransitionTime > time ? nextTransitionTime : time + LIGHT_SENSOR_RATE_MILLIS;
        mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
    }


 private void updateAutoBrightness(boolean sendUpdate) {

        float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
        float gamma = 1.0f;
        if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
                && mScreenAutoBrightnessAdjustment != 0.0f) {
            final float adjGamma = MathUtils.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
                    Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
            gamma *= adjGamma;
        }
        }

        if (gamma != 1.0f) {
            final float in = value;
            value = MathUtils.pow(value, gamma);
        }
        int newScreenAutoBrightness =
            clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
        if (mScreenAutoBrightness != newScreenAutoBrightness) {
            mScreenAutoBrightness = newScreenAutoBrightness;
            mLastScreenAutoBrightnessGamma = gamma;
            if (sendUpdate) {
                mCallbacks.updateBrightness();
            }
        }
    }

調用了DisplayPowerController的updateBrightness方法:

    @Override
    public void updateBrightness() {
        sendUpdatePowerState();
    }

最後調用了updatePowerState方法,這個方法在第一次開機設置背光亮度的時候分析過,不同的地方在於animateTo方法中,不是第一次調用了:

public boolean animateTo(int target, int rate) {
        // Immediately jump to the target the first time.
        if (mFirstTime || rate <= 0) {
           ....
        }

        // Start animating.開始進入動畫的過程
        if (!mAnimating && target != mCurrentValue) {
            mAnimating = true;
            mAnimatedValue = mCurrentValue;
            mLastFrameTimeNanos = System.nanoTime();
            postAnimationCallback();
        }
        return changed;
    }
   private void postAnimationCallback() {
        mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
    }
private final Runnable mAnimationCallback = new Runnable() {
        @Override // Choreographer callback
        public void run() {
            final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
            final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos)
                    * 0.000000001f;
            mLastFrameTimeNanos = frameTimeNanos;

            // Advance the animated value towards the target at the specified rate
            // and clamp to the target. This gives us the new current value but
            // we keep the animated value around to allow for fractional increments
            // towards the target.
            final float scale = ValueAnimator.getDurationScale();
            if (scale == 0) {
                // Animation off.
                mAnimatedValue = mTargetValue;
            } else {
                final float amount = timeDelta * mRate / scale;
                if (mTargetValue > mCurrentValue) {
                    mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
                } else {
                    mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
                }
            }
            final int oldCurrentValue = mCurrentValue;
            mCurrentValue = Math.round(mAnimatedValue);
//如果以前的值和當前值不同時,設置值
            if (oldCurrentValue != mCurrentValue) {
                mProperty.setValue(mObject, mCurrentValue);
            }
//如果第一次模擬後還是不能達到目標時,在來一次
            if (mTargetValue != mCurrentValue) {
                postAnimationCallback();
            } else {//如果目標值和當前值相同,證明不需要動畫了,調用動畫結束的回調
                mAnimating = false;
                if (mListener != null) {
                    mListener.onAnimationEnd();
                }
            }
        }
    };

從上述代碼可知,這是個循環,postAnimationCallback會一直調用,直到調節到需要的值纔會停止,所以就會有動畫的效果。

    所以這個類的名字叫RampAnimator,其實這個類是個工具,任何需要動畫設置的硬件都可以使用它。所以它設計爲了一個泛型,就是爲了可以傳入其他的類,讓其實現完了計算後要調用傳入的泛型的方法如setvale等,注意這個泛型要實現這個setvale方法。這個泛型可以是控制背光的。

總結:

DisplayPowerConntroller:是一個控制類,是自動背光調節的樞紐,聯繫其他的類。

AutobrightnessConntroller:通過註冊光感的回調來獲取光感的值,若有改變則,updateAutoBrightness即更新brightness值,然後通過回調來通知到DisplayPowerConntroller,最後調用DisplayPowerConntroller的updatePowerState方法。

RampAnimator:在updatePowerState方法中會調用mScreenBrightRampAnimator的animateTo方法,這裏會傳入一個brightness目標值,而在animateTo中則會通過一個循環一點點逼近這個目標值,從而實現動畫的效果。

DisplayPowerState:在循環過程中會調用DisplayPowerState.SCREEN_BRIGHTNESS方法來設置亮度值,即調用到DisplayPowerState.setScreenBrightness。setScreenBrightness最後會執行一個runnable,在該runnable中調用mPhotonicModulator.setState來設置State和brightness。

PhotonicModulator:在構造DisplayPowerState時就會創建PhotonicModulator,它繼承Thread,會一直跑一個線程,在該線程中一直會檢測State和brightness有沒有改變,如果改變就會調用mBacklight.setBrightness(backlight);來設置背光值。
















關於systemui和setting中亮度設置的不同步


       
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章