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