• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.systemui.settings.brightness;
18 
19 import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX;
20 import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinearFloat;
21 import static com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat;
22 
23 import android.animation.ValueAnimator;
24 import android.annotation.NonNull;
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.database.ContentObserver;
28 import android.hardware.display.BrightnessInfo;
29 import android.hardware.display.DisplayManager;
30 import android.net.Uri;
31 import android.os.AsyncTask;
32 import android.os.Handler;
33 import android.os.HandlerExecutor;
34 import android.os.Message;
35 import android.os.PowerManager;
36 import android.os.RemoteException;
37 import android.os.ServiceManager;
38 import android.os.UserHandle;
39 import android.os.UserManager;
40 import android.provider.Settings;
41 import android.service.vr.IVrManager;
42 import android.service.vr.IVrStateCallbacks;
43 import android.util.Log;
44 import android.util.MathUtils;
45 
46 import com.android.internal.display.BrightnessSynchronizer;
47 import com.android.internal.logging.MetricsLogger;
48 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
49 import com.android.settingslib.RestrictedLockUtilsInternal;
50 import com.android.systemui.dagger.qualifiers.Background;
51 import com.android.systemui.dagger.qualifiers.Main;
52 import com.android.systemui.settings.DisplayTracker;
53 import com.android.systemui.settings.UserTracker;
54 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
55 
56 import java.util.concurrent.Executor;
57 
58 import javax.inject.Inject;
59 
60 public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController {
61     private static final String TAG = "CentralSurfaces.BrightnessController";
62     private static final int SLIDER_ANIMATION_DURATION = 3000;
63 
64     private static final int MSG_UPDATE_SLIDER = 1;
65     private static final int MSG_ATTACH_LISTENER = 2;
66     private static final int MSG_DETACH_LISTENER = 3;
67     private static final int MSG_VR_MODE_CHANGED = 4;
68 
69     private static final Uri BRIGHTNESS_MODE_URI =
70             Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
71     private static final Uri BRIGHTNESS_FOR_VR_FLOAT_URI =
72             Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT);
73 
74     private final float mMinimumBacklightForVr;
75     private final float mMaximumBacklightForVr;
76 
77     private final int mDisplayId;
78     private final Context mContext;
79     private final ToggleSlider mControl;
80     private final DisplayManager mDisplayManager;
81     private final UserTracker mUserTracker;
82     private final DisplayTracker mDisplayTracker;
83     private final IVrManager mVrManager;
84 
85     private final Executor mMainExecutor;
86     private final Handler mBackgroundHandler;
87     private final BrightnessObserver mBrightnessObserver;
88 
89     private final DisplayTracker.Callback mBrightnessListener = new DisplayTracker.Callback() {
90         @Override
91         public void onDisplayChanged(int displayId) {
92             mBackgroundHandler.post(mUpdateSliderRunnable);
93         }
94     };
95 
96     private volatile boolean mAutomatic;  // Brightness adjusted automatically using ambient light.
97     private volatile boolean mIsVrModeEnabled;
98     private boolean mListening;
99     private boolean mExternalChange;
100     private boolean mControlValueInitialized;
101     private float mBrightnessMin = PowerManager.BRIGHTNESS_MIN;
102     private float mBrightnessMax = PowerManager.BRIGHTNESS_MAX;
103 
104     private ValueAnimator mSliderAnimator;
105 
106     @Override
setMirror(BrightnessMirrorController controller)107     public void setMirror(BrightnessMirrorController controller) {
108         mControl.setMirrorControllerAndMirror(controller);
109     }
110 
111     /** ContentObserver to watch brightness */
112     private class BrightnessObserver extends ContentObserver {
113 
BrightnessObserver(Handler handler)114         BrightnessObserver(Handler handler) {
115             super(handler);
116         }
117 
118         @Override
onChange(boolean selfChange, Uri uri)119         public void onChange(boolean selfChange, Uri uri) {
120             if (selfChange) return;
121 
122             if (BRIGHTNESS_MODE_URI.equals(uri)) {
123                 mBackgroundHandler.post(mUpdateModeRunnable);
124                 mBackgroundHandler.post(mUpdateSliderRunnable);
125             } else if (BRIGHTNESS_FOR_VR_FLOAT_URI.equals(uri)) {
126                 mBackgroundHandler.post(mUpdateSliderRunnable);
127             } else {
128                 mBackgroundHandler.post(mUpdateModeRunnable);
129                 mBackgroundHandler.post(mUpdateSliderRunnable);
130             }
131         }
132 
startObserving()133         public void startObserving() {
134             final ContentResolver cr = mContext.getContentResolver();
135             cr.unregisterContentObserver(this);
136             cr.registerContentObserver(
137                     BRIGHTNESS_MODE_URI,
138                     false, this, UserHandle.USER_ALL);
139             cr.registerContentObserver(
140                     BRIGHTNESS_FOR_VR_FLOAT_URI,
141                     false, this, UserHandle.USER_ALL);
142             mDisplayTracker.addBrightnessChangeCallback(mBrightnessListener,
143                     new HandlerExecutor(mHandler));
144         }
145 
stopObserving()146         public void stopObserving() {
147             final ContentResolver cr = mContext.getContentResolver();
148             cr.unregisterContentObserver(this);
149             mDisplayTracker.removeCallback(mBrightnessListener);
150         }
151 
152     }
153 
154     private final Runnable mStartListeningRunnable = new Runnable() {
155         @Override
156         public void run() {
157             if (mListening) {
158                 return;
159             }
160             mListening = true;
161 
162             if (mVrManager != null) {
163                 try {
164                     mVrManager.registerListener(mVrStateCallbacks);
165                     mIsVrModeEnabled = mVrManager.getVrModeState();
166                 } catch (RemoteException e) {
167                     Log.e(TAG, "Failed to register VR mode state listener: ", e);
168                 }
169             }
170 
171             mBrightnessObserver.startObserving();
172             mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
173 
174             // Update the slider and mode before attaching the listener so we don't
175             // receive the onChanged notifications for the initial values.
176             mUpdateModeRunnable.run();
177             mUpdateSliderRunnable.run();
178 
179             mHandler.sendEmptyMessage(MSG_ATTACH_LISTENER);
180         }
181     };
182 
183     private final Runnable mStopListeningRunnable = new Runnable() {
184         @Override
185         public void run() {
186             if (!mListening) {
187                 return;
188             }
189             mListening = false;
190 
191             if (mVrManager != null) {
192                 try {
193                     mVrManager.unregisterListener(mVrStateCallbacks);
194                 } catch (RemoteException e) {
195                     Log.e(TAG, "Failed to unregister VR mode state listener: ", e);
196                 }
197             }
198 
199             mBrightnessObserver.stopObserving();
200             mUserTracker.removeCallback(mUserChangedCallback);
201 
202             mHandler.sendEmptyMessage(MSG_DETACH_LISTENER);
203         }
204     };
205 
206     /**
207      * Fetch the brightness mode from the system settings and update the icon. Should be called from
208      * background thread.
209      */
210     private final Runnable mUpdateModeRunnable = new Runnable() {
211         @Override
212         public void run() {
213             int automatic;
214             automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
215                     Settings.System.SCREEN_BRIGHTNESS_MODE,
216                     Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
217                     mUserTracker.getUserId());
218             mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
219         }
220     };
221 
222     /**
223      * Fetch the brightness from the system settings and update the slider. Should be called from
224      * background thread.
225      */
226     private final Runnable mUpdateSliderRunnable = new Runnable() {
227         @Override
228         public void run() {
229             final boolean inVrMode = mIsVrModeEnabled;
230             final BrightnessInfo info = mContext.getDisplay().getBrightnessInfo();
231             if (info == null) {
232                 return;
233             }
234             mBrightnessMax = info.brightnessMaximum;
235             mBrightnessMin = info.brightnessMinimum;
236             // Value is passed as intbits, since this is what the message takes.
237             final int valueAsIntBits = Float.floatToIntBits(info.brightness);
238             mHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits,
239                     inVrMode ? 1 : 0).sendToTarget();
240         }
241     };
242 
243     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
244         @Override
245         public void onVrStateChanged(boolean enabled) {
246             mHandler.obtainMessage(MSG_VR_MODE_CHANGED, enabled ? 1 : 0, 0)
247                     .sendToTarget();
248         }
249     };
250 
251     private final Handler mHandler = new Handler() {
252         @Override
253         public void handleMessage(Message msg) {
254             mExternalChange = true;
255             try {
256                 switch (msg.what) {
257                     case MSG_UPDATE_SLIDER:
258                         updateSlider(Float.intBitsToFloat(msg.arg1), msg.arg2 != 0);
259                         break;
260                     case MSG_ATTACH_LISTENER:
261                         mControl.setOnChangedListener(BrightnessController.this);
262                         break;
263                     case MSG_DETACH_LISTENER:
264                         mControl.setOnChangedListener(null);
265                         break;
266                     case MSG_VR_MODE_CHANGED:
267                         updateVrMode(msg.arg1 != 0);
268                         break;
269                     default:
270                         super.handleMessage(msg);
271                 }
272             } finally {
273                 mExternalChange = false;
274             }
275         }
276     };
277 
278     private final UserTracker.Callback mUserChangedCallback =
279             new UserTracker.Callback() {
280                 @Override
281                 public void onUserChanged(int newUser, @NonNull Context userContext) {
282                     mBackgroundHandler.post(mUpdateModeRunnable);
283                     mBackgroundHandler.post(mUpdateSliderRunnable);
284                 }
285             };
286 
BrightnessController( Context context, ToggleSlider control, UserTracker userTracker, DisplayTracker displayTracker, @Main Executor mainExecutor, @Background Handler bgHandler)287     public BrightnessController(
288             Context context,
289             ToggleSlider control,
290             UserTracker userTracker,
291             DisplayTracker displayTracker,
292             @Main Executor mainExecutor,
293             @Background Handler bgHandler) {
294         mContext = context;
295         mControl = control;
296         mControl.setMax(GAMMA_SPACE_MAX);
297         mMainExecutor = mainExecutor;
298         mBackgroundHandler = bgHandler;
299         mUserTracker = userTracker;
300         mDisplayTracker = displayTracker;
301         mBrightnessObserver = new BrightnessObserver(mHandler);
302 
303         mDisplayId = mContext.getDisplayId();
304         PowerManager pm = context.getSystemService(PowerManager.class);
305         mMinimumBacklightForVr = pm.getBrightnessConstraint(
306                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR);
307         mMaximumBacklightForVr = pm.getBrightnessConstraint(
308                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR);
309 
310         mDisplayManager = context.getSystemService(DisplayManager.class);
311         mVrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
312                 Context.VR_SERVICE));
313     }
314 
registerCallbacks()315     public void registerCallbacks() {
316         mBackgroundHandler.post(mStartListeningRunnable);
317     }
318 
319     /** Unregister all call backs, both to and from the controller */
unregisterCallbacks()320     public void unregisterCallbacks() {
321         mBackgroundHandler.post(mStopListeningRunnable);
322         mControlValueInitialized = false;
323     }
324 
325     @Override
onChanged(boolean tracking, int value, boolean stopTracking)326     public void onChanged(boolean tracking, int value, boolean stopTracking) {
327         if (mExternalChange) return;
328 
329         if (mSliderAnimator != null) {
330             mSliderAnimator.cancel();
331         }
332 
333         final float minBacklight;
334         final float maxBacklight;
335         final int metric;
336 
337         if (mIsVrModeEnabled) {
338             metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR;
339             minBacklight = mMinimumBacklightForVr;
340             maxBacklight = mMaximumBacklightForVr;
341         } else {
342             metric = mAutomatic
343                     ? MetricsEvent.ACTION_BRIGHTNESS_AUTO
344                     : MetricsEvent.ACTION_BRIGHTNESS;
345             minBacklight = mBrightnessMin;
346             maxBacklight = mBrightnessMax;
347         }
348         final float valFloat = MathUtils.min(
349                 convertGammaToLinearFloat(value, minBacklight, maxBacklight),
350                 maxBacklight);
351         if (stopTracking) {
352             // TODO(brightnessfloat): change to use float value instead.
353             MetricsLogger.action(mContext, metric,
354                     BrightnessSynchronizer.brightnessFloatToInt(valFloat));
355 
356         }
357         setBrightness(valFloat);
358         if (!tracking) {
359             AsyncTask.execute(new Runnable() {
360                     public void run() {
361                         mDisplayManager.setBrightness(mDisplayId, valFloat);
362                     }
363                 });
364         }
365     }
366 
checkRestrictionAndSetEnabled()367     public void checkRestrictionAndSetEnabled() {
368         mBackgroundHandler.post(new Runnable() {
369             @Override
370             public void run() {
371                 mControl.setEnforcedAdmin(
372                         RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
373                                 UserManager.DISALLOW_CONFIG_BRIGHTNESS,
374                                 mUserTracker.getUserId()));
375             }
376         });
377     }
378 
hideSlider()379     public void hideSlider() {
380         mControl.hideView();
381     }
382 
showSlider()383     public void showSlider() {
384         mControl.showView();
385     }
386 
setBrightness(float brightness)387     private void setBrightness(float brightness) {
388         mDisplayManager.setTemporaryBrightness(mDisplayId, brightness);
389     }
390 
updateVrMode(boolean isEnabled)391     private void updateVrMode(boolean isEnabled) {
392         if (mIsVrModeEnabled != isEnabled) {
393             mIsVrModeEnabled = isEnabled;
394             mBackgroundHandler.post(mUpdateSliderRunnable);
395         }
396     }
397 
updateSlider(float brightnessValue, boolean inVrMode)398     private void updateSlider(float brightnessValue, boolean inVrMode) {
399         final float min;
400         final float max;
401         if (inVrMode) {
402             min = mMinimumBacklightForVr;
403             max = mMaximumBacklightForVr;
404         } else {
405             min = mBrightnessMin;
406             max = mBrightnessMax;
407         }
408 
409         // Ensure the slider is in a fixed position first, then check if we should animate.
410         if (mSliderAnimator != null && mSliderAnimator.isStarted()) {
411             mSliderAnimator.cancel();
412         }
413         // convertGammaToLinearFloat returns 0-1
414         if (BrightnessSynchronizer.floatEquals(brightnessValue,
415                 convertGammaToLinearFloat(mControl.getValue(), min, max))) {
416             // If the value in the slider is equal to the value on the current brightness
417             // then the slider does not need to animate, since the brightness will not change.
418             return;
419         }
420         // Returns GAMMA_SPACE_MIN - GAMMA_SPACE_MAX
421         final int sliderVal = convertLinearToGammaFloat(brightnessValue, min, max);
422         animateSliderTo(sliderVal);
423     }
424 
animateSliderTo(int target)425     private void animateSliderTo(int target) {
426         if (!mControlValueInitialized || !mControl.isVisible()) {
427             // Don't animate the first value since its default state isn't meaningful to users.
428             // We also don't want to animate slider if it's not visible - especially important when
429             // two sliders are active at the same time in split shade (one in QS and one in QQS),
430             // as this negatively affects transition between them and they share mirror slider -
431             // animating it from two different sources causes janky motion
432             mControl.setValue(target);
433             mControlValueInitialized = true;
434         }
435         mSliderAnimator = ValueAnimator.ofInt(mControl.getValue(), target);
436         mSliderAnimator.addUpdateListener((ValueAnimator animation) -> {
437             mExternalChange = true;
438             mControl.setValue((int) animation.getAnimatedValue());
439             mExternalChange = false;
440         });
441         final long animationDuration = SLIDER_ANIMATION_DURATION * Math.abs(
442                 mControl.getValue() - target) / GAMMA_SPACE_MAX;
443         mSliderAnimator.setDuration(animationDuration);
444         mSliderAnimator.start();
445     }
446 
447     /** Factory for creating a {@link BrightnessController}. */
448     public static class Factory {
449         private final Context mContext;
450         private final UserTracker mUserTracker;
451         private final DisplayTracker mDisplayTracker;
452         private final Executor mMainExecutor;
453         private final Handler mBackgroundHandler;
454 
455         @Inject
Factory( Context context, UserTracker userTracker, DisplayTracker displayTracker, @Main Executor mainExecutor, @Background Handler bgHandler)456         public Factory(
457                 Context context,
458                 UserTracker userTracker,
459                 DisplayTracker displayTracker,
460                 @Main Executor mainExecutor,
461                 @Background Handler bgHandler) {
462             mContext = context;
463             mUserTracker = userTracker;
464             mDisplayTracker = displayTracker;
465             mMainExecutor = mainExecutor;
466             mBackgroundHandler = bgHandler;
467         }
468 
469         /** Create a {@link BrightnessController} */
create(ToggleSlider toggleSlider)470         public BrightnessController create(ToggleSlider toggleSlider) {
471             return new BrightnessController(
472                     mContext,
473                     toggleSlider,
474                     mUserTracker,
475                     mDisplayTracker,
476                     mMainExecutor,
477                     mBackgroundHandler);
478         }
479     }
480 
481 }
482