平台
RK3399 + Android 9.0
概述
众所周知, Android 平台的上层亮度值的设置值范围为[0, 255], 在9.0之前, 亮度调节一直是线性变化, 在9.0开始, 它变成了曲线变化, 如下图:
图1-实际亮度值: 11
图2-实际亮度值: 15
图3-实际亮度值: 33
图4-实际亮度值: 105
图5-实际亮度值: 255
其中图3最直观体现与线性变化的区别, 进度条在50%以上, 那么实际亮度值应该接近122.
流程
亮度调节有两个常见的入口: 设置 和 状态栏.
设置
packages/Settings/AndroidManifest.xml
<activity android:name="Settings$DisplaySettingsActivity" android:label="@string/display_settings" android:icon="@drawable/ic_homepage_display" android:taskAffinity="com.android.settings" android:parentActivityName="Settings"> <intent-filter android:priority="1"> <action android:name="com.android.settings.DISPLAY_SETTINGS" /> <action android:name="android.settings.DISPLAY_SETTINGS" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.VOICE_LAUNCH" /> <category android:name="com.android.settings.SHORTCUT" /> </intent-filter> <intent-filter android:priority="7"> <action android:name="com.android.settings.action.SETTINGS" /> </intent-filter> <meta-data android:name="com.android.settings.category" android:value="com.android.settings.category.ia.homepage" /> <meta-data android:name="com.android.settings.FRAGMENT_CLASS" android:value="com.android.settings.DisplaySettings" /> <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED" android:value="true" /> <meta-data android:name="com.android.settings.summary" android:resource="@string/display_dashboard_summary"/> </activity> <!-- Keep compatibility with old shortcuts. --> <activity-alias android:name="DisplaySettings" android:label="@string/display_settings" android:exported="true" android:targetActivity="Settings$DisplaySettingsActivity"> <meta-data android:name="com.android.settings.FRAGMENT_CLASS" android:value="com.android.settings.DisplaySettings" /> </activity-alias>
packages/apps/Settings/src/com/android/settings/DisplaySettings.java
public class DisplaySettings extends DashboardFragment { private static final String TAG = "DisplaySettings"; public static final String KEY_DISPLAY_SIZE = "display_settings_screen_zoom"; private static final String KEY_SCREEN_TIMEOUT = "screen_timeout"; private static final String KEY_AMBIENT_DISPLAY = "ambient_display"; private static final String KEY_AUTO_BRIGHTNESS = "auto_brightness_entry"; private static final String KEY_NIGHT_DISPLAY = "night_display"; private static final String KET_HDMI_SETTINGS = "hdmi_settings"; @Override public int getMetricsCategory() { return MetricsEvent.DISPLAY; } @Override protected String getLogTag() { return TAG; } @Override protected int getPreferenceScreenResId() { return R.xml.display_settings; } //... }
packages/apps/Settings/res/xml/display_settings.xml
<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2016 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto" android:key="display_settings_screen" android:title="@string/display_settings" settings:keywords="@string/keywords_display" settings:initialExpandedChildrenCount="4"> <com.android.settingslib.RestrictedPreference android:key="brightness" android:title="@string/brightness" settings:keywords="@string/keywords_display_brightness_level" settings:useAdminDisabledSummary="true" settings:userRestriction="no_config_brightness"> <intent android:action="com.android.intent.action.SHOW_BRIGHTNESS_DIALOG" /> </com.android.settingslib.RestrictedPreference> ....
当点击亮度设置后, 会打开亮度调节窗口:
com.android.intent.action.SHOW_BRIGHTNESS_DIALOG
参照下面状态栏部分
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.QuickSettings.Dialog" android:finishOnCloseSystemDialogs="true" android:launchMode="singleInstance" android:excludeFromRecents="true" android:exported="true"> <intent-filter> <action android:name="com.android.intent.action.SHOW_BRIGHTNESS_DIALOG" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
状态栏
frameworks/base/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.settings; import android.app.Activity; import android.os.Bundle; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.ImageView; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; /** A dialog that provides controls for adjusting the screen brightness. */ public class BrightnessDialog extends Activity { private BrightnessController mBrightnessController; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Window window = getWindow(); window.setGravity(Gravity.TOP); window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); window.requestFeature(Window.FEATURE_NO_TITLE); // Use a dialog theme as the activity theme, but inflate the content as // the QS content. ContextThemeWrapper themedContext = new ContextThemeWrapper(this, com.android.internal.R.style.Theme_DeviceDefault_QuickSettings); View v = LayoutInflater.from(themedContext).inflate( R.layout.quick_settings_brightness_dialog, null); setContentView(v); final ImageView icon = findViewById(R.id.brightness_icon); final ToggleSliderView slider = findViewById(R.id.brightness_slider); mBrightnessController = new BrightnessController(this, icon, slider); } @Override protected void onStart() { super.onStart(); mBrightnessController.registerCallbacks(); MetricsLogger.visible(this, MetricsEvent.BRIGHTNESS_DIALOG); } @Override protected void onStop() { super.onStop(); MetricsLogger.hidden(this, MetricsEvent.BRIGHTNESS_DIALOG); mBrightnessController.unregisterCallbacks(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) { finish(); } return super.onKeyDown(keyCode, event); } }
UI部分主要就一个ToggleSliderView, 主要的一些逻辑操作都放在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 (mSliderAnimator != null) { mSliderAnimator.cancel(); } final int min; final int max; final int metric; final String setting; if (mIsVrModeEnabled) { metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR; min = mMinimumBacklightForVr; max = mMaximumBacklightForVr; setting = Settings.System.SCREEN_BRIGHTNESS_FOR_VR; } else { metric = mAutomatic ? MetricsEvent.ACTION_BRIGHTNESS_AUTO : MetricsEvent.ACTION_BRIGHTNESS; min = mMinimumBacklight; max = mMaximumBacklight; setting = Settings.System.SCREEN_BRIGHTNESS; } final int val = convertGammaToLinear(value, min, max); if (stopTracking) { MetricsLogger.action(mContext, metric, val); } setBrightness(val); if (!tracking) { AsyncTask.execute(new Runnable() { public void run() { Settings.System.putIntForUser(mContext.getContentResolver(), setting, val, UserHandle.USER_CURRENT); } }); } for (BrightnessStateChangeCallback cb : mChangeCallbacks) { cb.onBrightnessLevelChanged(); } }
转换曲线: convertGammaToLinear(value, min, max); 和 convertLinearToGamma(int val, int min, int max)
frameworks/base/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java
/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settingslib.display; import android.util.MathUtils; public class BrightnessUtils { public static final int GAMMA_SPACE_MAX = 1023; public static final boolean ENABLE_GAMMA = false; // Hybrid Log Gamma constant values private static final float R = 0.5f; private static final float A = 0.17883277f; private static final float B = 0.28466892f; private static final float C = 0.55991073f; /** * A function for converting from the gamma space that the slider works in to the * linear space that the setting works in. * * The gamma space effectively provides us a way to make linear changes to the slider that * result in linear changes in perception. If we made changes to the slider in the linear space * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law). * * Internally, this implements the Hybrid Log Gamma electro-optical transfer function, which is * a slight improvement to the typical gamma transfer function for displays whose max * brightness exceeds the 120 nit reference point, but doesn't set a specific reference * brightness like the PQ function does. * * Note that this transfer function is only valid if the display's backlight value is a linear * control. If it's calibrated to be something non-linear, then a different transfer function * should be used. * * @param val The slider value. * @param min The minimum acceptable value for the setting. * @param max The maximum acceptable value for the setting. * @return The corresponding setting value. */ public static final int convertGammaToLinear(int val, int min, int max) { final float normalizedVal = MathUtils.norm(0, GAMMA_SPACE_MAX, val); final float ret; if (normalizedVal <= R) { ret = MathUtils.sq(normalizedVal / R); } else { ret = MathUtils.exp((normalizedVal - C) / A) + B; } // HLG is normalized to the range [0, 12], so we need to re-normalize to the range [0, 1] // in order to derive the correct setting value. return Math.round(MathUtils.lerp(min, max, ret / 12)); } /** * A function for converting from the linear space that the setting works in to the * gamma space that the slider works in. * * The gamma space effectively provides us a way to make linear changes to the slider that * result in linear changes in perception. If we made changes to the slider in the linear space * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law). * * Internally, this implements the Hybrid Log Gamma opto-electronic transfer function, which is * a slight improvement to the typical gamma transfer function for displays whose max * brightness exceeds the 120 nit reference point, but doesn't set a specific reference * brightness like the PQ function does. * * Note that this transfer function is only valid if the display's backlight value is a linear * control. If it's calibrated to be something non-linear, then a different transfer function * should be used. * * @param val The brightness setting value. * @param min The minimum acceptable value for the setting. * @param max The maximum acceptable value for the setting. * @return The corresponding slider value */ public static final int convertLinearToGamma(int val, int min, int max) { // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1] final float normalizedVal = MathUtils.norm(min, max, val) * 12; final float ret; if (normalizedVal <= 1f) { ret = MathUtils.sqrt(normalizedVal) * R; } else { ret = A * MathUtils.log(normalizedVal - B) + C; } return Math.round(MathUtils.lerp(0, GAMMA_SPACE_MAX, ret)); } }
设置/写入:
进度条进度 -> convertGammaToLinear -> Settings.System.put
读取
Settings.System.get -> convertLinearToGamma -> 设置进度条进度
去除曲线功能
增加ENABLE_GAMMA控制
frameworks/base/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java
@@ -21,6 +21,7 @@ import android.util.MathUtils; public class BrightnessUtils { public static final int GAMMA_SPACE_MAX = 1023; + public static final boolean ENABLE_GAMMA = false; // Hybrid Log Gamma constant values private static final float R = 0.5f; @@ -51,6 +52,11 @@ public class BrightnessUtils { * @return The corresponding setting value. */ public static final int convertGammaToLinear(int val, int min, int max) { + if(!ENABLE_GAMMA){ + float ratio = (float)val / GAMMA_SPACE_MAX; + return min + (int)(ratio * (max - min)); + } + final float normalizedVal = MathUtils.norm(0, GAMMA_SPACE_MAX, val); final float ret; if (normalizedVal <= R) { @@ -87,6 +93,11 @@ public class BrightnessUtils { * @return The corresponding slider value */ public static final int convertLinearToGamma(int val, int min, int max) { + if(!ENABLE_GAMMA){ + float ratio = (float)(val - min)/ (max - min); + return (int)(ratio * GAMMA_SPACE_MAX); + } +
编译相关模块, 如: SettingsLib, Settings, SystemUI, 重新make install clean 和 make
扩展
编译失败:
mmm frameworks/base/packages/SystemUI/ -j2
============================================ PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=9 TARGET_PRODUCT=rk3399 TARGET_BUILD_VARIANT=userdebug TARGET_BUILD_TYPE=release TARGET_ARCH=arm64 TARGET_ARCH_VARIANT=armv8-a TARGET_CPU_VARIANT=cortex-a53 TARGET_2ND_ARCH=arm TARGET_2ND_ARCH_VARIANT=armv7-a-neon TARGET_2ND_CPU_VARIANT=cortex-a15 HOST_ARCH=x86_64 HOST_2ND_ARCH=x86 HOST_OS=linux HOST_OS_EXTRA=Linux-4.4.0-142-generic-x86_64-Ubuntu-16.04.6-LTS HOST_CROSS_OS=windows HOST_CROSS_ARCH=x86 HOST_CROSS_2ND_ARCH=x86_64 HOST_BUILD_TYPE=release BUILD_ID=PI OUT_DIR=out ============================================ ninja: no work to do. ninja: no work to do. out/build-rk3399-_frameworks_base_packages_SystemUI_Android.mk-cleanspec.ninja is missing, regenerating... out/build-rk3399-_frameworks_base_packages_SystemUI_Android.mk.ninja is missing, regenerating... ninja: error: 'out/target/common/obj/JAVA_LIBRARIES/metrics-helper-lib_intermediates/classes-header.jar', needed by 'out/target/common/obj/APPS/SystemUISharedLibTests_intermediates/classes-full-debug.jar', missing and no known rule to make it 14:37:27 ninja failed with: exit status 1 #### failed to build some targets (59 seconds) ####
原因: 单编有依赖, 改用 mmma
注意加上LOG打印, 以验证修改生效