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