1 /*
2  * Copyright (C) 2014 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.statusbar.phone;
18 
19 import android.content.res.Configuration;
20 import android.content.res.Resources;
21 import android.hardware.display.AmbientDisplayConfiguration;
22 import android.os.PowerManager;
23 import android.os.SystemProperties;
24 import android.os.UserHandle;
25 import android.provider.Settings;
26 import android.util.Log;
27 import android.util.MathUtils;
28 
29 import androidx.annotation.NonNull;
30 import androidx.annotation.VisibleForTesting;
31 
32 import com.android.keyguard.KeyguardUpdateMonitor;
33 import com.android.keyguard.KeyguardUpdateMonitorCallback;
34 import com.android.systemui.Dumpable;
35 import com.android.systemui.R;
36 import com.android.systemui.dagger.SysUISingleton;
37 import com.android.systemui.dagger.qualifiers.Main;
38 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
39 import com.android.systemui.doze.DozeScreenState;
40 import com.android.systemui.dump.DumpManager;
41 import com.android.systemui.flags.FeatureFlags;
42 import com.android.systemui.plugins.statusbar.StatusBarStateController;
43 import com.android.systemui.statusbar.policy.BatteryController;
44 import com.android.systemui.statusbar.policy.ConfigurationController;
45 import com.android.systemui.statusbar.policy.DevicePostureController;
46 import com.android.systemui.tuner.TunerService;
47 
48 import java.io.FileDescriptor;
49 import java.io.PrintWriter;
50 import java.util.HashSet;
51 import java.util.Set;
52 
53 import javax.inject.Inject;
54 
55 /**
56  * Retrieve doze information
57  */
58 @SysUISingleton
59 public class DozeParameters implements
60         TunerService.Tunable,
61         com.android.systemui.plugins.statusbar.DozeParameters,
62         Dumpable, ConfigurationController.ConfigurationListener,
63         StatusBarStateController.StateListener {
64     private static final int MAX_DURATION = 60 * 1000;
65     public static final boolean FORCE_NO_BLANKING =
66             SystemProperties.getBoolean("debug.force_no_blanking", false);
67     public static final boolean FORCE_BLANKING =
68             SystemProperties.getBoolean("debug.force_blanking", false);
69 
70     private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
71     private final PowerManager mPowerManager;
72 
73     private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
74     private final Resources mResources;
75     private final BatteryController mBatteryController;
76     private final FeatureFlags mFeatureFlags;
77     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
78 
79     private final Set<Callback> mCallbacks = new HashSet<>();
80 
81     private boolean mDozeAlwaysOn;
82     private boolean mControlScreenOffAnimation;
83 
84     private boolean mKeyguardShowing;
85     @VisibleForTesting
86     final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
87             new KeyguardUpdateMonitorCallback() {
88                 @Override
89                 public void onKeyguardVisibilityChanged(boolean showing) {
90                     mKeyguardShowing = showing;
91                     updateControlScreenOff();
92                 }
93 
94                 @Override
95                 public void onShadeExpandedChanged(boolean expanded) {
96                     updateControlScreenOff();
97                 }
98             };
99 
100     @Inject
DozeParameters( @ain Resources resources, AmbientDisplayConfiguration ambientDisplayConfiguration, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy, PowerManager powerManager, BatteryController batteryController, TunerService tunerService, DumpManager dumpManager, FeatureFlags featureFlags, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, KeyguardUpdateMonitor keyguardUpdateMonitor, ConfigurationController configurationController, StatusBarStateController statusBarStateController)101     protected DozeParameters(
102             @Main Resources resources,
103             AmbientDisplayConfiguration ambientDisplayConfiguration,
104             AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
105             PowerManager powerManager,
106             BatteryController batteryController,
107             TunerService tunerService,
108             DumpManager dumpManager,
109             FeatureFlags featureFlags,
110             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
111             KeyguardUpdateMonitor keyguardUpdateMonitor,
112             ConfigurationController configurationController,
113             StatusBarStateController statusBarStateController) {
114         mResources = resources;
115         mAmbientDisplayConfiguration = ambientDisplayConfiguration;
116         mAlwaysOnPolicy = alwaysOnDisplayPolicy;
117         mBatteryController = batteryController;
118         dumpManager.registerDumpable("DozeParameters", this);
119 
120         mControlScreenOffAnimation = !getDisplayNeedsBlanking();
121         mPowerManager = powerManager;
122         mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
123         mFeatureFlags = featureFlags;
124         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
125 
126         keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
127         tunerService.addTunable(
128                 this,
129                 Settings.Secure.DOZE_ALWAYS_ON,
130                 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
131         configurationController.addCallback(this);
132         statusBarStateController.addCallback(this);
133     }
134 
getDisplayStateSupported()135     public boolean getDisplayStateSupported() {
136         return getBoolean("doze.display.supported", R.bool.doze_display_state_supported);
137     }
138 
getDozeSuspendDisplayStateSupported()139     public boolean getDozeSuspendDisplayStateSupported() {
140         return mResources.getBoolean(R.bool.doze_suspend_display_state_supported);
141     }
142 
getPulseDuration()143     public int getPulseDuration() {
144         return getPulseInDuration() + getPulseVisibleDuration() + getPulseOutDuration();
145     }
146 
getScreenBrightnessDoze()147     public float getScreenBrightnessDoze() {
148         return mResources.getInteger(
149                 com.android.internal.R.integer.config_screenBrightnessDoze) / 255f;
150     }
151 
getPulseInDuration()152     public int getPulseInDuration() {
153         return getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in);
154     }
155 
getPulseVisibleDuration()156     public int getPulseVisibleDuration() {
157         return getInt("doze.pulse.duration.visible", R.integer.doze_pulse_duration_visible);
158     }
159 
getPulseOutDuration()160     public int getPulseOutDuration() {
161         return getInt("doze.pulse.duration.out", R.integer.doze_pulse_duration_out);
162     }
163 
getPulseOnSigMotion()164     public boolean getPulseOnSigMotion() {
165         return getBoolean("doze.pulse.sigmotion", R.bool.doze_pulse_on_significant_motion);
166     }
167 
getVibrateOnSigMotion()168     public boolean getVibrateOnSigMotion() {
169         return SystemProperties.getBoolean("doze.vibrate.sigmotion", false);
170     }
171 
getVibrateOnPickup()172     public boolean getVibrateOnPickup() {
173         return SystemProperties.getBoolean("doze.vibrate.pickup", false);
174     }
175 
getProxCheckBeforePulse()176     public boolean getProxCheckBeforePulse() {
177         return getBoolean("doze.pulse.proxcheck", R.bool.doze_proximity_check_before_pulse);
178     }
179 
180     /**
181      * @return true if we should only register for sensors that use the proximity sensor when the
182      * display state is {@link android.view.Display.STATE_OFF},
183      * {@link android.view.Display.STATE_DOZE} or {@link android.view.Display.STATE_DOZE_SUSPEND}
184      */
getSelectivelyRegisterSensorsUsingProx()185     public boolean getSelectivelyRegisterSensorsUsingProx() {
186         return getBoolean("doze.prox.selectively_register",
187                 R.bool.doze_selectively_register_prox);
188     }
189 
getPickupVibrationThreshold()190     public int getPickupVibrationThreshold() {
191         return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);
192     }
193 
getQuickPickupAodDuration()194     public int getQuickPickupAodDuration() {
195         return getInt("doze.gesture.quickpickup.duration",
196                 R.integer.doze_quick_pickup_aod_duration);
197     }
198 
199     /**
200      * For how long a wallpaper can be visible in AoD before it fades aways.
201      * @return duration in millis.
202      */
getWallpaperAodDuration()203     public long getWallpaperAodDuration() {
204         if (shouldControlScreenOff()) {
205             return DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY;
206         }
207         return mAlwaysOnPolicy.wallpaperVisibilityDuration;
208     }
209 
210     /**
211      * How long it takes for the wallpaper fade away (Animation duration.)
212      * @return duration in millis.
213      */
getWallpaperFadeOutDuration()214     public long getWallpaperFadeOutDuration() {
215         return mAlwaysOnPolicy.wallpaperFadeOutDuration;
216     }
217 
218     /**
219      * Checks if always on is available and enabled for the current user.
220      * @return {@code true} if enabled and available.
221      */
getAlwaysOn()222     public boolean getAlwaysOn() {
223         return mDozeAlwaysOn && !mBatteryController.isAodPowerSave();
224     }
225 
isQuickPickupEnabled()226     public boolean isQuickPickupEnabled() {
227         return mAmbientDisplayConfiguration.quickPickupSensorEnabled(UserHandle.USER_CURRENT);
228     }
229 
230     /**
231      * Some screens need to be completely black before changing the display power mode,
232      * unexpected behavior might happen if this parameter isn't respected.
233      *
234      * @return {@code true} if screen needs to be completely black before a power transition.
235      */
getDisplayNeedsBlanking()236     public boolean getDisplayNeedsBlanking() {
237         return FORCE_BLANKING || !FORCE_NO_BLANKING && mResources.getBoolean(
238                 com.android.internal.R.bool.config_displayBlanksAfterDoze);
239     }
240 
shouldControlScreenOff()241     public boolean shouldControlScreenOff() {
242         return mControlScreenOffAnimation;
243     }
244 
setControlScreenOffAnimation(boolean controlScreenOffAnimation)245     public void setControlScreenOffAnimation(boolean controlScreenOffAnimation) {
246         if (mControlScreenOffAnimation == controlScreenOffAnimation) {
247             return;
248         }
249         mControlScreenOffAnimation = controlScreenOffAnimation;
250         mPowerManager.setDozeAfterScreenOff(!controlScreenOffAnimation);
251     }
252 
253     /**
254      *
255      */
updateControlScreenOff()256     public void updateControlScreenOff() {
257         if (!getDisplayNeedsBlanking()) {
258             final boolean controlScreenOff =
259                     getAlwaysOn() && (mKeyguardShowing || shouldControlUnlockedScreenOff());
260             setControlScreenOffAnimation(controlScreenOff);
261         }
262     }
263 
264     /**
265      * Whether we want to control the screen off animation when the device is unlocked. If we do,
266      * we'll animate in AOD before turning off the screen, rather than simply fading to black and
267      * then abruptly showing AOD.
268      *
269      * There are currently several reasons we might not want to control the screen off even if we
270      * are able to, such as the shade being expanded, being in landscape, or having animations
271      * disabled for a11y.
272      */
shouldControlUnlockedScreenOff()273     public boolean shouldControlUnlockedScreenOff() {
274         return canControlUnlockedScreenOff()
275                 && mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation();
276     }
277 
278     /**
279      * Whether we're capable of controlling the screen off animation if we want to. This isn't
280      * possible if AOD isn't even enabled or if the flag is disabled.
281      */
canControlUnlockedScreenOff()282     public boolean canControlUnlockedScreenOff() {
283         return getAlwaysOn()
284                 && mFeatureFlags.useNewLockscreenAnimations()
285                 && !getDisplayNeedsBlanking();
286     }
287 
getBoolean(String propName, int resId)288     private boolean getBoolean(String propName, int resId) {
289         return SystemProperties.getBoolean(propName, mResources.getBoolean(resId));
290     }
291 
getInt(String propName, int resId)292     private int getInt(String propName, int resId) {
293         int value = SystemProperties.getInt(propName, mResources.getInteger(resId));
294         return MathUtils.constrain(value, 0, MAX_DURATION);
295     }
296 
getPulseVisibleDurationExtended()297     public int getPulseVisibleDurationExtended() {
298         return 2 * getPulseVisibleDuration();
299     }
300 
doubleTapReportsTouchCoordinates()301     public boolean doubleTapReportsTouchCoordinates() {
302         return mResources.getBoolean(R.bool.doze_double_tap_reports_touch_coordinates);
303     }
304 
305     /**
306      * Whether the single tap sensor uses the proximity sensor for this device posture.
307      */
singleTapUsesProx(@evicePostureController.DevicePostureInt int devicePosture)308     public boolean singleTapUsesProx(@DevicePostureController.DevicePostureInt int devicePosture) {
309         return getPostureSpecificBool(
310                 mResources.getIntArray(R.array.doze_single_tap_uses_prox_posture_mapping),
311                 singleTapUsesProx(),
312                 devicePosture
313         );
314     }
315 
316     /**
317      * Whether the single tap sensor uses the proximity sensor.
318      */
singleTapUsesProx()319     private boolean singleTapUsesProx() {
320         return mResources.getBoolean(R.bool.doze_single_tap_uses_prox);
321     }
322 
323     /**
324      * Whether the long press sensor uses the proximity sensor.
325      */
longPressUsesProx()326     public boolean longPressUsesProx() {
327         return mResources.getBoolean(R.bool.doze_long_press_uses_prox);
328     }
329 
330     /**
331      * Gets the brightness string array per posture. Brightness names along with
332      * doze_brightness_sensor_type is used to determine the brightness sensor to use for
333      * the current posture.
334      */
brightnessNames()335     public String[] brightnessNames() {
336         return mResources.getStringArray(R.array.doze_brightness_sensor_name_posture_mapping);
337     }
338 
339     /**
340      * Callback to listen for DozeParameter changes.
341      */
addCallback(Callback callback)342     public void addCallback(Callback callback) {
343         mCallbacks.add(callback);
344     }
345 
346     /**
347      * Remove callback that listens for DozeParameter changes.
348      */
removeCallback(Callback callback)349     public void removeCallback(Callback callback) {
350         mCallbacks.remove(callback);
351     }
352 
353     @Override
onTuningChanged(String key, String newValue)354     public void onTuningChanged(String key, String newValue) {
355         mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
356 
357         if (key.equals(Settings.Secure.DOZE_ALWAYS_ON)) {
358             updateControlScreenOff();
359         }
360 
361         for (Callback callback : mCallbacks) {
362             callback.onAlwaysOnChange();
363         }
364     }
365 
366     @Override
onConfigChanged(Configuration newConfig)367     public void onConfigChanged(Configuration newConfig) {
368         updateControlScreenOff();
369     }
370 
371     @Override
onStatePostChange()372     public void onStatePostChange() {
373         updateControlScreenOff();
374     }
375 
376     @Override
dump(@onNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args)377     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
378         pw.print("getAlwaysOn(): "); pw.println(getAlwaysOn());
379         pw.print("getDisplayStateSupported(): "); pw.println(getDisplayStateSupported());
380         pw.print("getPulseDuration(): "); pw.println(getPulseDuration());
381         pw.print("getPulseInDuration(): "); pw.println(getPulseInDuration());
382         pw.print("getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration());
383         pw.print("getPulseOutDuration(): "); pw.println(getPulseOutDuration());
384         pw.print("getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion());
385         pw.print("getVibrateOnSigMotion(): "); pw.println(getVibrateOnSigMotion());
386         pw.print("getVibrateOnPickup(): "); pw.println(getVibrateOnPickup());
387         pw.print("getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse());
388         pw.print("getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
389         pw.print("getSelectivelyRegisterSensorsUsingProx(): ");
390         pw.println(getSelectivelyRegisterSensorsUsingProx());
391     }
392 
getPostureSpecificBool( int[] postureMapping, boolean defaultSensorBool, int posture)393     private boolean getPostureSpecificBool(
394             int[] postureMapping,
395             boolean defaultSensorBool,
396             int posture) {
397         boolean bool = defaultSensorBool;
398         if (posture < postureMapping.length) {
399             bool = postureMapping[posture] != 0;
400         } else {
401             Log.e("DozeParameters", "Unsupported doze posture " + posture);
402         }
403 
404         return bool;
405     }
406 
407     interface Callback {
408         /**
409          * Invoked when the value of getAlwaysOn may have changed.
410          */
onAlwaysOnChange()411         void onAlwaysOnChange();
412     }
413 }
414