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