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