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.Context; 20 import android.hardware.display.AmbientDisplayConfiguration; 21 import android.os.PowerManager; 22 import android.os.SystemProperties; 23 import android.os.UserHandle; 24 import android.provider.Settings; 25 import android.text.TextUtils; 26 import android.util.MathUtils; 27 import android.util.SparseBooleanArray; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 import com.android.systemui.Dependency; 31 import com.android.systemui.R; 32 import com.android.systemui.doze.AlwaysOnDisplayPolicy; 33 import com.android.systemui.doze.DozeScreenState; 34 import com.android.systemui.tuner.TunerService; 35 36 import java.io.PrintWriter; 37 38 public class DozeParameters implements TunerService.Tunable { 39 private static final int MAX_DURATION = 60 * 1000; 40 public static final String DOZE_SENSORS_WAKE_UP_FULLY = "doze_sensors_wake_up_fully"; 41 public static final boolean FORCE_NO_BLANKING = 42 SystemProperties.getBoolean("debug.force_no_blanking", false); 43 public static final boolean FORCE_BLANKING = 44 SystemProperties.getBoolean("debug.force_blanking", false); 45 46 private static IntInOutMatcher sPickupSubtypePerformsProxMatcher; 47 private static DozeParameters sInstance; 48 49 private final Context mContext; 50 private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; 51 private final PowerManager mPowerManager; 52 53 private final AlwaysOnDisplayPolicy mAlwaysOnPolicy; 54 55 private boolean mDozeAlwaysOn; 56 private boolean mControlScreenOffAnimation; 57 getInstance(Context context)58 public static DozeParameters getInstance(Context context) { 59 if (sInstance == null) { 60 sInstance = new DozeParameters(context); 61 } 62 return sInstance; 63 } 64 65 @VisibleForTesting DozeParameters(Context context)66 protected DozeParameters(Context context) { 67 mContext = context.getApplicationContext(); 68 mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext); 69 mAlwaysOnPolicy = new AlwaysOnDisplayPolicy(mContext); 70 71 mControlScreenOffAnimation = !getDisplayNeedsBlanking(); 72 mPowerManager = mContext.getSystemService(PowerManager.class); 73 mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation); 74 75 Dependency.get(TunerService.class).addTunable(this, Settings.Secure.DOZE_ALWAYS_ON, 76 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); 77 } 78 dump(PrintWriter pw)79 public void dump(PrintWriter pw) { 80 pw.println(" DozeParameters:"); 81 pw.print(" getDisplayStateSupported(): "); pw.println(getDisplayStateSupported()); 82 pw.print(" getPulseDuration(): "); pw.println(getPulseDuration()); 83 pw.print(" getPulseInDuration(): "); pw.println(getPulseInDuration()); 84 pw.print(" getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration()); 85 pw.print(" getPulseOutDuration(): "); pw.println(getPulseOutDuration()); 86 pw.print(" getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion()); 87 pw.print(" getVibrateOnSigMotion(): "); pw.println(getVibrateOnSigMotion()); 88 pw.print(" getVibrateOnPickup(): "); pw.println(getVibrateOnPickup()); 89 pw.print(" getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse()); 90 pw.print(" getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold()); 91 pw.print(" getPickupSubtypePerformsProxCheck(): ");pw.println( 92 dumpPickupSubtypePerformsProxCheck()); 93 } 94 dumpPickupSubtypePerformsProxCheck()95 private String dumpPickupSubtypePerformsProxCheck() { 96 // Refresh sPickupSubtypePerformsProxMatcher 97 getPickupSubtypePerformsProxCheck(0); 98 99 if (sPickupSubtypePerformsProxMatcher == null) { 100 return "fallback: " + mContext.getResources().getBoolean( 101 R.bool.doze_pickup_performs_proximity_check); 102 } else { 103 return "spec: " + sPickupSubtypePerformsProxMatcher.mSpec; 104 } 105 } 106 getDisplayStateSupported()107 public boolean getDisplayStateSupported() { 108 return getBoolean("doze.display.supported", R.bool.doze_display_state_supported); 109 } 110 getDozeSuspendDisplayStateSupported()111 public boolean getDozeSuspendDisplayStateSupported() { 112 return mContext.getResources().getBoolean(R.bool.doze_suspend_display_state_supported); 113 } 114 getPulseDuration()115 public int getPulseDuration() { 116 return getPulseInDuration() + getPulseVisibleDuration() + getPulseOutDuration(); 117 } 118 getScreenBrightnessDoze()119 public float getScreenBrightnessDoze() { 120 return mContext.getResources().getInteger( 121 com.android.internal.R.integer.config_screenBrightnessDoze) / 255f; 122 } 123 getPulseInDuration()124 public int getPulseInDuration() { 125 return getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in); 126 } 127 getPulseVisibleDuration()128 public int getPulseVisibleDuration() { 129 return getInt("doze.pulse.duration.visible", R.integer.doze_pulse_duration_visible); 130 } 131 getPulseOutDuration()132 public int getPulseOutDuration() { 133 return getInt("doze.pulse.duration.out", R.integer.doze_pulse_duration_out); 134 } 135 getPulseOnSigMotion()136 public boolean getPulseOnSigMotion() { 137 return getBoolean("doze.pulse.sigmotion", R.bool.doze_pulse_on_significant_motion); 138 } 139 getVibrateOnSigMotion()140 public boolean getVibrateOnSigMotion() { 141 return SystemProperties.getBoolean("doze.vibrate.sigmotion", false); 142 } 143 getVibrateOnPickup()144 public boolean getVibrateOnPickup() { 145 return SystemProperties.getBoolean("doze.vibrate.pickup", false); 146 } 147 getProxCheckBeforePulse()148 public boolean getProxCheckBeforePulse() { 149 return getBoolean("doze.pulse.proxcheck", R.bool.doze_proximity_check_before_pulse); 150 } 151 getPickupVibrationThreshold()152 public int getPickupVibrationThreshold() { 153 return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold); 154 } 155 156 /** 157 * For how long a wallpaper can be visible in AoD before it fades aways. 158 * @return duration in millis. 159 */ getWallpaperAodDuration()160 public long getWallpaperAodDuration() { 161 if (shouldControlScreenOff()) { 162 return DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY; 163 } 164 return mAlwaysOnPolicy.wallpaperVisibilityDuration; 165 } 166 167 /** 168 * How long it takes for the wallpaper fade away (Animation duration.) 169 * @return duration in millis. 170 */ getWallpaperFadeOutDuration()171 public long getWallpaperFadeOutDuration() { 172 return mAlwaysOnPolicy.wallpaperFadeOutDuration; 173 } 174 175 /** 176 * Checks if always on is available and enabled for the current user. 177 * @return {@code true} if enabled and available. 178 */ getAlwaysOn()179 public boolean getAlwaysOn() { 180 return mDozeAlwaysOn; 181 } 182 183 /** 184 * Some screens need to be completely black before changing the display power mode, 185 * unexpected behavior might happen if this parameter isn't respected. 186 * 187 * @return {@code true} if screen needs to be completely black before a power transition. 188 */ getDisplayNeedsBlanking()189 public boolean getDisplayNeedsBlanking() { 190 return FORCE_BLANKING || !FORCE_NO_BLANKING && mContext.getResources().getBoolean( 191 com.android.internal.R.bool.config_displayBlanksAfterDoze); 192 } 193 shouldControlScreenOff()194 public boolean shouldControlScreenOff() { 195 return mControlScreenOffAnimation; 196 } 197 setControlScreenOffAnimation(boolean controlScreenOffAnimation)198 public void setControlScreenOffAnimation(boolean controlScreenOffAnimation) { 199 if (mControlScreenOffAnimation == controlScreenOffAnimation) { 200 return; 201 } 202 mControlScreenOffAnimation = controlScreenOffAnimation; 203 getPowerManager().setDozeAfterScreenOff(!controlScreenOffAnimation); 204 } 205 206 @VisibleForTesting getPowerManager()207 protected PowerManager getPowerManager() { 208 return mPowerManager; 209 } 210 getBoolean(String propName, int resId)211 private boolean getBoolean(String propName, int resId) { 212 return SystemProperties.getBoolean(propName, mContext.getResources().getBoolean(resId)); 213 } 214 getInt(String propName, int resId)215 private int getInt(String propName, int resId) { 216 int value = SystemProperties.getInt(propName, mContext.getResources().getInteger(resId)); 217 return MathUtils.constrain(value, 0, MAX_DURATION); 218 } 219 getString(String propName, int resId)220 private String getString(String propName, int resId) { 221 return SystemProperties.get(propName, mContext.getString(resId)); 222 } 223 getPickupSubtypePerformsProxCheck(int subType)224 public boolean getPickupSubtypePerformsProxCheck(int subType) { 225 String spec = getString("doze.pickup.proxcheck", 226 R.string.doze_pickup_subtype_performs_proximity_check); 227 228 if (TextUtils.isEmpty(spec)) { 229 // Fall back to non-subtype based property. 230 return mContext.getResources().getBoolean(R.bool.doze_pickup_performs_proximity_check); 231 } 232 233 if (sPickupSubtypePerformsProxMatcher == null 234 || !TextUtils.equals(spec, sPickupSubtypePerformsProxMatcher.mSpec)) { 235 sPickupSubtypePerformsProxMatcher = new IntInOutMatcher(spec); 236 } 237 238 return sPickupSubtypePerformsProxMatcher.isIn(subType); 239 } 240 getPulseVisibleDurationExtended()241 public int getPulseVisibleDurationExtended() { 242 return 2 * getPulseVisibleDuration(); 243 } 244 doubleTapReportsTouchCoordinates()245 public boolean doubleTapReportsTouchCoordinates() { 246 return mContext.getResources().getBoolean(R.bool.doze_double_tap_reports_touch_coordinates); 247 } 248 249 @Override onTuningChanged(String key, String newValue)250 public void onTuningChanged(String key, String newValue) { 251 mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT); 252 } 253 getPolicy()254 public AlwaysOnDisplayPolicy getPolicy() { 255 return mAlwaysOnPolicy; 256 } 257 258 /** 259 * Parses a spec of the form `1,2,3,!5,*`. The resulting object will match numbers that are 260 * listed, will not match numbers that are listed with a ! prefix, and will match / not match 261 * unlisted numbers depending on whether * or !* is present. 262 * 263 * * -> match any numbers that are not explicitly listed 264 * !* -> don't match any numbers that are not explicitly listed 265 * 2 -> match 2 266 * !3 -> don't match 3 267 * 268 * It is illegal to specify: 269 * - an empty spec 270 * - a spec containing that are empty, or a lone ! 271 * - a spec for anything other than numbers or * 272 * - multiple terms for the same number / multiple *s 273 */ 274 public static class IntInOutMatcher { 275 private static final String WILDCARD = "*"; 276 private static final char OUT_PREFIX = '!'; 277 278 private final SparseBooleanArray mIsIn; 279 private final boolean mDefaultIsIn; 280 final String mSpec; 281 IntInOutMatcher(String spec)282 public IntInOutMatcher(String spec) { 283 if (TextUtils.isEmpty(spec)) { 284 throw new IllegalArgumentException("Spec must not be empty"); 285 } 286 287 boolean defaultIsIn = false; 288 boolean foundWildcard = false; 289 290 mSpec = spec; 291 mIsIn = new SparseBooleanArray(); 292 293 for (String itemPrefixed : spec.split(",", -1)) { 294 if (itemPrefixed.length() == 0) { 295 throw new IllegalArgumentException( 296 "Illegal spec, must not have zero-length items: `" + spec + "`"); 297 } 298 boolean isIn = itemPrefixed.charAt(0) != OUT_PREFIX; 299 String item = isIn ? itemPrefixed : itemPrefixed.substring(1); 300 301 if (itemPrefixed.length() == 0) { 302 throw new IllegalArgumentException( 303 "Illegal spec, must not have zero-length items: `" + spec + "`"); 304 } 305 306 if (WILDCARD.equals(item)) { 307 if (foundWildcard) { 308 throw new IllegalArgumentException("Illegal spec, `" + WILDCARD + 309 "` must not appear multiple times in `" + spec + "`"); 310 } 311 defaultIsIn = isIn; 312 foundWildcard = true; 313 } else { 314 int key = Integer.parseInt(item); 315 if (mIsIn.indexOfKey(key) >= 0) { 316 throw new IllegalArgumentException("Illegal spec, `" + key + 317 "` must not appear multiple times in `" + spec + "`"); 318 } 319 mIsIn.put(key, isIn); 320 } 321 } 322 323 if (!foundWildcard) { 324 throw new IllegalArgumentException("Illegal spec, must specify either * or !*"); 325 } 326 327 mDefaultIsIn = defaultIsIn; 328 } 329 isIn(int value)330 public boolean isIn(int value) { 331 return (mIsIn.get(value, mDefaultIsIn)); 332 } 333 } 334 } 335