• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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