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