Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b88c93ef authored by Nathaniel R. Lewis's avatar Nathaniel R. Lewis Committed by Chris Ye
Browse files

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
parent 53c5d870
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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);
+5 −2
Original line number Diff line number Diff line
@@ -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();
            }
+3 −3
Original line number Diff line number Diff line
@@ -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);
        }
    }

+12 −8
Original line number Diff line number Diff line
@@ -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<NativeInputManager*>(ptr);

    size_t patternSize = env->GetArrayLength(patternObj);
@@ -1630,14 +1629,19 @@ static void nativeVibrate(JNIEnv* env,

    jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical(
            patternObj, nullptr));
    nsecs_t pattern[patternSize];
    jint* amplitudes = static_cast<jint*>(env->GetPrimitiveArrayCritical(amplitudesObj, nullptr));

    std::vector<VibrationElement> 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},