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 21 import static com.android.car.util.BrightnessUtils.GAMMA_SPACE_MAX; 22 import static com.android.car.util.BrightnessUtils.convertGammaToLinear; 23 import static com.android.car.util.BrightnessUtils.convertLinearToGamma; 24 import static com.android.car.util.Utils.getContentResolverForUser; 25 import static com.android.car.util.Utils.isEventOfType; 26 27 import android.car.builtin.power.PowerManagerHelper; 28 import android.car.builtin.util.Slogf; 29 import android.car.user.CarUserManager.UserLifecycleListener; 30 import android.car.user.UserLifecycleEventFilter; 31 import android.content.Context; 32 import android.database.ContentObserver; 33 import android.hardware.display.DisplayManager; 34 import android.hardware.display.DisplayManager.DisplayListener; 35 import android.os.Handler; 36 import android.os.Looper; 37 import android.os.SystemClock; 38 import android.os.UserHandle; 39 import android.provider.Settings.SettingNotFoundException; 40 import android.provider.Settings.System; 41 import android.util.Log; 42 import android.view.Display; 43 44 import com.android.car.CarLog; 45 import com.android.car.power.CarPowerManagementService; 46 import com.android.car.user.CarUserService; 47 import com.android.internal.annotations.GuardedBy; 48 49 /** 50 * Interface that abstracts display operations 51 */ 52 public interface DisplayInterface { 53 54 /** 55 * Sets the required services. 56 * 57 * @param carPowerManagementService {@link CarPowerManagementService} to listen to car power 58 * management changes 59 * @param carUserService {@link CarUserService} to listen to service life cycle 60 * changes 61 */ init(CarPowerManagementService carPowerManagementService, CarUserService carUserService)62 void init(CarPowerManagementService carPowerManagementService, CarUserService carUserService); 63 64 /** 65 * Sets display brightness. 66 * 67 * @param brightness Level from 0 to 100% 68 */ setDisplayBrightness(int brightness)69 void setDisplayBrightness(int brightness); 70 71 /** 72 * Turns on or off display. 73 * 74 * @param on {@code true} to turn on, {@code false} to turn off. 75 */ setDisplayState(boolean on)76 void setDisplayState(boolean on); 77 78 /** 79 * Starts monitoring the display state change. 80 * <p> When there is a change, {@link CarPowerManagementService} is notified. 81 */ startDisplayStateMonitoring()82 void startDisplayStateMonitoring(); 83 84 /** 85 * Stops monitoring the display state change. 86 */ stopDisplayStateMonitoring()87 void stopDisplayStateMonitoring(); 88 89 /** 90 * Gets the current on/off state of display. 91 */ isDisplayEnabled()92 boolean isDisplayEnabled(); 93 94 /** 95 * Refreshing display brightness. Used when user is switching and car turned on. 96 */ refreshDisplayBrightness()97 void refreshDisplayBrightness(); 98 99 /** 100 * Default implementation of display operations 101 */ 102 class DefaultImpl implements DisplayInterface { 103 private static final String TAG = DisplayInterface.class.getSimpleName(); 104 private static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG); 105 private final Context mContext; 106 private final DisplayManager mDisplayManager; 107 private final Object mLock = new Object(); 108 private final int mMaximumBacklight; 109 private final int mMinimumBacklight; 110 private final WakeLockInterface mWakeLockInterface; 111 @GuardedBy("mLock") 112 private CarPowerManagementService mCarPowerManagementService; 113 @GuardedBy("mLock") 114 private CarUserService mCarUserService; 115 @GuardedBy("mLock") 116 private boolean mDisplayStateSet; 117 @GuardedBy("mLock") 118 private int mLastBrightnessLevel = -1; 119 120 private final ContentObserver mBrightnessObserver = 121 new ContentObserver(new Handler(Looper.getMainLooper())) { 122 @Override 123 public void onChange(boolean selfChange) { 124 refreshDisplayBrightness(); 125 } 126 }; 127 128 private final DisplayManager.DisplayListener mDisplayListener = new DisplayListener() { 129 @Override 130 public void onDisplayAdded(int displayId) { 131 //ignore 132 } 133 134 @Override 135 public void onDisplayRemoved(int displayId) { 136 //ignore 137 } 138 139 @Override 140 public void onDisplayChanged(int displayId) { 141 if (displayId == Display.DEFAULT_DISPLAY) { 142 handleMainDisplayChanged(); 143 } 144 } 145 }; 146 DefaultImpl(Context context, WakeLockInterface wakeLockInterface)147 DefaultImpl(Context context, WakeLockInterface wakeLockInterface) { 148 mContext = context; 149 mDisplayManager = context.getSystemService(DisplayManager.class); 150 mMaximumBacklight = PowerManagerHelper.getMaximumScreenBrightnessSetting(context); 151 mMinimumBacklight = PowerManagerHelper.getMinimumScreenBrightnessSetting(context); 152 mWakeLockInterface = wakeLockInterface; 153 } 154 155 private final UserLifecycleListener mUserLifecycleListener = event -> { 156 if (!isEventOfType(TAG, event, USER_LIFECYCLE_EVENT_TYPE_SWITCHING)) { 157 return; 158 } 159 if (DEBUG) { 160 Slogf.d(TAG, "DisplayInterface.DefaultImpl.onEvent(%s)", event); 161 } 162 163 onUsersUpdate(); 164 }; 165 166 @Override refreshDisplayBrightness()167 public void refreshDisplayBrightness() { 168 CarPowerManagementService carPowerManagementService = null; 169 synchronized (mLock) { 170 carPowerManagementService = mCarPowerManagementService; 171 } 172 if (carPowerManagementService == null) { 173 Slogf.e(CarLog.TAG_POWER, "Could not set brightness: " 174 + "no CarPowerManagementService"); 175 return; 176 } 177 int gamma = GAMMA_SPACE_MAX; 178 try { 179 int linear = System.getInt( 180 getContentResolverForUser(mContext, UserHandle.CURRENT.getIdentifier()), 181 System.SCREEN_BRIGHTNESS); 182 gamma = convertLinearToGamma(linear, mMinimumBacklight, mMaximumBacklight); 183 } catch (SettingNotFoundException e) { 184 Slogf.e(CarLog.TAG_POWER, "Could not get SCREEN_BRIGHTNESS: ", e); 185 } 186 int percentBright = (gamma * 100 + ((GAMMA_SPACE_MAX + 1) / 2)) / GAMMA_SPACE_MAX; 187 carPowerManagementService.sendDisplayBrightness(percentBright); 188 } 189 handleMainDisplayChanged()190 private void handleMainDisplayChanged() { 191 boolean isOn = isMainDisplayOn(); 192 CarPowerManagementService service; 193 synchronized (mLock) { 194 if (mDisplayStateSet == isOn) { // same as what is set 195 return; 196 } 197 service = mCarPowerManagementService; 198 } 199 service.handleMainDisplayChanged(isOn); 200 } 201 isMainDisplayOn()202 private boolean isMainDisplayOn() { 203 Display disp = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); 204 return disp.getState() == Display.STATE_ON; 205 } 206 207 @Override setDisplayBrightness(int percentBright)208 public void setDisplayBrightness(int percentBright) { 209 synchronized (mLock) { 210 if (percentBright == mLastBrightnessLevel) { 211 // We have already set the value last time. Skipping 212 return; 213 } 214 mLastBrightnessLevel = percentBright; 215 } 216 int gamma = (percentBright * GAMMA_SPACE_MAX + 50) / 100; 217 int linear = convertGammaToLinear(gamma, mMinimumBacklight, mMaximumBacklight); 218 System.putInt( 219 getContentResolverForUser(mContext, UserHandle.CURRENT.getIdentifier()), 220 System.SCREEN_BRIGHTNESS, 221 linear); 222 } 223 224 @Override init(CarPowerManagementService carPowerManagementService, CarUserService carUserService)225 public void init(CarPowerManagementService carPowerManagementService, 226 CarUserService carUserService) { 227 synchronized (mLock) { 228 mCarPowerManagementService = carPowerManagementService; 229 mCarUserService = carUserService; 230 mDisplayStateSet = isMainDisplayOn(); 231 } 232 } 233 234 @Override startDisplayStateMonitoring()235 public void startDisplayStateMonitoring() { 236 CarPowerManagementService carPowerManagementService; 237 CarUserService carUserService; 238 synchronized (mLock) { 239 carPowerManagementService = mCarPowerManagementService; 240 carUserService = mCarUserService; 241 } 242 UserLifecycleEventFilter userSwitchingEventFilter = 243 new UserLifecycleEventFilter.Builder() 244 .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING).build(); 245 carUserService.addUserLifecycleListener(userSwitchingEventFilter, 246 mUserLifecycleListener); 247 getContentResolverForUser(mContext, UserHandle.ALL.getIdentifier()) 248 .registerContentObserver(System.getUriFor(System.SCREEN_BRIGHTNESS), 249 false, 250 mBrightnessObserver); 251 mDisplayManager.registerDisplayListener(mDisplayListener, 252 carPowerManagementService.getHandler()); 253 refreshDisplayBrightness(); 254 } 255 256 @Override stopDisplayStateMonitoring()257 public void stopDisplayStateMonitoring() { 258 CarUserService carUserService; 259 synchronized (mLock) { 260 carUserService = mCarUserService; 261 } 262 carUserService.removeUserLifecycleListener(mUserLifecycleListener); 263 mDisplayManager.unregisterDisplayListener(mDisplayListener); 264 getContentResolverForUser(mContext, UserHandle.ALL.getIdentifier()) 265 .unregisterContentObserver(mBrightnessObserver); 266 } 267 268 @Override setDisplayState(boolean on)269 public void setDisplayState(boolean on) { 270 synchronized (mLock) { 271 mDisplayStateSet = on; 272 } 273 if (on) { 274 mWakeLockInterface.switchToFullWakeLock(); 275 Slogf.i(CarLog.TAG_POWER, "on display"); 276 PowerManagerHelper.setDisplayState(mContext, /* on= */ true, 277 SystemClock.uptimeMillis()); 278 } else { 279 mWakeLockInterface.switchToPartialWakeLock(); 280 Slogf.i(CarLog.TAG_POWER, "off display"); 281 PowerManagerHelper.setDisplayState(mContext, /* on= */ false, 282 SystemClock.uptimeMillis()); 283 } 284 } 285 286 @Override isDisplayEnabled()287 public boolean isDisplayEnabled() { 288 return isMainDisplayOn(); 289 } 290 onUsersUpdate()291 private void onUsersUpdate() { 292 synchronized (mLock) { 293 if (mCarPowerManagementService == null) { 294 // CarPowerManagementService is not connected yet 295 return; 296 } 297 // We need to reset last value 298 mLastBrightnessLevel = -1; 299 } 300 refreshDisplayBrightness(); 301 } 302 } 303 } 304