1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.display; 18 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.database.ContentObserver; 22 import android.hardware.display.DisplayManager; 23 import android.hardware.display.DisplayManager.DisplayListener; 24 import android.net.Uri; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.os.PowerManager; 29 import android.os.UserHandle; 30 import android.provider.Settings; 31 import android.util.MathUtils; 32 import android.view.Display; 33 34 /** 35 * BrightnessSynchronizer helps convert between the int (old) system and float 36 * (new) system for storing the brightness. It has methods to convert between the two and also 37 * observes for when one of the settings is changed and syncs this with the other. 38 */ 39 public class BrightnessSynchronizer { 40 41 private static final int MSG_UPDATE_FLOAT = 1; 42 private static final int MSG_UPDATE_INT = 2; 43 private static final int MSG_UPDATE_BOTH = 3; 44 45 private static final String TAG = "BrightnessSynchronizer"; 46 private static final Uri BRIGHTNESS_URI = 47 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); 48 49 // The tolerance within which we consider brightness values approximately equal to eachother. 50 // This value is approximately 1/3 of the smallest possible brightness value. 51 public static final float EPSILON = 0.001f; 52 53 private DisplayManager mDisplayManager; 54 private final Context mContext; 55 56 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 57 @Override 58 public void handleMessage(Message msg) { 59 switch (msg.what) { 60 case MSG_UPDATE_FLOAT: 61 updateBrightnessFloatFromInt(msg.arg1); 62 break; 63 case MSG_UPDATE_INT: 64 updateBrightnessIntFromFloat(Float.intBitsToFloat(msg.arg1)); 65 break; 66 case MSG_UPDATE_BOTH: 67 updateBoth(Float.intBitsToFloat(msg.arg1)); 68 break; 69 default: 70 super.handleMessage(msg); 71 } 72 73 } 74 }; 75 76 private float mPreferredSettingValue; 77 BrightnessSynchronizer(Context context)78 public BrightnessSynchronizer(Context context) { 79 mContext = context; 80 } 81 82 /** 83 * Starts brightnessSyncObserver to ensure that the float and int brightness values stay 84 * in sync. 85 * This also ensures that values are synchronized at system start up too. 86 * So we force an update to the int value, since float is the source of truth. Fallback to int 87 * value, if float is invalid. If both are invalid, use default float value from config. 88 */ startSynchronizing()89 public void startSynchronizing() { 90 if (mDisplayManager == null) { 91 mDisplayManager = mContext.getSystemService(DisplayManager.class); 92 } 93 94 final BrightnessSyncObserver brightnessSyncObserver; 95 brightnessSyncObserver = new BrightnessSyncObserver(); 96 brightnessSyncObserver.startObserving(); 97 98 final float currentFloatBrightness = getScreenBrightnessFloat(); 99 final int currentIntBrightness = getScreenBrightnessInt(mContext); 100 101 if (!Float.isNaN(currentFloatBrightness)) { 102 updateBrightnessIntFromFloat(currentFloatBrightness); 103 } else if (currentIntBrightness != -1) { 104 updateBrightnessFloatFromInt(currentIntBrightness); 105 } else { 106 final float defaultBrightness = mContext.getResources().getFloat( 107 com.android.internal.R.dimen.config_screenBrightnessSettingDefaultFloat); 108 mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, defaultBrightness); 109 110 } 111 } 112 113 /** 114 * Converts between the int brightness system and the float brightness system. 115 */ brightnessIntToFloat(int brightnessInt)116 public static float brightnessIntToFloat(int brightnessInt) { 117 if (brightnessInt == PowerManager.BRIGHTNESS_OFF) { 118 return PowerManager.BRIGHTNESS_OFF_FLOAT; 119 } else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) { 120 return PowerManager.BRIGHTNESS_INVALID_FLOAT; 121 } else { 122 final float minFloat = PowerManager.BRIGHTNESS_MIN; 123 final float maxFloat = PowerManager.BRIGHTNESS_MAX; 124 final float minInt = PowerManager.BRIGHTNESS_OFF + 1; 125 final float maxInt = PowerManager.BRIGHTNESS_ON; 126 return MathUtils.constrainedMap(minFloat, maxFloat, minInt, maxInt, brightnessInt); 127 } 128 } 129 130 /** 131 * Converts between the float brightness system and the int brightness system. 132 */ brightnessFloatToInt(float brightnessFloat)133 public static int brightnessFloatToInt(float brightnessFloat) { 134 return Math.round(brightnessFloatToIntRange(brightnessFloat)); 135 } 136 137 /** 138 * Translates specified value from the float brightness system to the int brightness system, 139 * given the min/max of each range. Accounts for special values such as OFF and invalid values. 140 * Value returned as a float primitive (to preserve precision), but is a value within the 141 * int-system range. 142 */ brightnessFloatToIntRange(float brightnessFloat)143 public static float brightnessFloatToIntRange(float brightnessFloat) { 144 if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) { 145 return PowerManager.BRIGHTNESS_OFF; 146 } else if (Float.isNaN(brightnessFloat)) { 147 return PowerManager.BRIGHTNESS_INVALID; 148 } else { 149 final float minFloat = PowerManager.BRIGHTNESS_MIN; 150 final float maxFloat = PowerManager.BRIGHTNESS_MAX; 151 final float minInt = PowerManager.BRIGHTNESS_OFF + 1; 152 final float maxInt = PowerManager.BRIGHTNESS_ON; 153 return MathUtils.constrainedMap(minInt, maxInt, minFloat, maxFloat, brightnessFloat); 154 } 155 } 156 157 /** 158 * Gets the stored screen brightness float value from the display brightness setting. 159 * @return brightness 160 */ getScreenBrightnessFloat()161 private float getScreenBrightnessFloat() { 162 return mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY); 163 } 164 165 /** 166 * Gets the stored screen brightness int from the system settings. 167 * @param context for accessing settings 168 * 169 * @return brightness 170 */ getScreenBrightnessInt(Context context)171 private static int getScreenBrightnessInt(Context context) { 172 return Settings.System.getIntForUser(context.getContentResolver(), 173 Settings.System.SCREEN_BRIGHTNESS, PowerManager.BRIGHTNESS_INVALID, 174 UserHandle.USER_CURRENT); 175 } 176 177 /** 178 * Updates the settings based on a passed in int value. This is called whenever the int 179 * setting changes. mPreferredSettingValue holds the most recently updated brightness value 180 * as a float that we would like the display to be set to. 181 * 182 * We then schedule an update to both the int and float settings, but, remove all the other 183 * messages to update all, to prevent us getting stuck in a loop. 184 * 185 * @param value Brightness value as int to store in the float setting. 186 */ updateBrightnessFloatFromInt(int value)187 private void updateBrightnessFloatFromInt(int value) { 188 if (brightnessFloatToInt(mPreferredSettingValue) == value) { 189 return; 190 } 191 192 mPreferredSettingValue = brightnessIntToFloat(value); 193 final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue); 194 mHandler.removeMessages(MSG_UPDATE_BOTH); 195 mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget(); 196 } 197 198 /** 199 * Updates the settings based on a passed in float value. This is called whenever the float 200 * setting changes. mPreferredSettingValue holds the most recently updated brightness value 201 * as a float that we would like the display to be set to. 202 * 203 * We then schedule an update to both the int and float settings, but, remove all the other 204 * messages to update all, to prevent us getting stuck in a loop. 205 * 206 * @param value Brightness setting as float to store in int setting. 207 */ updateBrightnessIntFromFloat(float value)208 private void updateBrightnessIntFromFloat(float value) { 209 if (floatEquals(mPreferredSettingValue, value)) { 210 return; 211 } 212 213 mPreferredSettingValue = value; 214 final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue); 215 mHandler.removeMessages(MSG_UPDATE_BOTH); 216 mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget(); 217 } 218 219 220 /** 221 * Updates both setting values if they have changed 222 * mDisplayManager.setBrightness automatically checks for changes 223 * Settings.System.putIntForUser needs to be checked, to prevent an extra callback to this class 224 * 225 * @param newBrightnessFloat Brightness setting as float to store in both settings 226 */ updateBoth(float newBrightnessFloat)227 private void updateBoth(float newBrightnessFloat) { 228 int newBrightnessInt = brightnessFloatToInt(newBrightnessFloat); 229 mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat); 230 if (getScreenBrightnessInt(mContext) != newBrightnessInt) { 231 Settings.System.putIntForUser(mContext.getContentResolver(), 232 Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT); 233 } 234 235 } 236 237 /** 238 * Tests whether two brightness float values are within a small enough tolerance 239 * of each other. 240 * @param a first float to compare 241 * @param b second float to compare 242 * @return whether the two values are within a small enough tolerance value 243 */ floatEquals(float a, float b)244 public static boolean floatEquals(float a, float b) { 245 if (a == b) { 246 return true; 247 } else if (Float.isNaN(a) && Float.isNaN(b)) { 248 return true; 249 } else if (Math.abs(a - b) < EPSILON) { 250 return true; 251 } else { 252 return false; 253 } 254 } 255 256 private class BrightnessSyncObserver { 257 private final DisplayListener mListener = new DisplayListener() { 258 @Override 259 public void onDisplayAdded(int displayId) {} 260 261 @Override 262 public void onDisplayRemoved(int displayId) {} 263 264 @Override 265 public void onDisplayChanged(int displayId) { 266 float currentFloat = getScreenBrightnessFloat(); 267 int toSend = Float.floatToIntBits(currentFloat); 268 mHandler.removeMessages(MSG_UPDATE_INT); 269 mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget(); 270 } 271 }; 272 273 private final ContentObserver mContentObserver = new ContentObserver(mHandler) { 274 @Override 275 public void onChange(boolean selfChange, Uri uri) { 276 if (selfChange) { 277 return; 278 } 279 if (BRIGHTNESS_URI.equals(uri)) { 280 int currentBrightness = getScreenBrightnessInt(mContext); 281 mHandler.removeMessages(MSG_UPDATE_FLOAT); 282 mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget(); 283 } 284 } 285 }; 286 startObserving()287 public void startObserving() { 288 final ContentResolver cr = mContext.getContentResolver(); 289 cr.unregisterContentObserver(mContentObserver); 290 cr.registerContentObserver(BRIGHTNESS_URI, false, mContentObserver, 291 UserHandle.USER_ALL); 292 293 mDisplayManager.registerDisplayListener(mListener, mHandler, 294 DisplayManager.EVENT_FLAG_DISPLAY_CHANGED 295 | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS); 296 } 297 stopObserving()298 public void stopObserving() { 299 final ContentResolver cr = mContext.getContentResolver(); 300 cr.unregisterContentObserver(mContentObserver); 301 mDisplayManager.unregisterDisplayListener(mListener); 302 } 303 } 304 } 305