Refactor InputManager service for input rumble support.

Change aidl vibrate API to use VibrationEffect.
Handle default amplitude in InputManagerService.
Set both channels for vibration amplitudes.

Bug: 136215622
Test: Connect game controller with rumble support and play game for
force feedback effect.

Change-Id: Ib25dc4ea1b25031707c728ad947c6d8238480e30
This commit is contained in:
Chris Ye
2020-08-04 19:42:31 -07:00
parent 6fd5ba3a44
commit 6f0311bf22
4 changed files with 51 additions and 29 deletions

View File

@@ -23,6 +23,7 @@ import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.TouchCalibration;
import android.os.IBinder;
import android.os.VibrationEffect;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
@@ -83,7 +84,7 @@ interface IInputManager {
int isMicMuted();
// Input device vibrator control.
void vibrate(int deviceId, in long[] pattern, in int[] amplitudes, int repeat, IBinder token);
void vibrate(int deviceId, in VibrationEffect effect, IBinder token);
void cancelVibrate(int deviceId, IBinder token);
void setPointerIconType(int typeId);

View File

@@ -1297,27 +1297,8 @@ public final class InputManager {
@Override
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
Log.w(TAG, "Pre-baked effects aren't supported on input devices");
return;
}
try {
mIm.vibrate(mDeviceId, pattern, amplitudes, repeat, mToken);
mIm.vibrate(mDeviceId, effect, mToken);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}

View File

@@ -63,6 +63,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
@@ -141,6 +142,8 @@ public class InputManagerService extends IInputManager.Stub
private static final int MSG_RELOAD_DEVICE_ALIASES = 5;
private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 6;
private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
// Pointer to native input manager service object.
private final long mPtr;
@@ -1728,7 +1731,37 @@ public class InputManagerService extends IInputManager.Stub
// Binder call
@Override
public void vibrate(int deviceId, long[] pattern, int[] amplitudes, int repeat, IBinder token) {
public void vibrate(int deviceId, VibrationEffect effect, IBinder token) {
long[] pattern;
int[] amplitudes;
int repeat;
if (effect instanceof VibrationEffect.OneShot) {
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
pattern = new long[] { 0, oneShot.getDuration() };
int amplitude = oneShot.getAmplitude();
// android framework uses DEFAULT_AMPLITUDE to signal that the vibration
// should use some built-in default value, denoted here as DEFAULT_VIBRATION_MAGNITUDE
if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
amplitude = DEFAULT_VIBRATION_MAGNITUDE;
}
amplitudes = new int[] { 0, amplitude };
repeat = -1;
} else if (effect instanceof VibrationEffect.Waveform) {
VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
pattern = waveform.getTimings();
amplitudes = waveform.getAmplitudes();
for (int i = 0; i < amplitudes.length; i++) {
if (amplitudes[i] == VibrationEffect.DEFAULT_AMPLITUDE) {
amplitudes[i] = DEFAULT_VIBRATION_MAGNITUDE;
}
}
repeat = waveform.getRepeatIndex();
} else {
// TODO: Add support for prebaked effects
Log.w(TAG, "Pre-baked effects aren't supported on input devices");
return;
}
if (repeat >= pattern.length) {
throw new ArrayIndexOutOfBoundsException();
}
@@ -1747,7 +1780,6 @@ public class InputManagerService extends IInputManager.Stub
mVibratorTokens.put(token, v);
}
}
synchronized (v) {
v.mVibrating = true;
nativeVibrate(mPtr, deviceId, pattern, amplitudes, repeat, v.mTokenValue);

View File

@@ -75,6 +75,12 @@
using android::base::ParseUint;
using android::base::StringPrintf;
// Maximum allowable delay value in a vibration pattern before
// which the delay will be truncated.
static constexpr std::chrono::duration MAX_VIBRATE_PATTERN_DELAY = 100s;
static constexpr std::chrono::milliseconds MAX_VIBRATE_PATTERN_DELAY_MILLIS =
std::chrono::duration_cast<std::chrono::milliseconds>(MAX_VIBRATE_PATTERN_DELAY);
namespace android {
// The exponent used to calculate the pointer speed scaling factor.
@@ -1636,17 +1642,19 @@ static void nativeVibrate(JNIEnv* env, jclass /* clazz */, jlong ptr, jint devic
patternObj, nullptr));
jint* amplitudes = static_cast<jint*>(env->GetPrimitiveArrayCritical(amplitudesObj, nullptr));
std::vector<VibrationElement> pattern(patternSize);
std::vector<VibrationElement> elements(patternSize);
for (size_t i = 0; i < patternSize; i++) {
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]};
// VibrationEffect.validate guarantees duration > 0.
std::chrono::milliseconds duration(patternMillis[i]);
elements[i].duration = std::min(duration, MAX_VIBRATE_PATTERN_DELAY_MILLIS);
// TODO: (b/161629089) apply channel specific amplitudes from development API.
elements[i].channels = {static_cast<uint8_t>(amplitudes[i]),
static_cast<uint8_t>(amplitudes[i])};
}
env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT);
env->ReleasePrimitiveArrayCritical(amplitudesObj, amplitudes, JNI_ABORT);
im->getInputManager()->getReader()->vibrate(deviceId, pattern, repeat, token);
im->getInputManager()->getReader()->vibrate(deviceId, elements, repeat, token);
}
static void nativeCancelVibrate(JNIEnv* /* env */,