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