本文主要講述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中亮度設置的不同步