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