diff options
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); } |