android5.1 自動亮度調節簡析
android亮度調節在"設定"的"顯示"中,分為手動調節和自動調節。其中手動調節就是通過拖動"亮度調節"的拖動條來直接設定亮度值,自動調節則比較複雜,設定自動亮度模式後,再拖動"亮度調節"的拖動條,可以小範圍改變亮度值(前提是光照條件不劇烈變化),但這時不是直接設定的亮度值,而是需要通過一系列轉變轉成亮度值。
現在遇到問題是:在自動亮度調節模式下,調節亮度拖動條到最小,螢幕變得很黑。
設定中的亮度設定是在DisplaySettings.java(/Settings/src/com/android/settings/DisplaySettings.java)
中,但這裡面沒有拖動條的相關操作,只有設定手動調節和自動調節模式的操作。其實彈出拖動條的操作在BrightnessPreference.java(/Settings/src/com/android/settings/BrightnessPreference.java)
protected void onClick() {
Log.i("BrightnessPreference", "BrightnessPreference:brightness dialog open");
getContext().startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG),
UserHandle.CURRENT_OR_SELF);
}
通過查詢這個action,發現其就是android.intent.action.SHOW_BRIGHTNESS_DIALOG
SystemUI(frameworks\base\packages\SystemUI)
的AndroidManifest.xml裡也出現了:
<activity
android:name=".settings.BrightnessDialog"
android:label="@string/quick_settings_brightness_dialog_title"
android:theme="@android:style/Theme.DeviceDefault.Dialog"
android:finishOnCloseSystemDialogs ="true"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
這裡就是通過action啟動BrightnessDialog.java(/UsbStorageActivity/src/com/android/systemui/settings/BrightnessDialog.java)
,發現這個Activity就是亮度調節的彈出框,至此就沒有Settings什麼事了。
-
BrightnessDialog
在BrightnessDialog中初始化拖動條等相關控制元件,並例項化了
BrightnessController.java(/UsbStorageActivity/src/com/android/systemui/settings/BrightnessController.java)
:setContentView(R.layout.quick_settings_brightness_dialog); final ImageView icon = (ImageView) findViewById(R.id.brightness_icon); final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider); mBrightnessController = new BrightnessController(this, icon, slider);
並註冊和登出BrightnessController的回撥方法:
@Override protected void onStart() { super.onStart(); mBrightnessController.registerCallbacks(); } @Override protected void onStop() { super.onStop(); mBrightnessController.unregisterCallbacks(); }
-
BrightnessController
BrightnessController的回撥方法主要是在亮度調節模式改變時更新系統儲存的模式,在亮度調節時更新拖動條(此拖動條和下拉欄的亮度條保持一致)。這裡主要看亮度設定時的操作。
因為BrightnessController實現了對ToggleSlider的監聽,在拖動條改變時設定亮度:@Override public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) { updateIcon(mAutomatic); if (mExternalChange) return; if (!mAutomatic) { final int val = value + mMinimumBacklight; Log.i(TAG, "brightness mode:"+mAutomatic+"\nvalue:"+value+"value set:"+val); 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 { final float adj = value / (BRIGHTNESS_ADJ_RESOLUTION / 2f) - 1; //original code setBrightnessAdj(adj); if (!tracking) { AsyncTask.execute(new Runnable() { public void run() { Settings.System.putFloatForUser(mContext.getContentResolver(), Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adj, UserHandle.USER_CURRENT); } }); } } for (BrightnessStateChangeCallback cb : mChangeCallbacks) { cb.onBrightnessLevelChanged(); } }
value就是拖動條的值,在初始化的時候,亮度條的範圍和值都被設定:
private void updateSlider() { if (mAutomatic) { float value = Settings.System.getFloatForUser(mContext.getContentResolver(),Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, UserHandle.USER_CURRENT); mControl.setMax((int) BRIGHTNESS_ADJ_RESOLUTION); mControl.setValue((int) ((value + 1) * BRIGHTNESS_ADJ_RESOLUTION / 2f)); } else { int value; value = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, mMaximumBacklight, UserHandle.USER_CURRENT); mControl.setMax(mMaximumBacklight - mMinimumBacklight); mControl.setValue(value - mMinimumBacklight); } }
可以看出自動模式下亮度條最大值為
BRIGHTNESS_ADJ_RESOLUTION
,該值為100,而手動模式下亮度條最大值為mMaximumBacklight - mMinimumBacklight
,其中mMaximumBacklight
為255,mMinimumBacklight
為10。
在拖動條的值改變時,onChange()方法被呼叫,若為手動模式,則:final int val = value + mMinimumBacklight; setBrightness(val);
可以看出,手動模式直接在從拖動條獲取的基數value上加了個亮度的最小值,然後呼叫
setBrightness(val)
,再來看setBrightness(val)
:private void setBrightness(int brightness) { try { mPower.setTemporaryScreenBrightnessSettingOverride(brightness); } catch (RemoteException ex) { } }
mPower
是IPowerManager的物件,這裡先放在一邊。
再來看若為自動模式時拖動條改變,final float adj = value / (BRIGHTNESS_ADJ_RESOLUTION / 2f) - 1; setBrightnessAdj(adj);
其中
BRIGHTNESS_ADJ_RESOLUTION
為100。可以計算出adj的範圍隨著value的改變始終保持在[-1,1],暫且稱之為調節引數。然後呼叫setBrightnessAdj(adj)
:private void setBrightnessAdj(float adj) { try { mPower.setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(adj); } catch (RemoteException ex) { } }
可以看出,兩種模式都最終交給
IPowerManager
處理了,然後基本就是由framework層來進行處理。
3.IPowerManager
IPowerManager開頭就有註釋:/* * This file is auto-generated. DO NOT MODIFY. * Original file: frameworks/base/core/java/android/os/IPowerManager.aidl */
所以這個檔案是aidl自動生成的。
兩個方法:@Override public void setTemporaryScreenBrightnessSettingOverride(int brightness) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(brightness); mRemote.transact(Stub.TRANSACTION_setTemporaryScreenBrightnessSettingOverride, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeFloat(adj); mRemote.transact(Stub.TRANSACTION_setTemporaryScreenAutoBrightnessAdjustmentSettingOverride, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } }
然後搜尋發現在
PowerManagerService.java(com.android.server.power.PowerManagerService.class)
中的BinderService類implements了IPowerManager的Stub類,並且實現了上述兩個方法:\/** * Used by the settings application and brightness control widgets to * temporarily override the current screen brightness setting so that the * user can observe the effect of an intended settings change without applying * it immediately. * * The override will be canceled when the setting value is next updated. * * @param brightness The overridden brightness. * * @see android.provider.Settings.System#SCREEN_BRIGHTNESS */ @Override // Binder call public void setTemporaryScreenBrightnessSettingOverride(int brightness) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); final long ident = Binder.clearCallingIdentity(); try { setTemporaryScreenBrightnessSettingOverrideInternal(brightness); } finally { Binder.restoreCallingIdentity(ident); } } /** * Used by the settings application and brightness control widgets to * temporarily override the current screen auto-brightness adjustment setting so that the * user can observe the effect of an intended settings change without applying * it immediately. * * The override will be canceled when the setting value is next updated. * * @param adj The overridden brightness, or Float.NaN to disable the override. * * @see android.provider.Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ */ @Override // Binder call public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); final long ident = Binder.clearCallingIdentity(); try { setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(adj); } finally { Binder.restoreCallingIdentity(ident); } }
註釋裡說明,這兩個方法是Settings和亮度調節外掛使用的,再來看值傳進來後呼叫的兩個方法:
private void setTemporaryScreenBrightnessSettingOverrideInternal(int brightness) { synchronized (mLock) { if (mTemporaryScreenBrightnessSettingOverride != brightness) { mTemporaryScreenBrightnessSettingOverride = brightness; mDirty |= DIRTY_SETTINGS; updatePowerStateLocked(); } } } private void setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(float adj) { synchronized (mLock) { // Note: This condition handles NaN because NaN is not equal to any other // value, including itself. if (mTemporaryScreenAutoBrightnessAdjustmentSettingOverride != adj) { mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = adj; mDirty |= DIRTY_SETTINGS; updatePowerStateLocked(); } } }
拿到值後賦值給一個全域性變數,然後馬上呼叫
updatePowerStateLocked()
,再來看此方法對傳入的值做了哪些操作:/** * Updates the display power state asynchronously. * When the update is finished, mDisplayReady will be set to true. The display * controller posts a message to tell us when the actual display power state * has been updated so we come back here to double-check and finish up. * * This function recalculates the display power state each time. * * @return True if the display became ready. */ private boolean updateDisplayPowerStateLocked(int dirty) { final boolean oldDisplayReady = mDisplayReady; if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) { mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked(); // Determine appropriate screen brightness and auto-brightness adjustments. int screenBrightness = mScreenBrightnessSettingDefault; float screenAutoBrightnessAdjustment = 0.0f; boolean autoBrightness = (mScreenBrightnessModeSetting == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { screenBrightness = mScreenBrightnessOverrideFromWindowManager; autoBrightness = false; } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) { screenBrightness = mTemporaryScreenBrightnessSettingOverride; } else if (isValidBrightness(mScreenBrightnessSetting)) { screenBrightness = mScreenBrightnessSetting; } if (autoBrightness) { screenBrightness = mScreenBrightnessSettingDefault; if (isValidAutoBrightnessAdjustment( mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) { screenAutoBrightnessAdjustment = mTemporaryScreenAutoBrightnessAdjustmentSettingOverride; } else if (isValidAutoBrightnessAdjustment( mScreenAutoBrightnessAdjustmentSetting)) { screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting; } } screenBrightness = Math.max(Math.min(screenBrightness, mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum); 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; if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager; mDisplayPowerRequest.dozeScreenBrightness = mDozeScreenBrightnessOverrideFromDreamManager; } else { mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT; } mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest, mRequestWaitForNegativeProximity); mRequestWaitForNegativeProximity = false; if (DEBUG_SPEW) { Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady + ", policy=" + mDisplayPowerRequest.policy + ", mWakefulness=" + mWakefulness + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary) + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary) + ", mBootCompleted=" + mBootCompleted + ", mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress); } } return mDisplayReady && !oldDisplayReady; }
發現最終值都被賦給
mDisplayPowerRequest
的屬性,然後呼叫mDisplayManagerInternal.requestPowerState
將值傳出返回結果。mDisplayManagerInternal
是DisplayManagerInternal(android.hardware.display.DisplayManagerInternal.class)
的物件,再來看看DisplayManagerInternal
:
4.DisplayManagerInternalpublic abstract boolean requestPowerState(DisplayPowerRequest request, boolean waitForNegativeProximity);
發現並沒有具體的實現方法。通過搜尋發現
DisplayManageService(com.android.server.display.DisplayManagerService.class)
中的LocalService
類繼承了DisplayManagerInternal
並實現了requestPowerState()
方法:@Override public boolean requestPowerState(DisplayPowerRequest request, boolean waitForNegativeProximity) { return mDisplayPowerController.requestPowerState(request, waitForNegativeProximity); }
發現呼叫了
DisplayPowerController(com.android.server.display.DisplayPowerController.class)
的requestPowerState()
方法作為返回結果。
5.DisplayPowerControllerpublic boolean requestPowerState(DisplayPowerRequest request, boolean waitForNegativeProximity) { if (DEBUG) { Slog.d(TAG, "requestPowerState: " + request + ", waitForNegativeProximity=" + waitForNegativeProximity); } 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(); } return mDisplayReadyLocked; } }
可以看出,先是將request物件賦給本地,然後呼叫
sendUpdatePowerStateLocked()
:private void sendUpdatePowerStateLocked() { if (!mPendingUpdatePowerStateLocked) { mPendingUpdatePowerStateLocked = true; Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE); msg.setAsynchronous(true); mHandler.sendMessage(msg); } }
交給handler處理:
case MSG_UPDATE_POWER_STATE: updatePowerState(); break;
呼叫
updatePowerState()
方法,其中有這樣一段程式碼:// 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); }
mAutomaticBrightnessController
是AutomaticBrightnessController(com.android.server.display.AutomaticBrightnessController.class)
物件,下面來看AutomaticBrightnessController
.
6.AutomaticBrightnessControllerpublic 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 setScreenAutoBrightnessAdjustment(float adjustment) { if (adjustment != mScreenAutoBrightnessAdjustment) { mScreenAutoBrightnessAdjustment = adjustment; return true; } return false; }
拿到亮度調節引數後:
private void updateAutoBrightness(boolean sendUpdate) { if (!mAmbientLuxValid) { return; } 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 (DEBUG) { Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma); } } if (USE_TWILIGHT_ADJUSTMENT) { TwilightState state = mTwilight.getCurrentState(); if (state != null && state.isNight()) { final long now = System.currentTimeMillis(); final float earlyGamma = getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise()); final float lateGamma = getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise()); gamma *= earlyGamma * lateGamma; if (DEBUG) { Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma + ", lateGamma=" + lateGamma); } } } if (gamma != 1.0f) { final float in = value; value = MathUtils.pow(value, gamma); if (DEBUG) { Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma + ", in=" + in + ", out=" + value); } } int newScreenAutoBrightness = clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON)); if (mScreenAutoBrightness != newScreenAutoBrightness) { if (DEBUG) { Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness=" + mScreenAutoBrightness + ", newScreenAutoBrightness=" + newScreenAutoBrightness); } mScreenAutoBrightness = newScreenAutoBrightness; mLastScreenAutoBrightnessGamma = gamma; if (sendUpdate) { mCallbacks.updateBrightness(); } } }
這裡是一大段針對自動亮度調節,將亮度調節係數和一段時間內的光線感應器獲取的光強的加權平均通過一系列運算轉化為合適的螢幕背光亮度,具體的轉化模型這裡不深究。
在這裡來看一下,每次轉化好亮度後都會做如下處理:
private int clampScreenBrightness(int value) {
return MathUtils.constrain(value,
mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
}
mScreenBrightnessRangeMinimum
就是最小值,由建構函式傳過來,發現是DisplayPowerController
例項化AutomaticBrightnessController
時傳入的:
mAutomaticBrightnessController = new AutomaticBrightnessController(this,
handler.getLooper(), sensorManager, screenAutoBrightnessSpline,
lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
mScreenBrightnessRangeMaximum, dozeScaleFactor);
在DisplayPowerController
的建構函式中:
int screenBrightnessRangeMinimum = Math.min(Math.min(
screenBrightnessSettingMinimum, mScreenBrightnessDimConfig),
mScreenBrightnessDarkConfig);
這三個值均取自config.xml(frameworks\base\core\res\res\values\config.xml)
分別為:
screenBrightnessSettingMinimum:config_screenBrightnessSettingMinimum
mScreenBrightnessDimConfig:config_screenBrig