1 /* 2 * Copyright (C) 2016 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.car.systeminterface; 18 19 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING; 20 import static android.view.Display.DEFAULT_DISPLAY; 21 22 import static com.android.car.CarServiceUtils.getContentResolverForUser; 23 import static com.android.car.CarServiceUtils.isEventOfType; 24 import static com.android.car.internal.util.VersionUtils.isPlatformVersionAtLeastU; 25 import static com.android.car.util.BrightnessUtils.GAMMA_SPACE_MAX; 26 import static com.android.car.util.BrightnessUtils.convertGammaToLinear; 27 import static com.android.car.util.BrightnessUtils.convertLinearToGamma; 28 29 import android.annotation.RequiresApi; 30 import android.car.builtin.display.DisplayManagerHelper; 31 import android.car.builtin.os.UserManagerHelper; 32 import android.car.builtin.power.PowerManagerHelper; 33 import android.car.builtin.util.Slogf; 34 import android.car.user.CarUserManager.UserLifecycleListener; 35 import android.car.user.UserLifecycleEventFilter; 36 import android.content.Context; 37 import android.database.ContentObserver; 38 import android.hardware.display.DisplayManager; 39 import android.hardware.display.DisplayManager.DisplayListener; 40 import android.os.Build; 41 import android.os.Handler; 42 import android.os.Looper; 43 import android.os.SystemClock; 44 import android.os.UserHandle; 45 import android.os.UserManager; 46 import android.provider.Settings.SettingNotFoundException; 47 import android.provider.Settings.System; 48 import android.util.Log; 49 import android.util.SparseBooleanArray; 50 import android.util.SparseIntArray; 51 import android.view.Display; 52 53 import com.android.car.CarLog; 54 import com.android.car.internal.util.IntArray; 55 import com.android.car.power.CarPowerManagementService; 56 import com.android.car.user.CarUserService; 57 import com.android.car.util.BrightnessUtils; 58 import com.android.internal.annotations.GuardedBy; 59 60 /** 61 * Interface that abstracts display operations 62 */ 63 public interface DisplayInterface { 64 65 /** 66 * Sets the required services. 67 * 68 * @param carPowerManagementService {@link CarPowerManagementService} to listen to car power 69 * management changes 70 * @param carUserService {@link CarUserService} to listen to service life cycle 71 * changes 72 */ init(CarPowerManagementService carPowerManagementService, CarUserService carUserService)73 void init(CarPowerManagementService carPowerManagementService, CarUserService carUserService); 74 75 /** 76 * Sets display brightness. 77 * 78 * @param brightness Level from 0 to 100% 79 */ setDisplayBrightness(int brightness)80 void setDisplayBrightness(int brightness); 81 82 /** 83 * Sets display brightness with the given displayId. 84 * 85 * @param displayId ID of a display. 86 * @param brightness Level from 0 to 100. 87 */ setDisplayBrightness(int displayId, int brightness)88 void setDisplayBrightness(int displayId, int brightness); 89 90 /** 91 * Turns on or off display with the given displayId. 92 * 93 * @param displayId ID of a display. 94 * @param on {@code true} to turn on, {@code false} to turn off. 95 */ setDisplayState(int displayId, boolean on)96 void setDisplayState(int displayId, boolean on); 97 98 /** 99 * Turns on or off all displays. 100 * 101 * @param on {@code true} to turn on, {@code false} to turn off. 102 */ setAllDisplayState(boolean on)103 void setAllDisplayState(boolean on); 104 105 /** 106 * Starts monitoring the display state change. 107 * <p> When there is a change, {@link CarPowerManagementService} is notified. 108 */ startDisplayStateMonitoring()109 void startDisplayStateMonitoring(); 110 111 /** 112 * Stops monitoring the display state change. 113 */ stopDisplayStateMonitoring()114 void stopDisplayStateMonitoring(); 115 116 /** 117 * Gets the current on/off state of displays. 118 * 119 * @return {@code true}, if any display is turned on. Otherwise, {@code false}. 120 */ isAnyDisplayEnabled()121 boolean isAnyDisplayEnabled(); 122 123 /** 124 * Gets the current on/off state of display with the given displayId. 125 * 126 * @param displayId ID of a display. 127 */ isDisplayEnabled(int displayId)128 boolean isDisplayEnabled(int displayId); 129 130 /** 131 * Refreshing display brightness. Used when user is switching and car turned on. 132 */ refreshDisplayBrightness()133 void refreshDisplayBrightness(); 134 135 /** 136 * Refreshing display brightness with the given displayId. 137 * Used when brightness change is observed. 138 * 139 * @param displayId ID of a display. 140 */ refreshDisplayBrightness(int displayId)141 void refreshDisplayBrightness(int displayId); 142 143 /** 144 * Default implementation of display operations 145 */ 146 class DefaultImpl implements DisplayInterface { 147 private static final String TAG = DisplayInterface.class.getSimpleName(); 148 private static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG); 149 private static final int INVALID_DISPLAY_BRIGHTNESS = -1; 150 151 private final Context mContext; 152 private final DisplayManager mDisplayManager; 153 private final Object mLock = new Object(); 154 private final int mMaximumBacklight; 155 private final int mMinimumBacklight; 156 private final WakeLockInterface mWakeLockInterface; 157 @GuardedBy("mLock") 158 private CarPowerManagementService mCarPowerManagementService; 159 @GuardedBy("mLock") 160 private CarUserService mCarUserService; 161 @GuardedBy("mLock") 162 private final SparseBooleanArray mDisplayStateSet = new SparseBooleanArray(); 163 @GuardedBy("mLock") 164 private final SparseIntArray mDisplayBrightnessSet = new SparseIntArray(); 165 private final UserManager mUserManager; 166 167 private final ContentObserver mBrightnessObserver = 168 new ContentObserver(new Handler(Looper.getMainLooper())) { 169 @Override 170 public void onChange(boolean selfChange) { 171 Slogf.i(TAG, "Brightness change from Settings: selfChange=%b", selfChange); 172 refreshDisplayBrightness(); 173 } 174 }; 175 176 private final DisplayManager.DisplayListener mDisplayListener = new DisplayListener() { 177 @Override 178 public void onDisplayAdded(int displayId) { 179 synchronized (mLock) { 180 mDisplayStateSet.put(displayId, isDisplayOn(displayId)); 181 mDisplayBrightnessSet.put(displayId, INVALID_DISPLAY_BRIGHTNESS); 182 } 183 } 184 185 @Override 186 public void onDisplayRemoved(int displayId) { 187 synchronized (mLock) { 188 mDisplayStateSet.delete(displayId); 189 mDisplayBrightnessSet.delete(displayId); 190 } 191 } 192 193 @Override 194 public void onDisplayChanged(int displayId) { 195 handleDisplayChanged(displayId); 196 } 197 }; 198 DefaultImpl(Context context, WakeLockInterface wakeLockInterface)199 DefaultImpl(Context context, WakeLockInterface wakeLockInterface) { 200 mContext = context; 201 mDisplayManager = context.getSystemService(DisplayManager.class); 202 mMaximumBacklight = PowerManagerHelper.getMaximumScreenBrightnessSetting(context); 203 mMinimumBacklight = PowerManagerHelper.getMinimumScreenBrightnessSetting(context); 204 mWakeLockInterface = wakeLockInterface; 205 synchronized (mLock) { 206 for (Display display : mDisplayManager.getDisplays()) { 207 int displayId = display.getDisplayId(); 208 mDisplayStateSet.put(displayId, isDisplayOn(displayId)); 209 mDisplayBrightnessSet.put(displayId, INVALID_DISPLAY_BRIGHTNESS); 210 } 211 } 212 mUserManager = context.getSystemService(UserManager.class); 213 } 214 215 private final UserLifecycleListener mUserLifecycleListener = event -> { 216 if (!isEventOfType(TAG, event, USER_LIFECYCLE_EVENT_TYPE_SWITCHING)) { 217 return; 218 } 219 if (DEBUG) { 220 Slogf.d(TAG, "DisplayInterface.DefaultImpl.onEvent(%s)", event); 221 } 222 223 onUsersUpdate(); 224 }; 225 226 @Override refreshDisplayBrightness()227 public void refreshDisplayBrightness() { 228 refreshDisplayBrightness(DEFAULT_DISPLAY); 229 } 230 231 @Override refreshDisplayBrightness(int displayId)232 public void refreshDisplayBrightness(int displayId) { 233 CarPowerManagementService carPowerManagementService = null; 234 synchronized (mLock) { 235 carPowerManagementService = mCarPowerManagementService; 236 } 237 if (carPowerManagementService == null) { 238 Slogf.e(CarLog.TAG_POWER, "Could not set brightness: " 239 + "no CarPowerManagementService"); 240 return; 241 } 242 if (isPlatformVersionAtLeastU() 243 && UserManagerHelper.isVisibleBackgroundUsersSupported(mUserManager)) { 244 refreshDisplayBrightnessFromDisplay(carPowerManagementService, displayId); 245 } else { 246 refreshDisplayBrigtnessFromSetting(carPowerManagementService); 247 } 248 } 249 250 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) refreshDisplayBrightnessFromDisplay( CarPowerManagementService carPowerManagementService, int displayId)251 private void refreshDisplayBrightnessFromDisplay( 252 CarPowerManagementService carPowerManagementService, int displayId) { 253 int linear = BrightnessUtils.brightnessFloatToInt( 254 DisplayManagerHelper.getBrightness(mContext, displayId)); 255 int gamma = convertLinearToGamma(linear, mMinimumBacklight, mMaximumBacklight); 256 int percentBright = convertGammaToPercentBright(gamma); 257 Slogf.i(TAG, "Refreshing percent brightness(from display %d) to %d", displayId, 258 percentBright); 259 carPowerManagementService.sendDisplayBrightness(displayId, percentBright); 260 } 261 refreshDisplayBrigtnessFromSetting( CarPowerManagementService carPowerManagementService)262 private void refreshDisplayBrigtnessFromSetting( 263 CarPowerManagementService carPowerManagementService) { 264 int gamma = GAMMA_SPACE_MAX; 265 try { 266 int linear = System.getInt(getContentResolverForUser(mContext, 267 UserHandle.CURRENT.getIdentifier()), System.SCREEN_BRIGHTNESS); 268 gamma = convertLinearToGamma(linear, mMinimumBacklight, mMaximumBacklight); 269 } catch (SettingNotFoundException e) { 270 Slogf.e(CarLog.TAG_POWER, "Could not get SCREEN_BRIGHTNESS: ", e); 271 } 272 int percentBright = convertGammaToPercentBright(gamma); 273 Slogf.i(TAG, "Refreshing percent brightness(from Setting) to %d", percentBright); 274 carPowerManagementService.sendDisplayBrightness(percentBright); 275 } 276 convertGammaToPercentBright(int gamma)277 private static int convertGammaToPercentBright(int gamma) { 278 return (gamma * 100 + ((GAMMA_SPACE_MAX + 1) / 2)) / GAMMA_SPACE_MAX; 279 } 280 handleDisplayChanged(int displayId)281 private void handleDisplayChanged(int displayId) { 282 refreshDisplayBrightness(displayId); 283 boolean isOn = isDisplayOn(displayId); 284 CarPowerManagementService service; 285 synchronized (mLock) { 286 boolean state = mDisplayStateSet.get(displayId, false); 287 if (state == isOn) { // same as what is set 288 return; 289 } 290 service = mCarPowerManagementService; 291 } 292 service.handleDisplayChanged(displayId, isOn); 293 } 294 isDisplayOn(int displayId)295 private boolean isDisplayOn(int displayId) { 296 Display disp = mDisplayManager.getDisplay(displayId); 297 if (disp == null) { 298 return false; 299 } 300 return disp.getState() == Display.STATE_ON; 301 } 302 303 @Override setDisplayBrightness(int percentBright)304 public void setDisplayBrightness(int percentBright) { 305 setDisplayBrightness(DEFAULT_DISPLAY, percentBright); 306 } 307 308 @Override setDisplayBrightness(int displayId, int percentBright)309 public void setDisplayBrightness(int displayId, int percentBright) { 310 synchronized (mLock) { 311 if (percentBright == mDisplayBrightnessSet.get(displayId)) { 312 // We have already set the value last time. Skipping 313 return; 314 } 315 mDisplayBrightnessSet.put(displayId, percentBright); 316 } 317 int gamma = (percentBright * GAMMA_SPACE_MAX + 50) / 100; 318 int linear = convertGammaToLinear(gamma, mMinimumBacklight, mMaximumBacklight); 319 if (isPlatformVersionAtLeastU() 320 && UserManagerHelper.isVisibleBackgroundUsersSupported(mUserManager)) { 321 DisplayManagerHelper.setBrightness(mContext, displayId, 322 BrightnessUtils.brightnessIntToFloat(linear)); 323 } else { 324 System.putInt( 325 getContentResolverForUser(mContext, UserHandle.CURRENT.getIdentifier()), 326 System.SCREEN_BRIGHTNESS, 327 linear); 328 } 329 } 330 331 @Override init(CarPowerManagementService carPowerManagementService, CarUserService carUserService)332 public void init(CarPowerManagementService carPowerManagementService, 333 CarUserService carUserService) { 334 synchronized (mLock) { 335 mCarPowerManagementService = carPowerManagementService; 336 mCarUserService = carUserService; 337 } 338 } 339 340 @Override startDisplayStateMonitoring()341 public void startDisplayStateMonitoring() { 342 CarPowerManagementService carPowerManagementService; 343 CarUserService carUserService; 344 synchronized (mLock) { 345 carPowerManagementService = mCarPowerManagementService; 346 carUserService = mCarUserService; 347 } 348 UserLifecycleEventFilter userSwitchingEventFilter = 349 new UserLifecycleEventFilter.Builder() 350 .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING).build(); 351 carUserService.addUserLifecycleListener(userSwitchingEventFilter, 352 mUserLifecycleListener); 353 if (isPlatformVersionAtLeastU() 354 && UserManagerHelper.isVisibleBackgroundUsersSupported(mUserManager)) { 355 DisplayManagerHelper.registerDisplayListener(mContext, mDisplayListener, 356 carPowerManagementService.getHandler(), 357 DisplayManagerHelper.EVENT_FLAG_DISPLAY_ADDED 358 | DisplayManagerHelper.EVENT_FLAG_DISPLAY_REMOVED 359 | DisplayManagerHelper.EVENT_FLAG_DISPLAY_CHANGED 360 | DisplayManagerHelper.EVENT_FLAG_DISPLAY_BRIGHTNESS); 361 } else { 362 getContentResolverForUser(mContext, UserHandle.ALL.getIdentifier()) 363 .registerContentObserver(System.getUriFor(System.SCREEN_BRIGHTNESS), 364 false, 365 mBrightnessObserver); 366 } 367 368 for (Display display : mDisplayManager.getDisplays()) { 369 int displayId = display.getDisplayId(); 370 refreshDisplayBrightness(displayId); 371 } 372 } 373 374 @Override stopDisplayStateMonitoring()375 public void stopDisplayStateMonitoring() { 376 CarUserService carUserService; 377 synchronized (mLock) { 378 carUserService = mCarUserService; 379 } 380 carUserService.removeUserLifecycleListener(mUserLifecycleListener); 381 if (isPlatformVersionAtLeastU() 382 && UserManagerHelper.isVisibleBackgroundUsersSupported(mUserManager)) { 383 mDisplayManager.unregisterDisplayListener(mDisplayListener); 384 } else { 385 getContentResolverForUser(mContext, UserHandle.ALL.getIdentifier()) 386 .unregisterContentObserver(mBrightnessObserver); 387 } 388 } 389 390 @Override setDisplayState(int displayId, boolean on)391 public void setDisplayState(int displayId, boolean on) { 392 CarPowerManagementService carPowerManagementService; 393 synchronized (mLock) { 394 carPowerManagementService = mCarPowerManagementService; 395 if (on && carPowerManagementService != null 396 && !carPowerManagementService.canTurnOnDisplay(displayId)) { 397 Slogf.i(CarLog.TAG_POWER, "ignore turning on display %d because " 398 + "CarPowerManagementService doesn't support it", displayId); 399 return; 400 } 401 mDisplayStateSet.put(displayId, on); 402 } 403 if (on) { 404 mWakeLockInterface.switchToFullWakeLock(displayId); 405 Slogf.i(CarLog.TAG_POWER, "on display %d", displayId); 406 } else { 407 mWakeLockInterface.switchToPartialWakeLock(displayId); 408 Slogf.i(CarLog.TAG_POWER, "off display %d", displayId); 409 if (isPlatformVersionAtLeastU()) { 410 PowerManagerHelper.goToSleep(mContext, displayId, SystemClock.uptimeMillis()); 411 } else { 412 PowerManagerHelper.setDisplayState(mContext, /* on= */ false, 413 SystemClock.uptimeMillis()); 414 } 415 } 416 if (carPowerManagementService != null) { 417 carPowerManagementService.handleDisplayChanged(displayId, on); 418 } 419 } 420 421 @Override setAllDisplayState(boolean on)422 public void setAllDisplayState(boolean on) { 423 IntArray displayIds = new IntArray(); 424 synchronized (mLock) { 425 for (int i = 0; i < mDisplayStateSet.size(); i++) { 426 displayIds.add(mDisplayStateSet.keyAt(i)); 427 } 428 } 429 // setDisplayState has a binder call to system_server. Should not wrap setDisplayState 430 // with a lock. 431 for (int i = 0; i < displayIds.size(); i++) { 432 setDisplayState(displayIds.get(i), on); 433 } 434 } 435 436 @Override isAnyDisplayEnabled()437 public boolean isAnyDisplayEnabled() { 438 synchronized (mLock) { 439 for (int i = 0; i < mDisplayStateSet.size(); i++) { 440 if (isDisplayEnabled(mDisplayStateSet.keyAt(i))) { 441 return true; 442 } 443 } 444 } 445 return false; 446 } 447 448 @Override isDisplayEnabled(int displayId)449 public boolean isDisplayEnabled(int displayId) { 450 return isDisplayOn(displayId); 451 } 452 onUsersUpdate()453 private void onUsersUpdate() { 454 synchronized (mLock) { 455 if (mCarPowerManagementService == null) { 456 // CarPowerManagementService is not connected yet 457 return; 458 } 459 // We need to reset last value 460 mDisplayBrightnessSet.put(DEFAULT_DISPLAY, INVALID_DISPLAY_BRIGHTNESS); 461 } 462 refreshDisplayBrightness(); 463 } 464 } 465 } 466