diff options
| author | 2023-07-13 01:02:09 -0700 | |
|---|---|---|
| committer | 2023-07-19 17:13:20 +0000 | |
| commit | d95c41afef81a87b24c447bee488c6008b4a79bc (patch) | |
| tree | d6cb0d54f3c84021b9e755b8e6c08cfd50b31b1f | |
| parent | 1cc8cc91780be949490fa6df4b357d3f49ec6527 (diff) | |
Move Haptic Feedback Vibration Determination Logic to Helper Class
This allows moving the logic to decide what vibration and vibration
attribute to use for a given haptic feedback type outside Window
manager and into the vibrator codespace. This is as part of the larger
effort to move haptic feedback logic out of Window manager space.
This also makes easy to add more logic to the determination of the
vibration to be used for a haptic feedback (example - allowing OEM
provided haptic effect vibration determination logic).
Bug: 185779088
Bug: 291128479
Test: manual
Change-Id: Ic00323f95ccdb77b918cc871deee19c1942359f7
3 files changed, 222 insertions, 148 deletions
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 7bbe8781e434..5e01c4939d24 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -208,7 +208,6 @@ import com.android.internal.policy.LogDecelerateInterpolator; import com.android.internal.policy.PhoneWindow; import com.android.internal.policy.TransitionAnimation; import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.LockPatternUtils; import com.android.server.AccessibilityManagerInternal; @@ -228,6 +227,7 @@ import com.android.server.policy.keyguard.KeyguardServiceDelegate; import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener; import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback; import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.vibrator.HapticFeedbackVibrationProvider; import com.android.server.vr.VrManagerInternal; import com.android.server.wallpaper.WallpaperManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -371,12 +371,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final String TALKBACK_LABEL = "TalkBack"; private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800; - private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = - VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH); - private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES = - VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION); - private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES = - VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK); /** * Keyguard stuff @@ -450,6 +444,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { PackageManager mPackageManager; SideFpsEventHandler mSideFpsEventHandler; LockPatternUtils mLockPatternUtils; + private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider; private boolean mHasFeatureAuto; private boolean mHasFeatureWatch; private boolean mHasFeatureLeanback; @@ -458,9 +453,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Assigned on main thread, accessed on UI thread volatile VrManagerInternal mVrManagerInternal; - // Vibrator pattern for haptic feedback during boot when safe mode is enabled. - long[] mSafeModeEnabledVibePattern; - /** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */ boolean mEnableShiftMenuBugReports = false; @@ -558,7 +550,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mPowerVolUpBehavior; boolean mStylusButtonsEnabled = true; boolean mHasSoftInput = false; - boolean mHapticTextHandleEnabled; boolean mUseTvRouting; boolean mAllowStartActivityForLongPressOnPowerDuringSetup; MetricsLogger mLogger; @@ -2251,9 +2242,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup); - mHapticTextHandleEnabled = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_enableHapticTextHandle); - mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION; mHandleVolumeKeysInWM = mContext.getResources().getBoolean( @@ -2294,8 +2282,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mContext.registerReceiver(mMultiuserReceiver, filter); mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); - mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(), - com.android.internal.R.array.config_safeModeEnabledVibePattern); + mHapticFeedbackVibrationProvider = + new HapticFeedbackVibrationProvider(mContext.getResources(), mVibrator); mGlobalKeyManager = new GlobalKeyManager(mContext); @@ -5499,10 +5487,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - static long[] getLongIntArray(Resources r, int resid) { - return ArrayUtils.convertToLongArray(r.getIntArray(resid)); - } - private void bindKeyguard() { synchronized (mLock) { if (mKeyguardBound) { @@ -5989,138 +5973,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (!mVibrator.hasVibrator()) { return false; } - VibrationEffect effect = getVibrationEffect(effectId); + VibrationEffect effect = + mHapticFeedbackVibrationProvider.getVibrationForHapticFeedback(effectId); if (effect == null) { return false; } - VibrationAttributes attrs = getVibrationAttributes(effectId); - if (always) { - attrs = new VibrationAttributes.Builder(attrs) - .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF) - .build(); - } + VibrationAttributes attrs = + mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback( + effectId, /* bypassVibrationIntensitySetting= */ always); mVibrator.vibrate(uid, packageName, effect, reason, attrs); return true; } - private VibrationEffect getVibrationEffect(int effectId) { - long[] pattern; - switch (effectId) { - case HapticFeedbackConstants.CONTEXT_CLICK: - case HapticFeedbackConstants.GESTURE_END: - case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE: - case HapticFeedbackConstants.SCROLL_TICK: - case HapticFeedbackConstants.SEGMENT_TICK: - return VibrationEffect.get(VibrationEffect.EFFECT_TICK); - - case HapticFeedbackConstants.TEXT_HANDLE_MOVE: - if (!mHapticTextHandleEnabled) { - return null; - } - // fallthrough - case HapticFeedbackConstants.CLOCK_TICK: - case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK: - return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); - - case HapticFeedbackConstants.KEYBOARD_RELEASE: - case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE: - case HapticFeedbackConstants.ENTRY_BUMP: - case HapticFeedbackConstants.DRAG_CROSSING: - return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false); - - case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS - case HapticFeedbackConstants.VIRTUAL_KEY: - case HapticFeedbackConstants.EDGE_RELEASE: - case HapticFeedbackConstants.CALENDAR_DATE: - case HapticFeedbackConstants.CONFIRM: - case HapticFeedbackConstants.GESTURE_START: - case HapticFeedbackConstants.SCROLL_ITEM_FOCUS: - case HapticFeedbackConstants.SCROLL_LIMIT: - return VibrationEffect.get(VibrationEffect.EFFECT_CLICK); - - case HapticFeedbackConstants.LONG_PRESS: - case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON: - case HapticFeedbackConstants.DRAG_START: - case HapticFeedbackConstants.EDGE_SQUEEZE: - return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); - - case HapticFeedbackConstants.REJECT: - return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); - - case HapticFeedbackConstants.SAFE_MODE_ENABLED: - pattern = mSafeModeEnabledVibePattern; - break; - - case HapticFeedbackConstants.ASSISTANT_BUTTON: - if (mVibrator.areAllPrimitivesSupported( - VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, - VibrationEffect.Composition.PRIMITIVE_TICK)) { - // quiet ramp, short pause, then sharp tick - return VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f) - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50) - .compose(); - } - // fallback for devices without composition support - return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); - - case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE: - return getScaledPrimitiveOrElseEffect( - VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f, - VibrationEffect.EFFECT_TEXTURE_TICK); - - case HapticFeedbackConstants.TOGGLE_ON: - return getScaledPrimitiveOrElseEffect( - VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, - VibrationEffect.EFFECT_TICK); - - case HapticFeedbackConstants.TOGGLE_OFF: - return getScaledPrimitiveOrElseEffect( - VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f, - VibrationEffect.EFFECT_TEXTURE_TICK); - - case HapticFeedbackConstants.NO_HAPTICS: - default: - return null; - } - if (pattern.length == 0) { - // No vibration - return null; - } else if (pattern.length == 1) { - // One-shot vibration - return VibrationEffect.createOneShot(pattern[0], VibrationEffect.DEFAULT_AMPLITUDE); - } else { - // Pattern vibration - return VibrationEffect.createWaveform(pattern, -1); - } - } - - private VibrationEffect getScaledPrimitiveOrElseEffect(int primitiveId, float scale, - int elseEffectId) { - if (mVibrator.areAllPrimitivesSupported(primitiveId)) { - return VibrationEffect.startComposition() - .addPrimitive(primitiveId, scale) - .compose(); - } else { - return VibrationEffect.get(elseEffectId); - } - } - - private VibrationAttributes getVibrationAttributes(int effectId) { - switch (effectId) { - case HapticFeedbackConstants.EDGE_SQUEEZE: - case HapticFeedbackConstants.EDGE_RELEASE: - return PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES; - case HapticFeedbackConstants.ASSISTANT_BUTTON: - case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON: - case HapticFeedbackConstants.SCROLL_TICK: - case HapticFeedbackConstants.SCROLL_ITEM_FOCUS: - case HapticFeedbackConstants.SCROLL_LIMIT: - return HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES; - default: - return TOUCH_VIBRATION_ATTRIBUTES; - } - } @Override public void keepScreenOnStartedLw() { @@ -6258,8 +6122,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup="); pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup); pw.print(prefix); - pw.print("mHasSoftInput="); pw.print(mHasSoftInput); - pw.print(" mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled); + pw.print("mHasSoftInput="); pw.println(mHasSoftInput); pw.print(prefix); pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed); pw.print(" mIncallPowerBehavior="); @@ -6284,6 +6147,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout); pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive); + mHapticFeedbackVibrationProvider.dump(prefix, pw); mGlobalKeyManager.dump(prefix, pw); mKeyCombinationManager.dump(prefix, pw); mSingleKeyGestureDetector.dump(prefix, pw); diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java new file mode 100644 index 000000000000..308ce4f9cb68 --- /dev/null +++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java @@ -0,0 +1,194 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.annotation.Nullable; +import android.content.res.Resources; +import android.os.VibrationAttributes; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.view.HapticFeedbackConstants; + +import java.io.PrintWriter; + +/** + * Provides the {@link VibrationEffect} and {@link VibrationAttributes} for haptic feedback. + * + * @hide + */ +public final class HapticFeedbackVibrationProvider { + private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = + VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH); + private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES = + VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION); + private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES = + VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK); + + private final Vibrator mVibrator; + private final boolean mHapticTextHandleEnabled; + // Vibrator effect for haptic feedback during boot when safe mode is enabled. + private final VibrationEffect mSafeModeEnabledVibrationEffect; + + /** @hide */ + public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) { + mVibrator = vibrator; + mHapticTextHandleEnabled = res.getBoolean( + com.android.internal.R.bool.config_enableHapticTextHandle); + mSafeModeEnabledVibrationEffect = + VibrationSettings.createEffectFromResource( + res, com.android.internal.R.array.config_safeModeEnabledVibePattern); + } + + /** + * Provides the {@link VibrationEffect} for a given haptic feedback effect ID (provided in + * {@link HapticFeedbackConstants}). + * + * @param effectId the haptic feedback effect ID whose respective vibration we want to get. + * @return a {@link VibrationEffect} for the given haptic feedback effect ID, or {@code null} if + * the provided effect ID is not supported. + */ + @Nullable public VibrationEffect getVibrationForHapticFeedback(int effectId) { + switch (effectId) { + case HapticFeedbackConstants.CONTEXT_CLICK: + case HapticFeedbackConstants.GESTURE_END: + case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE: + case HapticFeedbackConstants.SCROLL_TICK: + case HapticFeedbackConstants.SEGMENT_TICK: + return VibrationEffect.get(VibrationEffect.EFFECT_TICK); + + case HapticFeedbackConstants.TEXT_HANDLE_MOVE: + if (!mHapticTextHandleEnabled) { + return null; + } + // fallthrough + case HapticFeedbackConstants.CLOCK_TICK: + case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK: + return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); + + case HapticFeedbackConstants.KEYBOARD_RELEASE: + case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE: + case HapticFeedbackConstants.ENTRY_BUMP: + case HapticFeedbackConstants.DRAG_CROSSING: + return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false); + + case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS + case HapticFeedbackConstants.VIRTUAL_KEY: + case HapticFeedbackConstants.EDGE_RELEASE: + case HapticFeedbackConstants.CALENDAR_DATE: + case HapticFeedbackConstants.CONFIRM: + case HapticFeedbackConstants.GESTURE_START: + case HapticFeedbackConstants.SCROLL_ITEM_FOCUS: + case HapticFeedbackConstants.SCROLL_LIMIT: + return VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + + case HapticFeedbackConstants.LONG_PRESS: + case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON: + case HapticFeedbackConstants.DRAG_START: + case HapticFeedbackConstants.EDGE_SQUEEZE: + return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); + + case HapticFeedbackConstants.REJECT: + return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); + + case HapticFeedbackConstants.SAFE_MODE_ENABLED: + return mSafeModeEnabledVibrationEffect; + + case HapticFeedbackConstants.ASSISTANT_BUTTON: + if (mVibrator.areAllPrimitivesSupported( + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, + VibrationEffect.Composition.PRIMITIVE_TICK)) { + // quiet ramp, short pause, then sharp tick + return VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50) + .compose(); + } + // fallback for devices without composition support + return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); + + case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE: + return getScaledPrimitiveOrElseEffect( + VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f, + VibrationEffect.EFFECT_TEXTURE_TICK); + + case HapticFeedbackConstants.TOGGLE_ON: + return getScaledPrimitiveOrElseEffect( + VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, + VibrationEffect.EFFECT_TICK); + + case HapticFeedbackConstants.TOGGLE_OFF: + return getScaledPrimitiveOrElseEffect( + VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f, + VibrationEffect.EFFECT_TEXTURE_TICK); + + case HapticFeedbackConstants.NO_HAPTICS: + default: + return null; + } + } + + /** + * Provides the {@link VibrationAttributes} that should be used for a haptic feedback. + * + * @param effectId the haptic feedback effect ID whose respective vibration attributes we want + * to get. + * @param bypassVibrationIntensitySetting {@code true} if the returned attribute should bypass + * vibration intensity settings. {@code false} otherwise. + * @return the {@link VibrationAttributes} that should be used for the provided haptic feedback. + */ + public VibrationAttributes getVibrationAttributesForHapticFeedback( + int effectId, boolean bypassVibrationIntensitySetting) { + VibrationAttributes attrs; + switch (effectId) { + case HapticFeedbackConstants.EDGE_SQUEEZE: + case HapticFeedbackConstants.EDGE_RELEASE: + attrs = PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES; + break; + case HapticFeedbackConstants.ASSISTANT_BUTTON: + case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON: + case HapticFeedbackConstants.SCROLL_TICK: + case HapticFeedbackConstants.SCROLL_ITEM_FOCUS: + case HapticFeedbackConstants.SCROLL_LIMIT: + attrs = HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES; + break; + default: + attrs = TOUCH_VIBRATION_ATTRIBUTES; + } + if (bypassVibrationIntensitySetting) { + attrs = new VibrationAttributes.Builder(attrs) + .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF) + .build(); + } + return attrs; + } + + /** Dumps relevant state. */ + public void dump(String prefix, PrintWriter pw) { + pw.print("mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled); + } + + private VibrationEffect getScaledPrimitiveOrElseEffect( + int primitiveId, float scale, int elseEffectId) { + if (mVibrator.areAllPrimitivesSupported(primitiveId)) { + return VibrationEffect.startComposition() + .addPrimitive(primitiveId, scale) + .compose(); + } else { + return VibrationEffect.get(elseEffectId); + } + } +} diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 4ae7c77c104e..dbd6bf461e85 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -698,7 +698,23 @@ final class VibrationSettings { @Nullable private VibrationEffect createEffectFromResource(int resId) { - long[] timings = getLongIntArray(mContext.getResources(), resId); + return createEffectFromResource(mContext.getResources(), resId); + } + + /** + * Provides a {@link VibrationEffect} from a timings-array provided as an int-array resource.. + * + * <p>If the timings array is {@code null} or empty, it returns {@code null}. + * + * <p>If the timings array has a size of one, it returns a one-shot vibration with duration that + * is equal to the single value in the array. + * + * <p>If the timings array has more than one values, it returns a non-repeating wave-form + * vibration with off-on timings as per the provided timings array. + */ + @Nullable + static VibrationEffect createEffectFromResource(Resources res, int resId) { + long[] timings = getLongIntArray(res, resId); return createEffectFromTimings(timings); } |