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 import static android.view.Display.DEFAULT_DISPLAY;
21 
22 import static com.android.car.CarServiceUtils.getContentResolverForUser;
23 import static com.android.car.CarServiceUtils.isEventOfType;
24 import static com.android.car.util.BrightnessUtils.GAMMA_SPACE_MAX;
25 import static com.android.car.util.BrightnessUtils.convertGammaToLinear;
26 import static com.android.car.util.BrightnessUtils.convertLinearToGamma;
27 
28 import android.car.builtin.display.DisplayManagerHelper;
29 import android.car.builtin.os.UserManagerHelper;
30 import android.car.builtin.power.PowerManagerHelper;
31 import android.car.builtin.util.Slogf;
32 import android.car.user.CarUserManager.UserLifecycleListener;
33 import android.car.user.UserLifecycleEventFilter;
34 import android.content.Context;
35 import android.database.ContentObserver;
36 import android.hardware.display.DisplayManager;
37 import android.hardware.display.DisplayManager.DisplayListener;
38 import android.os.Handler;
39 import android.os.Looper;
40 import android.os.SystemClock;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.provider.Settings.SettingNotFoundException;
44 import android.provider.Settings.System;
45 import android.util.Log;
46 import android.util.SparseBooleanArray;
47 import android.util.SparseIntArray;
48 import android.view.Display;
49 
50 import com.android.car.CarLog;
51 import com.android.car.internal.util.IntArray;
52 import com.android.car.power.CarPowerManagementService;
53 import com.android.car.user.CarUserService;
54 import com.android.car.util.BrightnessUtils;
55 import com.android.internal.annotations.GuardedBy;
56 
57 /**
58  * Interface that abstracts display operations
59  */
60 public interface DisplayInterface {
61 
62     /**
63      * Sets the required services.
64      *
65      * @param carPowerManagementService {@link CarPowerManagementService} to listen to car power
66      *                                  management changes
67      * @param carUserService            {@link CarUserService} to listen to service life cycle
68      *                                  changes
69      */
init(CarPowerManagementService carPowerManagementService, CarUserService carUserService)70     void init(CarPowerManagementService carPowerManagementService, CarUserService carUserService);
71 
72     /**
73      * Sets display brightness.
74      *
75      * @param brightness Level from 0 to 100%
76      */
setDisplayBrightness(int brightness)77     void setDisplayBrightness(int brightness);
78 
79     /**
80      * Sets display brightness with the given displayId.
81      *
82      * @param displayId ID of a display.
83      * @param brightness Level from 0 to 100.
84      */
setDisplayBrightness(int displayId, int brightness)85     void setDisplayBrightness(int displayId, int brightness);
86 
87     /**
88      * Turns on or off display with the given displayId.
89      *
90      * @param displayId ID of a display.
91      * @param on {@code true} to turn on, {@code false} to turn off.
92      */
setDisplayState(int displayId, boolean on)93     void setDisplayState(int displayId, boolean on);
94 
95     /**
96      * Turns on or off all displays.
97      *
98      * @param on {@code true} to turn on, {@code false} to turn off.
99      */
setAllDisplayState(boolean on)100     void setAllDisplayState(boolean on);
101 
102     /**
103      * Starts monitoring the display state change.
104      * <p> When there is a change, {@link CarPowerManagementService} is notified.
105      */
startDisplayStateMonitoring()106     void startDisplayStateMonitoring();
107 
108     /**
109      * Stops monitoring the display state change.
110      */
stopDisplayStateMonitoring()111     void stopDisplayStateMonitoring();
112 
113     /**
114      * Gets the current on/off state of displays.
115      *
116      * @return {@code true}, if any display is turned on. Otherwise, {@code false}.
117      */
isAnyDisplayEnabled()118     boolean isAnyDisplayEnabled();
119 
120     /**
121      * Gets the current on/off state of display with the given displayId.
122      *
123      * @param displayId ID of a display.
124      */
isDisplayEnabled(int displayId)125     boolean isDisplayEnabled(int displayId);
126 
127     /**
128      * Refreshing display brightness. Used when user is switching and car turned on.
129      */
refreshDisplayBrightness()130     void refreshDisplayBrightness();
131 
132     /**
133      * Refreshing display brightness with the given displayId.
134      * Used when brightness change is observed.
135      *
136      * @param displayId ID of a display.
137      */
refreshDisplayBrightness(int displayId)138     void refreshDisplayBrightness(int displayId);
139 
140     /**
141      * Default implementation of display operations
142      */
143     class DefaultImpl implements DisplayInterface {
144         private static final String TAG = DisplayInterface.class.getSimpleName();
145         private static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG);
146         private static final int INVALID_DISPLAY_BRIGHTNESS = -1;
147 
148         private final Context mContext;
149         private final DisplayManager mDisplayManager;
150         private final Object mLock = new Object();
151         private final int mMaximumBacklight;
152         private final int mMinimumBacklight;
153         private final WakeLockInterface mWakeLockInterface;
154         @GuardedBy("mLock")
155         private CarPowerManagementService mCarPowerManagementService;
156         @GuardedBy("mLock")
157         private CarUserService mCarUserService;
158         @GuardedBy("mLock")
159         private final SparseBooleanArray mDisplayStateSet = new SparseBooleanArray();
160         @GuardedBy("mLock")
161         private final SparseIntArray mDisplayBrightnessSet = new SparseIntArray();
162         private final UserManager mUserManager;
163 
164         private final ContentObserver mBrightnessObserver =
165                 new ContentObserver(new Handler(Looper.getMainLooper())) {
166                     @Override
167                     public void onChange(boolean selfChange) {
168                         Slogf.i(TAG, "Brightness change from Settings: selfChange=%b", selfChange);
169                         refreshDisplayBrightness();
170                     }
171                 };
172 
173         private final DisplayManager.DisplayListener mDisplayListener = new DisplayListener() {
174             @Override
175             public void onDisplayAdded(int displayId) {
176                 Slogf.i(TAG, "onDisplayAdded: displayId=%d", displayId);
177                 synchronized (mLock) {
178                     mDisplayStateSet.put(displayId, isDisplayOn(displayId));
179                     mDisplayBrightnessSet.put(displayId, INVALID_DISPLAY_BRIGHTNESS);
180                 }
181             }
182 
183             @Override
184             public void onDisplayRemoved(int displayId) {
185                 Slogf.i(TAG, "onDisplayRemoved: displayId=%d", displayId);
186                 synchronized (mLock) {
187                     mDisplayStateSet.delete(displayId);
188                     mDisplayBrightnessSet.delete(displayId);
189                 }
190             }
191 
192             @Override
193             public void onDisplayChanged(int displayId) {
194                 Slogf.i(TAG, "onDisplayChanged: displayId=%d", displayId);
195                 handleDisplayChanged(displayId);
196             }
197         };
198 
DefaultImpl(Context context, WakeLockInterface wakeLockInterface)199         DefaultImpl(Context context, WakeLockInterface wakeLockInterface) {
200             mContext = context;
201             mDisplayManager = context.getSystemService(DisplayManager.class);
202             mMaximumBacklight = PowerManagerHelper.getMaximumScreenBrightnessSetting(context);
203             mMinimumBacklight = PowerManagerHelper.getMinimumScreenBrightnessSetting(context);
204             mWakeLockInterface = wakeLockInterface;
205             synchronized (mLock) {
206                 for (Display display : mDisplayManager.getDisplays()) {
207                     int displayId = display.getDisplayId();
208                     mDisplayStateSet.put(displayId, isDisplayOn(displayId));
209                     mDisplayBrightnessSet.put(displayId, INVALID_DISPLAY_BRIGHTNESS);
210                 }
211             }
212             mUserManager = context.getSystemService(UserManager.class);
213         }
214 
215         private final UserLifecycleListener mUserLifecycleListener = event -> {
216             if (!isEventOfType(TAG, event, USER_LIFECYCLE_EVENT_TYPE_SWITCHING)) {
217                 return;
218             }
219             if (DEBUG) {
220                 Slogf.d(TAG, "DisplayInterface.DefaultImpl.onEvent(%s)", event);
221             }
222 
223             onUsersUpdate();
224         };
225 
226         @Override
refreshDisplayBrightness()227         public void refreshDisplayBrightness() {
228             refreshDisplayBrightness(DEFAULT_DISPLAY);
229         }
230 
231         @Override
refreshDisplayBrightness(int displayId)232         public void refreshDisplayBrightness(int displayId) {
233             CarPowerManagementService carPowerManagementService = null;
234             synchronized (mLock) {
235                 carPowerManagementService = mCarPowerManagementService;
236             }
237             if (carPowerManagementService == null) {
238                 Slogf.e(CarLog.TAG_POWER, "Could not set brightness: "
239                         + "no CarPowerManagementService");
240                 return;
241             }
242             if (UserManagerHelper.isVisibleBackgroundUsersSupported(mUserManager)) {
243                 refreshDisplayBrightnessFromDisplay(carPowerManagementService, displayId);
244             } else {
245                 refreshDisplayBrigtnessFromSetting(carPowerManagementService);
246             }
247         }
248 
refreshDisplayBrightnessFromDisplay( CarPowerManagementService carPowerManagementService, int displayId)249         private void refreshDisplayBrightnessFromDisplay(
250                 CarPowerManagementService carPowerManagementService, int displayId) {
251             int linear = BrightnessUtils.brightnessFloatToInt(
252                     DisplayManagerHelper.getBrightness(mContext, displayId));
253             int gamma = convertLinearToGamma(linear, mMinimumBacklight, mMaximumBacklight);
254             int percentBright = convertGammaToPercentBright(gamma);
255             Slogf.i(TAG, "Refreshing percent brightness(from display %d) to %d", displayId,
256                     percentBright);
257             carPowerManagementService.sendDisplayBrightness(displayId, percentBright);
258         }
259 
refreshDisplayBrigtnessFromSetting( CarPowerManagementService carPowerManagementService)260         private void refreshDisplayBrigtnessFromSetting(
261                 CarPowerManagementService carPowerManagementService) {
262             int gamma = GAMMA_SPACE_MAX;
263             try {
264                 int linear = System.getInt(getContentResolverForUser(mContext,
265                         UserHandle.CURRENT.getIdentifier()), System.SCREEN_BRIGHTNESS);
266                 gamma = convertLinearToGamma(linear, mMinimumBacklight, mMaximumBacklight);
267             } catch (SettingNotFoundException e) {
268                 Slogf.e(CarLog.TAG_POWER, "Could not get SCREEN_BRIGHTNESS: ", e);
269             }
270             int percentBright = convertGammaToPercentBright(gamma);
271             Slogf.i(TAG, "Refreshing percent brightness(from Setting) to %d", percentBright);
272             carPowerManagementService.sendDisplayBrightness(percentBright);
273         }
274 
convertGammaToPercentBright(int gamma)275         private static int convertGammaToPercentBright(int gamma) {
276             return (gamma * 100 + ((GAMMA_SPACE_MAX + 1) / 2)) / GAMMA_SPACE_MAX;
277         }
278 
handleDisplayChanged(int displayId)279         private void handleDisplayChanged(int displayId) {
280             refreshDisplayBrightness(displayId);
281             boolean isOn = isDisplayOn(displayId);
282             CarPowerManagementService service;
283             synchronized (mLock) {
284                 boolean state = mDisplayStateSet.get(displayId, false);
285                 if (state == isOn) { // same as what is set
286                     return;
287                 }
288                 service = mCarPowerManagementService;
289             }
290             service.handleDisplayChanged(displayId, isOn);
291         }
292 
isDisplayOn(int displayId)293         private boolean isDisplayOn(int displayId) {
294             Display disp = mDisplayManager.getDisplay(displayId);
295             if (disp == null) {
296                 return false;
297             }
298             return disp.getState() == Display.STATE_ON;
299         }
300 
301         @Override
setDisplayBrightness(int percentBright)302         public void setDisplayBrightness(int percentBright) {
303             setDisplayBrightness(DEFAULT_DISPLAY, percentBright);
304         }
305 
306         @Override
setDisplayBrightness(int displayId, int percentBright)307         public void setDisplayBrightness(int displayId, int percentBright) {
308             synchronized (mLock) {
309                 if (percentBright == mDisplayBrightnessSet.get(displayId)) {
310                     // We have already set the value last time. Skipping
311                     return;
312                 }
313                 mDisplayBrightnessSet.put(displayId, percentBright);
314             }
315             int gamma = (percentBright * GAMMA_SPACE_MAX + 50) / 100;
316             int linear = convertGammaToLinear(gamma, mMinimumBacklight, mMaximumBacklight);
317             if (UserManagerHelper.isVisibleBackgroundUsersSupported(mUserManager)) {
318                 DisplayManagerHelper.setBrightness(mContext, displayId,
319                         BrightnessUtils.brightnessIntToFloat(linear));
320             } else {
321                 System.putInt(
322                         getContentResolverForUser(mContext, UserHandle.CURRENT.getIdentifier()),
323                         System.SCREEN_BRIGHTNESS,
324                         linear);
325             }
326         }
327 
328         @Override
init(CarPowerManagementService carPowerManagementService, CarUserService carUserService)329         public void init(CarPowerManagementService carPowerManagementService,
330                 CarUserService carUserService) {
331             synchronized (mLock) {
332                 mCarPowerManagementService = carPowerManagementService;
333                 mCarUserService = carUserService;
334             }
335         }
336 
337         @Override
startDisplayStateMonitoring()338         public void startDisplayStateMonitoring() {
339             Slogf.i(TAG, "Starting to monitor display state change");
340             CarPowerManagementService carPowerManagementService;
341             CarUserService carUserService;
342             synchronized (mLock) {
343                 carPowerManagementService = mCarPowerManagementService;
344                 carUserService = mCarUserService;
345             }
346             UserLifecycleEventFilter userSwitchingEventFilter =
347                     new UserLifecycleEventFilter.Builder()
348                             .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING).build();
349             carUserService.addUserLifecycleListener(userSwitchingEventFilter,
350                     mUserLifecycleListener);
351             if (UserManagerHelper.isVisibleBackgroundUsersSupported(mUserManager)) {
352                 DisplayManagerHelper.registerDisplayListener(mContext, mDisplayListener,
353                         carPowerManagementService.getHandler(),
354                         DisplayManagerHelper.EVENT_FLAG_DISPLAY_ADDED
355                                 | DisplayManagerHelper.EVENT_FLAG_DISPLAY_REMOVED
356                                 | DisplayManagerHelper.EVENT_FLAG_DISPLAY_CHANGED
357                                 | DisplayManagerHelper.EVENT_FLAG_DISPLAY_BRIGHTNESS);
358             } else {
359                 getContentResolverForUser(mContext, UserHandle.ALL.getIdentifier())
360                         .registerContentObserver(System.getUriFor(System.SCREEN_BRIGHTNESS),
361                                 false,
362                                 mBrightnessObserver);
363             }
364 
365             for (Display display : mDisplayManager.getDisplays()) {
366                 int displayId = display.getDisplayId();
367                 refreshDisplayBrightness(displayId);
368             }
369         }
370 
371         @Override
stopDisplayStateMonitoring()372         public void stopDisplayStateMonitoring() {
373             CarUserService carUserService;
374             synchronized (mLock) {
375                 carUserService = mCarUserService;
376             }
377             carUserService.removeUserLifecycleListener(mUserLifecycleListener);
378             if (UserManagerHelper.isVisibleBackgroundUsersSupported(mUserManager)) {
379                 mDisplayManager.unregisterDisplayListener(mDisplayListener);
380             } else {
381                 getContentResolverForUser(mContext, UserHandle.ALL.getIdentifier())
382                         .unregisterContentObserver(mBrightnessObserver);
383             }
384         }
385 
386         @Override
setDisplayState(int displayId, boolean on)387         public void setDisplayState(int displayId, boolean on) {
388             CarPowerManagementService carPowerManagementService;
389             synchronized (mLock) {
390                 carPowerManagementService = mCarPowerManagementService;
391                 if (on && carPowerManagementService != null
392                         && !carPowerManagementService.canTurnOnDisplay(displayId)) {
393                     Slogf.i(CarLog.TAG_POWER, "ignore turning on display %d because "
394                             + "CarPowerManagementService doesn't support it", displayId);
395                     return;
396                 }
397                 mDisplayStateSet.put(displayId, on);
398             }
399             if (on) {
400                 mWakeLockInterface.switchToFullWakeLock(displayId);
401                 Slogf.i(CarLog.TAG_POWER, "on display %d", displayId);
402             } else {
403                 mWakeLockInterface.switchToPartialWakeLock(displayId);
404                 Slogf.i(CarLog.TAG_POWER, "off display %d", displayId);
405                 PowerManagerHelper.goToSleep(mContext, displayId, SystemClock.uptimeMillis());
406             }
407             if (carPowerManagementService != null) {
408                 carPowerManagementService.handleDisplayChanged(displayId, on);
409             }
410         }
411 
412         @Override
setAllDisplayState(boolean on)413         public void setAllDisplayState(boolean on) {
414             IntArray displayIds = new IntArray();
415             synchronized (mLock) {
416                 for (int i = 0; i < mDisplayStateSet.size(); i++) {
417                     displayIds.add(mDisplayStateSet.keyAt(i));
418                 }
419             }
420             // setDisplayState has a binder call to system_server. Should not wrap setDisplayState
421             // with a lock.
422             for (int i = 0; i < displayIds.size(); i++) {
423                 int displayId = displayIds.get(i);
424                 try {
425                     setDisplayState(displayId, on);
426                 } catch (IllegalArgumentException e) {
427                     Slogf.w(TAG, "Cannot set display(%d) state(%b)", displayId, on);
428                 }
429             }
430         }
431 
432         @Override
isAnyDisplayEnabled()433         public boolean isAnyDisplayEnabled() {
434             synchronized (mLock) {
435                 for (int i = 0; i < mDisplayStateSet.size(); i++) {
436                     if (isDisplayEnabled(mDisplayStateSet.keyAt(i))) {
437                         return true;
438                     }
439                 }
440             }
441             return false;
442         }
443 
444         @Override
isDisplayEnabled(int displayId)445         public boolean isDisplayEnabled(int displayId) {
446             return isDisplayOn(displayId);
447         }
448 
onUsersUpdate()449         private void onUsersUpdate() {
450             synchronized (mLock) {
451                 if (mCarPowerManagementService == null) {
452                     // CarPowerManagementService is not connected yet
453                     return;
454                 }
455                 // We need to reset last value
456                 mDisplayBrightnessSet.put(DEFAULT_DISPLAY, INVALID_DISPLAY_BRIGHTNESS);
457             }
458             refreshDisplayBrightness();
459         }
460     }
461 }
462