1 /* 2 * Copyright (C) 2022 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.os.vibrator; 18 19 import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY; 20 import static android.os.VibrationAttributes.USAGE_ALARM; 21 import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST; 22 import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK; 23 import static android.os.VibrationAttributes.USAGE_IME_FEEDBACK; 24 import static android.os.VibrationAttributes.USAGE_MEDIA; 25 import static android.os.VibrationAttributes.USAGE_NOTIFICATION; 26 import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION; 27 import static android.os.VibrationAttributes.USAGE_RINGTONE; 28 import static android.os.VibrationAttributes.USAGE_TOUCH; 29 import static android.os.VibrationAttributes.USAGE_UNKNOWN; 30 31 import android.annotation.Nullable; 32 import android.content.res.Resources; 33 import android.os.VibrationAttributes; 34 import android.os.Vibrator; 35 import android.os.Vibrator.VibrationIntensity; 36 import android.util.IndentingPrintWriter; 37 38 import java.io.PrintWriter; 39 import java.util.Arrays; 40 41 /** 42 * List of device-specific internal vibration configuration loaded from platform config.xml. 43 * 44 * <p>This should not be public, but some individual values are exposed by {@link Vibrator} by 45 * hidden methods, made available to Settings, SysUI and other platform client code. They can also 46 * be individually exposed with the necessary permissions by the {@link Vibrator} service. 47 * 48 * @hide 49 */ 50 public class VibrationConfig { 51 52 /** 53 * Hardcoded default scale level gain to be applied between each scale level to define their 54 * scale factor value. 55 * 56 * <p>Default gain defined as 3 dBs. 57 */ 58 private static final float DEFAULT_SCALE_LEVEL_GAIN = 1.4f; 59 60 /** 61 * Hardcoded default amplitude to be used when device config is invalid, i.e. not in [1,255]. 62 */ 63 private static final int DEFAULT_AMPLITUDE = 255; 64 65 // TODO(b/191150049): move these to vibrator static config file 66 private final float mHapticChannelMaxVibrationAmplitude; 67 private final int mDefaultVibrationAmplitude; 68 private final int mRampStepDurationMs; 69 private final int mRampDownDurationMs; 70 private final int mRequestVibrationParamsTimeoutMs; 71 private final int[] mRequestVibrationParamsForUsages; 72 73 private final boolean mIgnoreVibrationsOnWirelessCharger; 74 75 @VibrationIntensity 76 private final int mDefaultAlarmVibrationIntensity; 77 @VibrationIntensity 78 private final int mDefaultHapticFeedbackIntensity; 79 @VibrationIntensity 80 private final int mDefaultMediaVibrationIntensity; 81 @VibrationIntensity 82 private final int mDefaultNotificationVibrationIntensity; 83 @VibrationIntensity 84 private final int mDefaultRingVibrationIntensity; 85 @VibrationIntensity 86 private final int mDefaultKeyboardVibrationIntensity; 87 88 private final boolean mKeyboardVibrationSettingsSupported; 89 private final int mVibrationPipelineMaxDurationMs; 90 91 /** @hide */ VibrationConfig(@ullable Resources resources)92 public VibrationConfig(@Nullable Resources resources) { 93 mDefaultVibrationAmplitude = resources.getInteger( 94 com.android.internal.R.integer.config_defaultVibrationAmplitude); 95 mHapticChannelMaxVibrationAmplitude = loadFloat(resources, 96 com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude); 97 mRampDownDurationMs = loadInteger(resources, 98 com.android.internal.R.integer.config_vibrationWaveformRampDownDuration, 0); 99 mRampStepDurationMs = loadInteger(resources, 100 com.android.internal.R.integer.config_vibrationWaveformRampStepDuration, 0); 101 mRequestVibrationParamsTimeoutMs = loadInteger(resources, 102 com.android.internal.R.integer.config_requestVibrationParamsTimeout, 0); 103 mRequestVibrationParamsForUsages = loadIntArray(resources, 104 com.android.internal.R.array.config_requestVibrationParamsForUsages); 105 106 mIgnoreVibrationsOnWirelessCharger = loadBoolean(resources, 107 com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger); 108 mKeyboardVibrationSettingsSupported = loadBoolean(resources, 109 com.android.internal.R.bool.config_keyboardVibrationSettingsSupported); 110 mVibrationPipelineMaxDurationMs = loadInteger(resources, 111 com.android.internal.R.integer.config_vibrationPipelineMaxDuration, 0); 112 113 mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources, 114 com.android.internal.R.integer.config_defaultAlarmVibrationIntensity); 115 mDefaultHapticFeedbackIntensity = loadDefaultIntensity(resources, 116 com.android.internal.R.integer.config_defaultHapticFeedbackIntensity); 117 mDefaultMediaVibrationIntensity = loadDefaultIntensity(resources, 118 com.android.internal.R.integer.config_defaultMediaVibrationIntensity); 119 mDefaultNotificationVibrationIntensity = loadDefaultIntensity(resources, 120 com.android.internal.R.integer.config_defaultNotificationVibrationIntensity); 121 mDefaultRingVibrationIntensity = loadDefaultIntensity(resources, 122 com.android.internal.R.integer.config_defaultRingVibrationIntensity); 123 mDefaultKeyboardVibrationIntensity = loadDefaultIntensity(resources, 124 com.android.internal.R.integer.config_defaultKeyboardVibrationIntensity); 125 } 126 127 @VibrationIntensity loadDefaultIntensity(@ullable Resources res, int resId)128 private static int loadDefaultIntensity(@Nullable Resources res, int resId) { 129 int defaultIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM; 130 int value = loadInteger(res, resId, defaultIntensity); 131 if (value < Vibrator.VIBRATION_INTENSITY_OFF || value > Vibrator.VIBRATION_INTENSITY_HIGH) { 132 return defaultIntensity; 133 } 134 return value; 135 } 136 loadFloat(@ullable Resources res, int resId)137 private static float loadFloat(@Nullable Resources res, int resId) { 138 return res != null ? res.getFloat(resId) : 0f; 139 } 140 loadInteger(@ullable Resources res, int resId, int defaultValue)141 private static int loadInteger(@Nullable Resources res, int resId, int defaultValue) { 142 return res != null ? res.getInteger(resId) : defaultValue; 143 } 144 loadBoolean(@ullable Resources res, int resId)145 private static boolean loadBoolean(@Nullable Resources res, int resId) { 146 return res != null && res.getBoolean(resId); 147 } 148 loadIntArray(@ullable Resources res, int resId)149 private static int[] loadIntArray(@Nullable Resources res, int resId) { 150 return res != null ? res.getIntArray(resId) : new int[0]; 151 } 152 153 /** 154 * Return the maximum amplitude the vibrator can play using the audio haptic channels. 155 * 156 * @return a positive value representing the maximum absolute value the device can play signals 157 * from audio haptic channels, or {@link Float#NaN NaN} if it's unknown. 158 */ getHapticChannelMaximumAmplitude()159 public float getHapticChannelMaximumAmplitude() { 160 if (mHapticChannelMaxVibrationAmplitude <= 0) { 161 return Float.NaN; 162 } 163 return mHapticChannelMaxVibrationAmplitude; 164 } 165 166 /** 167 * Return the device default vibration amplitude value to replace the 168 * {@link android.os.VibrationEffect#DEFAULT_AMPLITUDE} constant. 169 */ getDefaultVibrationAmplitude()170 public int getDefaultVibrationAmplitude() { 171 if (mDefaultVibrationAmplitude < 1 || mDefaultVibrationAmplitude > 255) { 172 return DEFAULT_AMPLITUDE; 173 } 174 return mDefaultVibrationAmplitude; 175 } 176 177 /** 178 * Return the device default gain to be applied between scale levels to define the scale factor 179 * for each level. 180 */ getDefaultVibrationScaleLevelGain()181 public float getDefaultVibrationScaleLevelGain() { 182 // TODO(b/356407380): add device config for this 183 return DEFAULT_SCALE_LEVEL_GAIN; 184 } 185 186 /** 187 * The duration, in milliseconds, that should be applied to the ramp to turn off the vibrator 188 * when a vibration is cancelled or finished at non-zero amplitude. 189 */ getRampDownDurationMs()190 public int getRampDownDurationMs() { 191 if (mRampDownDurationMs < 0) { 192 return 0; 193 } 194 return mRampDownDurationMs; 195 } 196 197 /** 198 * The duration, in milliseconds, that the vibrator control service will wait for new 199 * vibration params. 200 */ getRequestVibrationParamsTimeoutMs()201 public int getRequestVibrationParamsTimeoutMs() { 202 return Math.max(mRequestVibrationParamsTimeoutMs, 0); 203 } 204 205 /** 206 * The list of usages that should request vibration params before they are played. These 207 * usages don't have strong latency requirements, e.g. ringtone and notification, and can be 208 * slightly delayed. 209 */ getRequestVibrationParamsForUsages()210 public int[] getRequestVibrationParamsForUsages() { 211 return mRequestVibrationParamsForUsages; 212 } 213 214 /** 215 * The duration, in milliseconds, that should be applied to convert vibration effect's 216 * {@link android.os.vibrator.RampSegment} to a {@link android.os.vibrator.StepSegment} on 217 * devices without PWLE support. 218 */ getRampStepDurationMs()219 public int getRampStepDurationMs() { 220 if (mRampStepDurationMs < 0) { 221 return 0; 222 } 223 return mRampStepDurationMs; 224 } 225 226 /** 227 * The max duration, in milliseconds, allowed for pipelining vibration requests. 228 * 229 * <p>If the ongoing vibration duration is shorter than this threshold then it should be allowed 230 * to finish before the next vibration can start. If the ongoing vibration is longer than this 231 * then it should be cancelled when it's superseded for the new one. 232 * 233 * @return the max duration allowed for vibration effect to finish before the next request, or 234 * zero to disable effect pipelining. 235 */ getVibrationPipelineMaxDurationMs()236 public int getVibrationPipelineMaxDurationMs() { 237 if (mVibrationPipelineMaxDurationMs < 0) { 238 return 0; 239 } 240 return mVibrationPipelineMaxDurationMs; 241 } 242 243 /** 244 * Whether or not vibrations are ignored if the device is on a wireless charger. 245 * 246 * <p>This may be the case if vibration during wireless charging causes unwanted results, like 247 * moving the device out of alignment with the charging pad. 248 */ ignoreVibrationsOnWirelessCharger()249 public boolean ignoreVibrationsOnWirelessCharger() { 250 return mIgnoreVibrationsOnWirelessCharger; 251 } 252 253 /** 254 * Whether the device support keyboard vibration settings. 255 * @hide 256 */ isKeyboardVibrationSettingsSupported()257 public boolean isKeyboardVibrationSettingsSupported() { 258 return mKeyboardVibrationSettingsSupported; 259 } 260 261 /** Get the default vibration intensity for given usage. */ 262 @VibrationIntensity getDefaultVibrationIntensity(@ibrationAttributes.Usage int usage)263 public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) { 264 switch (usage) { 265 case USAGE_ALARM: 266 return mDefaultAlarmVibrationIntensity; 267 case USAGE_NOTIFICATION: 268 case USAGE_COMMUNICATION_REQUEST: 269 return mDefaultNotificationVibrationIntensity; 270 case USAGE_RINGTONE: 271 return mDefaultRingVibrationIntensity; 272 case USAGE_TOUCH: 273 case USAGE_HARDWARE_FEEDBACK: 274 case USAGE_PHYSICAL_EMULATION: 275 case USAGE_ACCESSIBILITY: 276 return mDefaultHapticFeedbackIntensity; 277 case USAGE_IME_FEEDBACK: 278 return isKeyboardVibrationSettingsSupported() 279 ? mDefaultKeyboardVibrationIntensity : mDefaultHapticFeedbackIntensity; 280 case USAGE_MEDIA: 281 case USAGE_UNKNOWN: 282 // fall through 283 default: 284 return mDefaultMediaVibrationIntensity; 285 } 286 } 287 288 @Override toString()289 public String toString() { 290 return "VibrationConfig{" 291 + "mIgnoreVibrationsOnWirelessCharger=" + mIgnoreVibrationsOnWirelessCharger 292 + ", mDefaultVibrationAmplitude=" + mDefaultVibrationAmplitude 293 + ", mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude 294 + ", mRampStepDurationMs=" + mRampStepDurationMs 295 + ", mRampDownDurationMs=" + mRampDownDurationMs 296 + ", mRequestVibrationParamsForUsages=" 297 + Arrays.toString(getRequestVibrationParamsForUsagesNames()) 298 + ", mRequestVibrationParamsTimeoutMs=" + mRequestVibrationParamsTimeoutMs 299 + ", mDefaultAlarmIntensity=" + mDefaultAlarmVibrationIntensity 300 + ", mDefaultHapticFeedbackIntensity=" + mDefaultHapticFeedbackIntensity 301 + ", mDefaultMediaIntensity=" + mDefaultMediaVibrationIntensity 302 + ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity 303 + ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity 304 + ", mDefaultKeyboardIntensity=" + mDefaultKeyboardVibrationIntensity 305 + ", mKeyboardVibrationSettingsSupported=" + mKeyboardVibrationSettingsSupported 306 + "}"; 307 } 308 309 /** 310 * Write current settings into given {@link PrintWriter}, skipping the default settings. 311 * 312 * @hide 313 */ dumpWithoutDefaultSettings(IndentingPrintWriter pw)314 public void dumpWithoutDefaultSettings(IndentingPrintWriter pw) { 315 pw.println("VibrationConfig:"); 316 pw.increaseIndent(); 317 pw.println("ignoreVibrationsOnWirelessCharger = " + mIgnoreVibrationsOnWirelessCharger); 318 pw.println("defaultVibrationAmplitude = " + mDefaultVibrationAmplitude); 319 pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude); 320 pw.println("rampStepDurationMs = " + mRampStepDurationMs); 321 pw.println("rampDownDurationMs = " + mRampDownDurationMs); 322 pw.println("requestVibrationParamsForUsages = " 323 + Arrays.toString(getRequestVibrationParamsForUsagesNames())); 324 pw.println("requestVibrationParamsTimeoutMs = " + mRequestVibrationParamsTimeoutMs); 325 pw.decreaseIndent(); 326 } 327 getRequestVibrationParamsForUsagesNames()328 private String[] getRequestVibrationParamsForUsagesNames() { 329 int usagesCount = mRequestVibrationParamsForUsages.length; 330 String[] names = new String[usagesCount]; 331 for (int i = 0; i < usagesCount; i++) { 332 names[i] = VibrationAttributes.usageToString(mRequestVibrationParamsForUsages[i]); 333 } 334 335 return names; 336 } 337 } 338