1 /* 2 * Copyright (C) 2020 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.server.vibrator; 18 19 import android.content.Context; 20 import android.hardware.vibrator.V1_0.EffectStrength; 21 import android.os.IExternalVibratorService; 22 import android.os.VibrationEffect; 23 import android.os.Vibrator; 24 import android.os.vibrator.PrebakedSegment; 25 import android.util.Slog; 26 import android.util.SparseArray; 27 28 /** Controls vibration scaling. */ 29 final class VibrationScaler { 30 private static final String TAG = "VibrationScaler"; 31 32 // Scale levels. Each level, except MUTE, is defined as the delta between the current setting 33 // and the default intensity for that type of vibration (i.e. current - default). 34 private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2 35 private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1 36 private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0 37 private static final int SCALE_HIGH = IExternalVibratorService.SCALE_HIGH; // 1 38 private static final int SCALE_VERY_HIGH = IExternalVibratorService.SCALE_VERY_HIGH; // 2 39 40 // Scale factors for each level. 41 private static final float SCALE_FACTOR_VERY_LOW = 0.6f; 42 private static final float SCALE_FACTOR_LOW = 0.8f; 43 private static final float SCALE_FACTOR_NONE = 1f; 44 private static final float SCALE_FACTOR_HIGH = 1.2f; 45 private static final float SCALE_FACTOR_VERY_HIGH = 1.4f; 46 47 // A mapping from the intensity adjustment to the scaling to apply, where the intensity 48 // adjustment is defined as the delta between the default intensity level and the user selected 49 // intensity level. It's important that we apply the scaling on the delta between the two so 50 // that the default intensity level applies no scaling to application provided effects. 51 private final SparseArray<ScaleLevel> mScaleLevels; 52 private final VibrationSettings mSettingsController; 53 private final int mDefaultVibrationAmplitude; 54 VibrationScaler(Context context, VibrationSettings settingsController)55 VibrationScaler(Context context, VibrationSettings settingsController) { 56 mSettingsController = settingsController; 57 mDefaultVibrationAmplitude = context.getResources().getInteger( 58 com.android.internal.R.integer.config_defaultVibrationAmplitude); 59 60 mScaleLevels = new SparseArray<>(); 61 mScaleLevels.put(SCALE_VERY_LOW, new ScaleLevel(SCALE_FACTOR_VERY_LOW)); 62 mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_FACTOR_LOW)); 63 mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_FACTOR_NONE)); 64 mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_FACTOR_HIGH)); 65 mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_FACTOR_VERY_HIGH)); 66 } 67 68 /** 69 * Calculates the scale to be applied to external vibration with given usage. 70 * 71 * @param usageHint one of VibrationAttributes.USAGE_* 72 * @return one of IExternalVibratorService.SCALE_* 73 */ getExternalVibrationScale(int usageHint)74 public int getExternalVibrationScale(int usageHint) { 75 int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint); 76 int currentIntensity = mSettingsController.getCurrentIntensity(usageHint); 77 78 if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) { 79 // Bypassing user settings, or it has changed between checking and scaling. Use default. 80 return SCALE_NONE; 81 } 82 83 int scaleLevel = currentIntensity - defaultIntensity; 84 85 if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) { 86 return scaleLevel; 87 } else { 88 // Something about our scaling has gone wrong, so just play with no scaling. 89 Slog.w(TAG, "Error in scaling calculations, ended up with invalid scale level " 90 + scaleLevel + " for vibration with usage " + usageHint); 91 return SCALE_NONE; 92 } 93 } 94 95 /** 96 * Scale a {@link VibrationEffect} based on the given usage hint for this vibration. 97 * 98 * @param effect the effect to be scaled 99 * @param usageHint one of VibrationAttributes.USAGE_* 100 * @return The same given effect, if no changes were made, or a new {@link VibrationEffect} with 101 * resolved and scaled amplitude 102 */ scale(VibrationEffect effect, int usageHint)103 public <T extends VibrationEffect> T scale(VibrationEffect effect, int usageHint) { 104 int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint); 105 int currentIntensity = mSettingsController.getCurrentIntensity(usageHint); 106 107 if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) { 108 // Bypassing user settings, or it has changed between checking and scaling. Use default. 109 currentIntensity = defaultIntensity; 110 } 111 112 int newEffectStrength = intensityToEffectStrength(currentIntensity); 113 effect = effect.applyEffectStrength(newEffectStrength).resolve(mDefaultVibrationAmplitude); 114 ScaleLevel scale = mScaleLevels.get(currentIntensity - defaultIntensity); 115 116 if (scale == null) { 117 // Something about our scaling has gone wrong, so just play with no scaling. 118 Slog.e(TAG, "No configured scaling level!" 119 + " (current=" + currentIntensity + ", default= " + defaultIntensity + ")"); 120 return (T) effect; 121 } 122 123 return (T) effect.scale(scale.factor); 124 } 125 126 /** 127 * Scale a {@link PrebakedSegment} based on the given usage hint for this vibration. 128 * 129 * @param prebaked the prebaked segment to be scaled 130 * @param usageHint one of VibrationAttributes.USAGE_* 131 * @return The same segment if no changes were made, or a new {@link PrebakedSegment} with 132 * updated effect strength 133 */ scale(PrebakedSegment prebaked, int usageHint)134 public PrebakedSegment scale(PrebakedSegment prebaked, int usageHint) { 135 int currentIntensity = mSettingsController.getCurrentIntensity(usageHint); 136 137 if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) { 138 // Bypassing user settings, or it has changed between checking and scaling. Use default. 139 currentIntensity = mSettingsController.getDefaultIntensity(usageHint); 140 } 141 142 int newEffectStrength = intensityToEffectStrength(currentIntensity); 143 return prebaked.applyEffectStrength(newEffectStrength); 144 } 145 146 /** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */ intensityToEffectStrength(int intensity)147 private static int intensityToEffectStrength(int intensity) { 148 switch (intensity) { 149 case Vibrator.VIBRATION_INTENSITY_LOW: 150 return EffectStrength.LIGHT; 151 case Vibrator.VIBRATION_INTENSITY_MEDIUM: 152 return EffectStrength.MEDIUM; 153 case Vibrator.VIBRATION_INTENSITY_HIGH: 154 return EffectStrength.STRONG; 155 default: 156 Slog.w(TAG, "Got unexpected vibration intensity: " + intensity); 157 return EffectStrength.STRONG; 158 } 159 } 160 161 /** Represents the scale that must be applied to a vibration effect intensity. */ 162 private static final class ScaleLevel { 163 public final float factor; 164 ScaleLevel(float factor)165 ScaleLevel(float factor) { 166 this.factor = factor; 167 } 168 169 @Override toString()170 public String toString() { 171 return "ScaleLevel{factor=" + factor + "}"; 172 } 173 } 174 } 175