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 android.hardware.display; 18 19 import android.annotation.TestApi; 20 import android.content.Context; 21 import android.os.Build; 22 import android.os.SystemProperties; 23 import android.provider.Settings; 24 import android.text.TextUtils; 25 import android.util.ArrayMap; 26 import android.util.SparseArray; 27 28 import com.android.internal.R; 29 import com.android.internal.util.ArrayUtils; 30 31 import java.util.Map; 32 33 /** 34 * AmbientDisplayConfiguration encapsulates reading access to the configuration of ambient display. 35 * 36 * @hide 37 */ 38 @TestApi 39 public class AmbientDisplayConfiguration { 40 private static final String TAG = "AmbientDisplayConfig"; 41 private final Context mContext; 42 private final boolean mAlwaysOnByDefault; 43 private final boolean mPickupGestureEnabledByDefault; 44 45 /** Copied from android.provider.Settings.Secure since these keys are hidden. */ 46 private static final String[] DOZE_SETTINGS = { 47 Settings.Secure.DOZE_ENABLED, 48 Settings.Secure.DOZE_ALWAYS_ON, 49 Settings.Secure.DOZE_PICK_UP_GESTURE, 50 Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, 51 Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, 52 Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, 53 Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, 54 Settings.Secure.DOZE_TAP_SCREEN_GESTURE 55 }; 56 57 /** Non-user configurable doze settings */ 58 private static final String[] NON_USER_CONFIGURABLE_DOZE_SETTINGS = { 59 Settings.Secure.DOZE_QUICK_PICKUP_GESTURE 60 }; 61 62 final SparseArray<Map<String, String>> mUsersInitialValues = new SparseArray<>(); 63 64 /** @hide */ 65 @TestApi AmbientDisplayConfiguration(Context context)66 public AmbientDisplayConfiguration(Context context) { 67 mContext = context; 68 mAlwaysOnByDefault = mContext.getResources().getBoolean(R.bool.config_dozeAlwaysOnEnabled); 69 mPickupGestureEnabledByDefault = 70 mContext.getResources().getBoolean(R.bool.config_dozePickupGestureEnabled); 71 } 72 73 /** @hide */ enabled(int user)74 public boolean enabled(int user) { 75 return pulseOnNotificationEnabled(user) 76 || pulseOnLongPressEnabled(user) 77 || alwaysOnEnabled(user) 78 || wakeLockScreenGestureEnabled(user) 79 || wakeDisplayGestureEnabled(user) 80 || pickupGestureEnabled(user) 81 || tapGestureEnabled(user) 82 || doubleTapGestureEnabled(user) 83 || quickPickupSensorEnabled(user) 84 || screenOffUdfpsEnabled(user); 85 } 86 87 /** @hide */ pulseOnNotificationEnabled(int user)88 public boolean pulseOnNotificationEnabled(int user) { 89 return boolSettingDefaultOn(Settings.Secure.DOZE_ENABLED, user) 90 && pulseOnNotificationAvailable(); 91 } 92 93 /** @hide */ pulseOnNotificationAvailable()94 public boolean pulseOnNotificationAvailable() { 95 return mContext.getResources().getBoolean(R.bool.config_pulseOnNotificationsAvailable) 96 && ambientDisplayAvailable(); 97 } 98 99 /** @hide */ pickupGestureEnabled(int user)100 public boolean pickupGestureEnabled(int user) { 101 return boolSetting(Settings.Secure.DOZE_PICK_UP_GESTURE, user, 102 mPickupGestureEnabledByDefault ? 1 : 0) 103 && dozePickupSensorAvailable(); 104 } 105 106 /** @hide */ dozePickupSensorAvailable()107 public boolean dozePickupSensorAvailable() { 108 return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup); 109 } 110 111 /** @hide */ tapGestureEnabled(int user)112 public boolean tapGestureEnabled(int user) { 113 return boolSettingDefaultOn(Settings.Secure.DOZE_TAP_SCREEN_GESTURE, user) 114 && tapSensorAvailable(); 115 } 116 117 /** @hide */ tapSensorAvailable()118 public boolean tapSensorAvailable() { 119 for (String tapType : tapSensorTypeMapping()) { 120 if (!TextUtils.isEmpty(tapType)) { 121 return true; 122 } 123 } 124 return false; 125 } 126 127 /** @hide */ doubleTapGestureEnabled(int user)128 public boolean doubleTapGestureEnabled(int user) { 129 return boolSettingDefaultOn(Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, user) 130 && doubleTapSensorAvailable(); 131 } 132 133 /** @hide */ doubleTapSensorAvailable()134 public boolean doubleTapSensorAvailable() { 135 return !TextUtils.isEmpty(doubleTapSensorType()); 136 } 137 138 /** @hide */ quickPickupSensorEnabled(int user)139 public boolean quickPickupSensorEnabled(int user) { 140 return boolSettingDefaultOn(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE, user) 141 && !TextUtils.isEmpty(quickPickupSensorType()) 142 && pickupGestureEnabled(user) 143 && !alwaysOnEnabled(user); 144 } 145 146 /** @hide */ screenOffUdfpsEnabled(int user)147 public boolean screenOffUdfpsEnabled(int user) { 148 return !TextUtils.isEmpty(udfpsLongPressSensorType()) 149 && boolSettingDefaultOff("screen_off_udfps_enabled", user); 150 } 151 152 /** @hide */ wakeScreenGestureAvailable()153 public boolean wakeScreenGestureAvailable() { 154 return mContext.getResources() 155 .getBoolean(R.bool.config_dozeWakeLockScreenSensorAvailable); 156 } 157 158 /** @hide */ wakeLockScreenGestureEnabled(int user)159 public boolean wakeLockScreenGestureEnabled(int user) { 160 return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, user) 161 && wakeScreenGestureAvailable(); 162 } 163 164 /** @hide */ wakeDisplayGestureEnabled(int user)165 public boolean wakeDisplayGestureEnabled(int user) { 166 return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, user) 167 && wakeScreenGestureAvailable(); 168 } 169 170 /** @hide */ getWakeLockScreenDebounce()171 public long getWakeLockScreenDebounce() { 172 return mContext.getResources().getInteger(R.integer.config_dozeWakeLockScreenDebounce); 173 } 174 175 /** @hide */ doubleTapSensorType()176 public String doubleTapSensorType() { 177 return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType); 178 } 179 180 /** @hide 181 * May support multiple postures. 182 */ tapSensorTypeMapping()183 public String[] tapSensorTypeMapping() { 184 String[] postureMapping = 185 mContext.getResources().getStringArray(R.array.config_dozeTapSensorPostureMapping); 186 if (ArrayUtils.isEmpty(postureMapping)) { 187 return new String[] { 188 mContext.getResources().getString(R.string.config_dozeTapSensorType) 189 }; 190 } 191 return postureMapping; 192 } 193 194 /** @hide */ longPressSensorType()195 public String longPressSensorType() { 196 return mContext.getResources().getString(R.string.config_dozeLongPressSensorType); 197 } 198 199 /** @hide */ udfpsLongPressSensorType()200 public String udfpsLongPressSensorType() { 201 return mContext.getResources().getString(R.string.config_dozeUdfpsLongPressSensorType); 202 } 203 204 /** @hide */ quickPickupSensorType()205 public String quickPickupSensorType() { 206 return mContext.getResources().getString(R.string.config_quickPickupSensorType); 207 } 208 209 /** @hide */ pulseOnLongPressEnabled(int user)210 public boolean pulseOnLongPressEnabled(int user) { 211 return pulseOnLongPressAvailable() && boolSettingDefaultOff( 212 Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user); 213 } 214 pulseOnLongPressAvailable()215 private boolean pulseOnLongPressAvailable() { 216 return !TextUtils.isEmpty(longPressSensorType()); 217 } 218 219 /** 220 * Returns if Always-on-Display functionality is enabled on the display for a specified user. 221 * 222 * @hide 223 */ 224 @TestApi alwaysOnEnabled(int user)225 public boolean alwaysOnEnabled(int user) { 226 return boolSetting(Settings.Secure.DOZE_ALWAYS_ON, user, mAlwaysOnByDefault ? 1 : 0) 227 && alwaysOnAvailable() && !accessibilityInversionEnabled(user); 228 } 229 230 /** 231 * Returns if Always-on-Display functionality is available on the display. 232 * 233 * @hide 234 */ 235 @TestApi alwaysOnAvailable()236 public boolean alwaysOnAvailable() { 237 return (alwaysOnDisplayDebuggingEnabled() || alwaysOnDisplayAvailable()) 238 && ambientDisplayAvailable(); 239 } 240 241 /** 242 * Returns if Always-on-Display functionality is available on the display for a specified user. 243 * 244 * @hide 245 */ 246 @TestApi alwaysOnAvailableForUser(int user)247 public boolean alwaysOnAvailableForUser(int user) { 248 return alwaysOnAvailable() && !accessibilityInversionEnabled(user); 249 } 250 251 /** @hide */ ambientDisplayComponent()252 public String ambientDisplayComponent() { 253 return mContext.getResources().getString(R.string.config_dozeComponent); 254 } 255 256 /** @hide */ accessibilityInversionEnabled(int user)257 public boolean accessibilityInversionEnabled(int user) { 258 return boolSettingDefaultOff(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, user); 259 } 260 261 /** @hide */ ambientDisplayAvailable()262 public boolean ambientDisplayAvailable() { 263 return !TextUtils.isEmpty(ambientDisplayComponent()); 264 } 265 266 /** @hide */ dozeSuppressed(int user)267 public boolean dozeSuppressed(int user) { 268 return boolSettingDefaultOff(Settings.Secure.SUPPRESS_DOZE, user); 269 } 270 alwaysOnDisplayAvailable()271 private boolean alwaysOnDisplayAvailable() { 272 return mContext.getResources().getBoolean(R.bool.config_dozeAlwaysOnDisplayAvailable); 273 } 274 alwaysOnDisplayDebuggingEnabled()275 private boolean alwaysOnDisplayDebuggingEnabled() { 276 return SystemProperties.getBoolean("debug.doze.aod", false) && Build.IS_DEBUGGABLE; 277 } 278 boolSettingDefaultOn(String name, int user)279 private boolean boolSettingDefaultOn(String name, int user) { 280 return boolSetting(name, user, 1); 281 } 282 boolSettingDefaultOff(String name, int user)283 private boolean boolSettingDefaultOff(String name, int user) { 284 return boolSetting(name, user, 0); 285 } 286 boolSetting(String name, int user, int def)287 private boolean boolSetting(String name, int user, int def) { 288 return Settings.Secure.getIntForUser(mContext.getContentResolver(), name, def, user) != 0; 289 } 290 291 /** @hide */ 292 @TestApi disableDozeSettings(int userId)293 public void disableDozeSettings(int userId) { 294 disableDozeSettings(false /* shouldDisableNonUserConfigurable */, userId); 295 } 296 297 /** @hide */ 298 @TestApi disableDozeSettings(boolean shouldDisableNonUserConfigurable, int userId)299 public void disableDozeSettings(boolean shouldDisableNonUserConfigurable, int userId) { 300 Map<String, String> initialValues = mUsersInitialValues.get(userId); 301 if (initialValues != null && !initialValues.isEmpty()) { 302 throw new IllegalStateException("Don't call #disableDozeSettings more than once," 303 + "without first calling #restoreDozeSettings"); 304 } 305 initialValues = new ArrayMap<>(); 306 for (String name : DOZE_SETTINGS) { 307 initialValues.put(name, getDozeSetting(name, userId)); 308 putDozeSetting(name, "0", userId); 309 } 310 if (shouldDisableNonUserConfigurable) { 311 for (String name : NON_USER_CONFIGURABLE_DOZE_SETTINGS) { 312 initialValues.put(name, getDozeSetting(name, userId)); 313 putDozeSetting(name, "0", userId); 314 } 315 } 316 mUsersInitialValues.put(userId, initialValues); 317 } 318 319 /** @hide */ 320 @TestApi restoreDozeSettings(int userId)321 public void restoreDozeSettings(int userId) { 322 final Map<String, String> initialValues = mUsersInitialValues.get(userId); 323 if (initialValues != null && !initialValues.isEmpty()) { 324 for (String name : DOZE_SETTINGS) { 325 putDozeSetting(name, initialValues.get(name), userId); 326 } 327 mUsersInitialValues.remove(userId); 328 } 329 } 330 getDozeSetting(String name, int userId)331 private String getDozeSetting(String name, int userId) { 332 return Settings.Secure.getStringForUser(mContext.getContentResolver(), name, userId); 333 } 334 putDozeSetting(String name, String value, int userId)335 private void putDozeSetting(String name, String value, int userId) { 336 Settings.Secure.putStringForUser(mContext.getContentResolver(), name, value, userId); 337 } 338 } 339