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 com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX; 20 import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinear; 21 import static com.android.settingslib.display.BrightnessUtils.convertLinearToGamma; 22 23 import android.app.ActivityManager; 24 import android.car.userlib.CarUserManagerHelper; 25 import android.car.userlib.CarUserManagerHelper.OnUsersUpdateListener; 26 import android.content.ContentResolver; 27 import android.content.Context; 28 import android.database.ContentObserver; 29 import android.hardware.display.DisplayManager; 30 import android.hardware.display.DisplayManager.DisplayListener; 31 import android.hardware.input.InputManager; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.os.PowerManager; 35 import android.os.RemoteException; 36 import android.os.ServiceManager; 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 import android.view.DisplayAddress; 44 import android.view.IWindowManager; 45 import android.view.InputDevice; 46 47 import com.android.car.CarLog; 48 import com.android.car.CarPowerManagementService; 49 50 /** 51 * Interface that abstracts display operations 52 */ 53 public interface DisplayInterface { 54 /** 55 * @param brightness Level from 0 to 100% 56 */ setDisplayBrightness(int brightness)57 void setDisplayBrightness(int brightness); setDisplayState(boolean on)58 void setDisplayState(boolean on); startDisplayStateMonitoring(CarPowerManagementService service)59 void startDisplayStateMonitoring(CarPowerManagementService service); stopDisplayStateMonitoring()60 void stopDisplayStateMonitoring(); 61 62 /** 63 * Refreshing display brightness. Used when user is switching and car turned on. 64 */ refreshDisplayBrightness()65 void refreshDisplayBrightness(); 66 67 /** 68 * Reconfigure all secondary displays due to b/131909551 69 */ reconfigureSecondaryDisplays()70 void reconfigureSecondaryDisplays(); 71 /** 72 * Default implementation of display operations 73 */ 74 class DefaultImpl implements DisplayInterface, OnUsersUpdateListener { 75 static final String TAG = DisplayInterface.class.getSimpleName(); 76 77 private final ActivityManager mActivityManager; 78 private final ContentResolver mContentResolver; 79 private final Context mContext; 80 private final DisplayManager mDisplayManager; 81 private final InputManager mInputManager; 82 private final int mMaximumBacklight; 83 private final int mMinimumBacklight; 84 private final PowerManager mPowerManager; 85 private final WakeLockInterface mWakeLockInterface; 86 private CarPowerManagementService mService; 87 private boolean mDisplayStateSet; 88 private CarUserManagerHelper mCarUserManagerHelper; 89 private int mLastBrightnessLevel = -1; 90 91 private ContentObserver mBrightnessObserver = 92 new ContentObserver(new Handler(Looper.getMainLooper())) { 93 @Override 94 public void onChange(boolean selfChange) { 95 refreshDisplayBrightness(); 96 } 97 }; 98 99 private final DisplayManager.DisplayListener mDisplayListener = new DisplayListener() { 100 @Override 101 public void onDisplayAdded(int displayId) { 102 //ignore 103 } 104 105 @Override 106 public void onDisplayRemoved(int displayId) { 107 //ignore 108 } 109 110 @Override 111 public void onDisplayChanged(int displayId) { 112 if (displayId == Display.DEFAULT_DISPLAY) { 113 handleMainDisplayChanged(); 114 } 115 } 116 }; 117 DefaultImpl(Context context, WakeLockInterface wakeLockInterface)118 DefaultImpl(Context context, WakeLockInterface wakeLockInterface) { 119 mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 120 mContext = context; 121 mContentResolver = mContext.getContentResolver(); 122 mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); 123 mInputManager = (InputManager) mContext.getSystemService(Context.INPUT_SERVICE); 124 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 125 mMaximumBacklight = mPowerManager.getMaximumScreenBrightnessSetting(); 126 mMinimumBacklight = mPowerManager.getMinimumScreenBrightnessSetting(); 127 mWakeLockInterface = wakeLockInterface; 128 mCarUserManagerHelper = new CarUserManagerHelper(context); 129 mCarUserManagerHelper.registerOnUsersUpdateListener(this); 130 } 131 132 @Override refreshDisplayBrightness()133 public synchronized void refreshDisplayBrightness() { 134 int gamma = GAMMA_SPACE_MAX; 135 try { 136 int linear = System.getIntForUser( 137 mContentResolver, 138 System.SCREEN_BRIGHTNESS, 139 mActivityManager.getCurrentUser()); 140 gamma = convertLinearToGamma(linear, mMinimumBacklight, mMaximumBacklight); 141 } catch (SettingNotFoundException e) { 142 Log.e(CarLog.TAG_POWER, "Could not get SCREEN_BRIGHTNESS: " + e); 143 } 144 int percentBright = (gamma * 100 + ((GAMMA_SPACE_MAX + 1) / 2)) / GAMMA_SPACE_MAX; 145 mService.sendDisplayBrightness(percentBright); 146 } 147 handleMainDisplayChanged()148 private void handleMainDisplayChanged() { 149 boolean isOn = isMainDisplayOn(); 150 CarPowerManagementService service; 151 synchronized (this) { 152 if (mDisplayStateSet == isOn) { // same as what is set 153 return; 154 } 155 service = mService; 156 } 157 service.handleMainDisplayChanged(isOn); 158 } 159 isMainDisplayOn()160 private boolean isMainDisplayOn() { 161 Display disp = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); 162 return disp.getState() == Display.STATE_ON; 163 } 164 165 @Override setDisplayBrightness(int percentBright)166 public void setDisplayBrightness(int percentBright) { 167 if (percentBright == mLastBrightnessLevel) { 168 // We have already set the value last time. Skipping 169 return; 170 } 171 mLastBrightnessLevel = percentBright; 172 int gamma = (percentBright * GAMMA_SPACE_MAX + 50) / 100; 173 int linear = convertGammaToLinear(gamma, mMinimumBacklight, mMaximumBacklight); 174 System.putIntForUser( 175 mContentResolver, 176 System.SCREEN_BRIGHTNESS, 177 linear, 178 mActivityManager.getCurrentUser()); 179 } 180 181 @Override startDisplayStateMonitoring(CarPowerManagementService service)182 public void startDisplayStateMonitoring(CarPowerManagementService service) { 183 synchronized (this) { 184 mService = service; 185 mDisplayStateSet = isMainDisplayOn(); 186 } 187 mContentResolver.registerContentObserver( 188 System.getUriFor(System.SCREEN_BRIGHTNESS), 189 false, 190 mBrightnessObserver, 191 UserHandle.USER_ALL); 192 mDisplayManager.registerDisplayListener(mDisplayListener, service.getHandler()); 193 refreshDisplayBrightness(); 194 } 195 196 @Override stopDisplayStateMonitoring()197 public void stopDisplayStateMonitoring() { 198 mDisplayManager.unregisterDisplayListener(mDisplayListener); 199 mContentResolver.unregisterContentObserver(mBrightnessObserver); 200 } 201 202 @Override setDisplayState(boolean on)203 public void setDisplayState(boolean on) { 204 synchronized (this) { 205 mDisplayStateSet = on; 206 } 207 if (on) { 208 mWakeLockInterface.switchToFullWakeLock(); 209 Log.i(CarLog.TAG_POWER, "on display"); 210 mPowerManager.wakeUp(SystemClock.uptimeMillis()); 211 } else { 212 mWakeLockInterface.switchToPartialWakeLock(); 213 Log.i(CarLog.TAG_POWER, "off display"); 214 mPowerManager.goToSleep(SystemClock.uptimeMillis()); 215 } 216 // Turn touchscreen input devices on or off, the same as the display 217 for (int deviceId : mInputManager.getInputDeviceIds()) { 218 InputDevice inputDevice = mInputManager.getInputDevice(deviceId); 219 if (inputDevice != null 220 && (inputDevice.getSources() & InputDevice.SOURCE_TOUCHSCREEN) 221 == InputDevice.SOURCE_TOUCHSCREEN) { 222 if (on) { 223 mInputManager.enableInputDevice(deviceId); 224 } else { 225 mInputManager.disableInputDevice(deviceId); 226 } 227 } 228 } 229 } 230 231 @Override onUsersUpdate()232 public void onUsersUpdate() { 233 if (mService == null) { 234 // CarPowerManagementService is not connected yet 235 return; 236 } 237 // We need to reset last value 238 mLastBrightnessLevel = -1; 239 refreshDisplayBrightness(); 240 } 241 242 @Override reconfigureSecondaryDisplays()243 public void reconfigureSecondaryDisplays() { 244 IWindowManager wm = IWindowManager.Stub 245 .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)); 246 if (wm == null) { 247 Log.e(TAG, "reconfigureSecondaryDisplays IWindowManager not available"); 248 return; 249 } 250 Display[] displays = mDisplayManager.getDisplays(); 251 for (Display display : displays) { 252 if (display.getDisplayId() == Display.DEFAULT_DISPLAY) { // skip main 253 continue; 254 } 255 // Only use physical secondary displays 256 if (display.getAddress() instanceof DisplayAddress.Physical) { 257 int displayId = display.getDisplayId(); 258 try { 259 // Do not change the mode but this triggers reconfiguring. 260 int windowingMode = wm.getWindowingMode(displayId); 261 wm.setWindowingMode(displayId, windowingMode); 262 } catch (RemoteException e) { 263 Log.e(CarLog.TAG_SERVICE, "cannot access IWindowManager", e); 264 } 265 } 266 } 267 } 268 } 269 } 270