From b88c93ef011e55c2d0c2d4b0d3c7c99dfe9e4937 Mon Sep 17 00:00:00 2001 From: "Nathaniel R. Lewis" Date: Mon, 12 Aug 2019 22:17:37 +0000 Subject: [PATCH] InputManager: Support amplitude control for InputDeviceVibrator Add support for sending vibration amplitude(s) from OneShot and Waveform VibrationEffects to the native layer to support variable amplitude vibration motors in input devices such as gamepads. Fix bug with vibration API. In N, the VibrationEffect method which takes both timings and amplitudes states that each timing/amplitude pair makes the vibration last for the specified duration with the specified amplitude, 0 meaning the motor is off. If this method is used with an input device vibrator, it turns off for every even numbered element, regardless of the amplitude value. The method which takes only timing values works as expected. Test: Connect a gamepad whose driver supports FF_RUMBLE, find it with the android input framework, and do something like this: // waveform where rumble magnitude doubles every 2 seconds VibrationEffect effect = VibrationEffect.createWaveform( new long[] { 2000L, 2000L, 2000L, 2000L, 2000L }, new int[] { 16, 32, 64, 128, 255 }, -1); inputDevice.getVibrator().vibrate(effect); Bug: 136215622 Bug: 38511270 Change-Id: I06afd374e30d63b49aa79fa2b68e2a236b5347b2 --- .../android/hardware/input/IInputManager.aidl | 2 +- .../android/hardware/input/InputManager.java | 7 +++++-- .../server/input/InputManagerService.java | 6 +++--- ...droid_server_input_InputManagerService.cpp | 20 +++++++++++-------- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index fb44b0b66fd81..ba0636fa80fff 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -83,7 +83,7 @@ interface IInputManager { int isMicMuted(); // Input device vibrator control. - void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token); + void vibrate(int deviceId, in long[] pattern, in int[] amplitudes, int repeat, IBinder token); void cancelVibrate(int deviceId, IBinder token); void setPointerIconType(int typeId); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 72217a1196ea8..f0faeb0783863 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -54,8 +54,8 @@ import com.android.internal.os.SomeArgs; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.concurrent.Executor; import java.util.List; +import java.util.concurrent.Executor; /** * Provides information about input devices and available key layouts. @@ -1298,14 +1298,17 @@ public final class InputManager { public void vibrate(int uid, String opPkg, VibrationEffect effect, String reason, AudioAttributes attributes) { long[] pattern; + int[] amplitudes; int repeat; if (effect instanceof VibrationEffect.OneShot) { VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect; pattern = new long[] { 0, oneShot.getDuration() }; + amplitudes = new int[] { 0, oneShot.getAmplitude() }; repeat = -1; } else if (effect instanceof VibrationEffect.Waveform) { VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect; pattern = waveform.getTimings(); + amplitudes = waveform.getAmplitudes(); repeat = waveform.getRepeatIndex(); } else { // TODO: Add support for prebaked effects @@ -1314,7 +1317,7 @@ public final class InputManager { } try { - mIm.vibrate(mDeviceId, pattern, repeat, mToken); + mIm.vibrate(mDeviceId, pattern, amplitudes, repeat, mToken); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index d9b25baf2482b..74ed815f080a4 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -236,7 +236,7 @@ public class InputManagerService extends IInputManager.Stub private static native void nativeSetInteractive(long ptr, boolean interactive); private static native void nativeReloadCalibration(long ptr); private static native void nativeVibrate(long ptr, int deviceId, long[] pattern, - int repeat, int token); + int[] amplitudes, int repeat, int token); private static native void nativeCancelVibrate(long ptr, int deviceId, int token); private static native void nativeReloadKeyboardLayouts(long ptr); private static native void nativeReloadDeviceAliases(long ptr); @@ -1716,7 +1716,7 @@ public class InputManagerService extends IInputManager.Stub // Binder call @Override - public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) { + public void vibrate(int deviceId, long[] pattern, int[] amplitudes, int repeat, IBinder token) { if (repeat >= pattern.length) { throw new ArrayIndexOutOfBoundsException(); } @@ -1738,7 +1738,7 @@ public class InputManagerService extends IInputManager.Stub synchronized (v) { v.mVibrating = true; - nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue); + nativeVibrate(mPtr, deviceId, pattern, amplitudes, repeat, v.mTokenValue); } } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 7bd455ab82a15..98eb256544292 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -1615,9 +1615,8 @@ static void nativeReloadCalibration(JNIEnv* env, jclass clazz, jlong ptr) { im->reloadCalibration(); } -static void nativeVibrate(JNIEnv* env, - jclass /* clazz */, jlong ptr, jint deviceId, jlongArray patternObj, - jint repeat, jint token) { +static void nativeVibrate(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, + jlongArray patternObj, jintArray amplitudesObj, jint repeat, jint token) { NativeInputManager* im = reinterpret_cast(ptr); size_t patternSize = env->GetArrayLength(patternObj); @@ -1630,14 +1629,19 @@ static void nativeVibrate(JNIEnv* env, jlong* patternMillis = static_cast(env->GetPrimitiveArrayCritical( patternObj, nullptr)); - nsecs_t pattern[patternSize]; + jint* amplitudes = static_cast(env->GetPrimitiveArrayCritical(amplitudesObj, nullptr)); + + std::vector pattern(patternSize); for (size_t i = 0; i < patternSize; i++) { - pattern[i] = max(jlong(0), min(patternMillis[i], - (jlong)(MAX_VIBRATE_PATTERN_DELAY_NSECS / 1000000LL))) * 1000000LL; + jlong duration = + max(min(patternMillis[i], (jlong)MAX_VIBRATE_PATTERN_DELAY_MSECS), (jlong)0); + pattern[i].duration = std::chrono::milliseconds(duration); + pattern[i].channels = {amplitudes[i]}; } env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(amplitudesObj, amplitudes, JNI_ABORT); - im->getInputManager()->getReader()->vibrate(deviceId, pattern, patternSize, repeat, token); + im->getInputManager()->getReader()->vibrate(deviceId, pattern, repeat, token); } static void nativeCancelVibrate(JNIEnv* /* env */, @@ -1789,7 +1793,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches}, {"nativeSetInteractive", "(JZ)V", (void*)nativeSetInteractive}, {"nativeReloadCalibration", "(J)V", (void*)nativeReloadCalibration}, - {"nativeVibrate", "(JI[JII)V", (void*)nativeVibrate}, + {"nativeVibrate", "(JI[J[III)V", (void*)nativeVibrate}, {"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate}, {"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts}, {"nativeReloadDeviceAliases", "(J)V", (void*)nativeReloadDeviceAliases},