summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/input/EventHub.cpp66
-rw-r--r--services/input/EventHub.h14
-rw-r--r--services/input/InputReader.cpp172
-rw-r--r--services/input/InputReader.h51
-rw-r--r--services/input/tests/InputReader_test.cpp6
-rw-r--r--services/java/com/android/server/input/InputManagerService.java90
-rw-r--r--services/jni/com_android_server_input_InputManagerService.cpp36
7 files changed, 431 insertions, 4 deletions
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index fbffc94..c0eb1b9 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -161,12 +161,14 @@ EventHub::Device::Device(int fd, int32_t id, const String8& path,
const InputDeviceIdentifier& identifier) :
next(NULL),
fd(fd), id(id), path(path), identifier(identifier),
- classes(0), configuration(NULL), virtualKeyMap(NULL) {
+ classes(0), configuration(NULL), virtualKeyMap(NULL),
+ ffEffectPlaying(false), ffEffectId(-1) {
memset(keyBitmask, 0, sizeof(keyBitmask));
memset(absBitmask, 0, sizeof(absBitmask));
memset(relBitmask, 0, sizeof(relBitmask));
memset(swBitmask, 0, sizeof(swBitmask));
memset(ledBitmask, 0, sizeof(ledBitmask));
+ memset(ffBitmask, 0, sizeof(ffBitmask));
memset(propBitmask, 0, sizeof(propBitmask));
}
@@ -534,6 +536,62 @@ sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
return NULL;
}
+void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
+ AutoMutex _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device && !device->isVirtual()) {
+ ff_effect effect;
+ memset(&effect, 0, sizeof(effect));
+ effect.type = FF_RUMBLE;
+ effect.id = device->ffEffectId;
+ effect.u.rumble.strong_magnitude = 0xc000;
+ effect.u.rumble.weak_magnitude = 0xc000;
+ effect.replay.length = (duration + 999999LL) / 1000000LL;
+ effect.replay.delay = 0;
+ if (ioctl(device->fd, EVIOCSFF, &effect)) {
+ ALOGW("Could not upload force feedback effect to device %s due to error %d.",
+ device->identifier.name.string(), errno);
+ return;
+ }
+ device->ffEffectId = effect.id;
+
+ struct input_event ev;
+ ev.time.tv_sec = 0;
+ ev.time.tv_usec = 0;
+ ev.type = EV_FF;
+ ev.code = device->ffEffectId;
+ ev.value = 1;
+ if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
+ ALOGW("Could not start force feedback effect on device %s due to error %d.",
+ device->identifier.name.string(), errno);
+ return;
+ }
+ device->ffEffectPlaying = true;
+ }
+}
+
+void EventHub::cancelVibrate(int32_t deviceId) {
+ AutoMutex _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device && !device->isVirtual()) {
+ if (device->ffEffectPlaying) {
+ device->ffEffectPlaying = false;
+
+ struct input_event ev;
+ ev.time.tv_sec = 0;
+ ev.time.tv_usec = 0;
+ ev.type = EV_FF;
+ ev.code = device->ffEffectId;
+ ev.value = 0;
+ if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
+ ALOGW("Could not stop force feedback effect on device %s due to error %d.",
+ device->identifier.name.string(), errno);
+ return;
+ }
+ }
+ }
+}
+
EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
if (deviceId == BUILT_IN_KEYBOARD_ID) {
deviceId = mBuiltInKeyboardId;
@@ -949,6 +1007,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
+ ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
// See if this is a keyboard. Ignore everything in the button range except for
@@ -1010,6 +1069,11 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
}
}
+ // Check whether this device supports the vibrator.
+ if (test_bit(FF_RUMBLE, device->ffBitmask)) {
+ device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
+ }
+
// Configure virtual keys.
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
// Load the virtual keys for the touch screen, if any.
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 88159e7..51d2bac 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -113,6 +113,9 @@ enum {
/* The input device is a joystick (implies gamepad, has joystick absolute axes). */
INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
+ /* The input device has a vibrator (supports FF_RUMBLE). */
+ INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
+
/* The input device is virtual (not a real device, not part of UI configuration). */
INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
@@ -219,6 +222,10 @@ public:
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
+ /* Control the vibrator. */
+ virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
+ virtual void cancelVibrate(int32_t deviceId) = 0;
+
/* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
virtual void requestReopenDevices() = 0;
@@ -277,6 +284,9 @@ public:
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
+ virtual void vibrate(int32_t deviceId, nsecs_t duration);
+ virtual void cancelVibrate(int32_t deviceId);
+
virtual void requestReopenDevices();
virtual void wake();
@@ -303,6 +313,7 @@ private:
uint8_t relBitmask[(REL_MAX + 1) / 8];
uint8_t swBitmask[(SW_MAX + 1) / 8];
uint8_t ledBitmask[(LED_MAX + 1) / 8];
+ uint8_t ffBitmask[(FF_MAX + 1) / 8];
uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
String8 configurationFile;
@@ -310,6 +321,9 @@ private:
VirtualKeyMap* virtualKeyMap;
KeyMap keyMap;
+ bool ffEffectPlaying;
+ int16_t ffEffectId; // initially -1
+
Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
~Device();
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 71eba52..8c37fbb 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -36,6 +36,9 @@
// Log debug messages about gesture detection.
#define DEBUG_GESTURES 0
+// Log debug messages about the vibrator.
+#define DEBUG_VIBRATOR 0
+
#include "InputReader.h"
#include <cutils/log.h>
@@ -273,9 +276,7 @@ void InputReader::loopOnce() {
mConfigurationChangesToRefresh = 0;
timeoutMillis = 0;
refreshConfigurationLocked(changes);
- }
-
- if (timeoutMillis < 0 && mNextTimeout != LLONG_MAX) {
+ } else if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
@@ -426,6 +427,11 @@ InputDevice* InputReader::createDeviceLocked(int32_t deviceId,
device->addMapper(new SwitchInputMapper(device));
}
+ // Vibrator-like devices.
+ if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
+ device->addMapper(new VibratorInputMapper(device));
+ }
+
// Keyboard-like devices.
uint32_t keyboardSource = 0;
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
@@ -594,6 +600,7 @@ void InputReader::fadePointerLocked() {
void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) {
if (when < mNextTimeout) {
mNextTimeout = when;
+ mEventHub->wake();
}
}
@@ -721,6 +728,27 @@ void InputReader::requestRefreshConfiguration(uint32_t changes) {
}
}
+void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+ ssize_t repeat, int32_t token) {
+ AutoMutex _l(mLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ device->vibrate(pattern, patternSize, repeat, token);
+ }
+}
+
+void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
+ AutoMutex _l(mLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ device->cancelVibrate(token);
+ }
+}
+
void InputReader::dump(String8& dump) {
AutoMutex _l(mLock);
@@ -1054,6 +1082,23 @@ bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
return result;
}
+void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token) {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->vibrate(pattern, patternSize, repeat, token);
+ }
+}
+
+void InputDevice::cancelVibrate(int32_t token) {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->cancelVibrate(token);
+ }
+}
+
int32_t InputDevice::getMetaState() {
int32_t result = 0;
size_t numMappers = mMappers.size();
@@ -1739,6 +1784,13 @@ bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
return false;
}
+void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token) {
+}
+
+void InputMapper::cancelVibrate(int32_t token) {
+}
+
int32_t InputMapper::getMetaState() {
return 0;
}
@@ -1796,6 +1848,120 @@ int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCod
}
+// --- VibratorInputMapper ---
+
+VibratorInputMapper::VibratorInputMapper(InputDevice* device) :
+ InputMapper(device), mVibrating(false) {
+}
+
+VibratorInputMapper::~VibratorInputMapper() {
+}
+
+uint32_t VibratorInputMapper::getSources() {
+ return 0;
+}
+
+void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ info->setVibrator(true);
+}
+
+void VibratorInputMapper::process(const RawEvent* rawEvent) {
+ // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
+}
+
+void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token) {
+#if DEBUG_VIBRATOR
+ String8 patternStr;
+ for (size_t i = 0; i < patternSize; i++) {
+ if (i != 0) {
+ patternStr.append(", ");
+ }
+ patternStr.appendFormat("%lld", pattern[i]);
+ }
+ ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%ld, token=%d",
+ getDeviceId(), patternStr.string(), repeat, token);
+#endif
+
+ mVibrating = true;
+ memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
+ mPatternSize = patternSize;
+ mRepeat = repeat;
+ mToken = token;
+ mIndex = -1;
+
+ nextStep();
+}
+
+void VibratorInputMapper::cancelVibrate(int32_t token) {
+#if DEBUG_VIBRATOR
+ ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
+#endif
+
+ if (mVibrating && mToken == token) {
+ stopVibrating();
+ }
+}
+
+void VibratorInputMapper::timeoutExpired(nsecs_t when) {
+ if (mVibrating) {
+ if (when >= mNextStepTime) {
+ nextStep();
+ } else {
+ getContext()->requestTimeoutAtTime(mNextStepTime);
+ }
+ }
+}
+
+void VibratorInputMapper::nextStep() {
+ mIndex += 1;
+ if (size_t(mIndex) >= mPatternSize) {
+ if (mRepeat < 0) {
+ // We are done.
+ stopVibrating();
+ return;
+ }
+ mIndex = mRepeat;
+ }
+
+ bool vibratorOn = mIndex & 1;
+ nsecs_t duration = mPattern[mIndex];
+ if (vibratorOn) {
+#if DEBUG_VIBRATOR
+ ALOGD("nextStep: sending vibrate deviceId=%d, duration=%lld",
+ getDeviceId(), duration);
+#endif
+ getEventHub()->vibrate(getDeviceId(), duration);
+ } else {
+#if DEBUG_VIBRATOR
+ ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
+#endif
+ getEventHub()->cancelVibrate(getDeviceId());
+ }
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ mNextStepTime = now + duration;
+ getContext()->requestTimeoutAtTime(mNextStepTime);
+#if DEBUG_VIBRATOR
+ ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
+#endif
+}
+
+void VibratorInputMapper::stopVibrating() {
+ mVibrating = false;
+#if DEBUG_VIBRATOR
+ ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
+#endif
+ getEventHub()->cancelVibrate(getDeviceId());
+}
+
+void VibratorInputMapper::dump(String8& dump) {
+ dump.append(INDENT2 "Vibrator Input Mapper:\n");
+ dump.appendFormat(INDENT3 "Vibrating: %s\n", toString(mVibrating));
+}
+
+
// --- KeyboardInputMapper ---
KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index d29776d..ed57596 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -33,6 +33,14 @@
#include <stddef.h>
#include <unistd.h>
+// Maximum supported size of a vibration pattern.
+// Must be at least 2.
+#define MAX_VIBRATE_PATTERN_SIZE 100
+
+// Maximum allowable delay value in a vibration pattern before
+// which the delay will be truncated.
+#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
+
namespace android {
class InputDevice;
@@ -267,6 +275,11 @@ public:
* The changes flag is a bitfield that indicates what has changed and whether
* the input devices must all be reopened. */
virtual void requestRefreshConfiguration(uint32_t changes) = 0;
+
+ /* Controls the vibrator of a particular input device. */
+ virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+ ssize_t repeat, int32_t token) = 0;
+ virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
};
@@ -334,6 +347,10 @@ public:
virtual void requestRefreshConfiguration(uint32_t changes);
+ virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+ ssize_t repeat, int32_t token);
+ virtual void cancelVibrate(int32_t deviceId, int32_t token);
+
protected:
// These members are protected so they can be instrumented by test cases.
virtual InputDevice* createDeviceLocked(int32_t deviceId,
@@ -466,6 +483,8 @@ public:
int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags);
+ void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+ void cancelVibrate(int32_t token);
int32_t getMetaState();
@@ -848,6 +867,9 @@ public:
virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags);
+ virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token);
+ virtual void cancelVibrate(int32_t token);
virtual int32_t getMetaState();
@@ -880,6 +902,35 @@ private:
};
+class VibratorInputMapper : public InputMapper {
+public:
+ VibratorInputMapper(InputDevice* device);
+ virtual ~VibratorInputMapper();
+
+ virtual uint32_t getSources();
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void process(const RawEvent* rawEvent);
+
+ virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ int32_t token);
+ virtual void cancelVibrate(int32_t token);
+ virtual void timeoutExpired(nsecs_t when);
+ virtual void dump(String8& dump);
+
+private:
+ bool mVibrating;
+ nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
+ size_t mPatternSize;
+ ssize_t mRepeat;
+ int32_t mToken;
+ ssize_t mIndex;
+ nsecs_t mNextStepTime;
+
+ void nextStep();
+ void stopVibrating();
+};
+
+
class KeyboardInputMapper : public InputMapper {
public:
KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index e59af4e..94d4189 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -646,6 +646,12 @@ private:
return NULL;
}
+ virtual void vibrate(int32_t deviceId, nsecs_t duration) {
+ }
+
+ virtual void cancelVibrate(int32_t deviceId) {
+ }
+
virtual bool isExternal(int32_t deviceId) const {
return false;
}
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index ce7671f..e819432 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -105,6 +105,12 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
mTempInputDevicesChangedListenersToNotify =
new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only
+ // State for vibrator tokens.
+ private Object mVibratorLock = new Object();
+ private HashMap<IBinder, VibratorToken> mVibratorTokens =
+ new HashMap<IBinder, VibratorToken>();
+ private int mNextVibratorTokenValue;
+
// State for the currently installed input filter.
final Object mInputFilterLock = new Object();
InputFilter mInputFilter; // guarded by mInputFilterLock
@@ -142,6 +148,9 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
InputChannel fromChannel, InputChannel toChannel);
private static native void nativeSetPointerSpeed(int ptr, int speed);
private static native void nativeSetShowTouches(int ptr, boolean enabled);
+ private static native void nativeVibrate(int ptr, int deviceId, long[] pattern,
+ int repeat, int token);
+ private static native void nativeCancelVibrate(int ptr, int deviceId, int token);
private static native String nativeDump(int ptr);
private static native void nativeMonitor(int ptr);
@@ -792,6 +801,65 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
return result;
}
+ // Binder call
+ @Override
+ public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
+ if (repeat >= pattern.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ VibratorToken v;
+ synchronized (mVibratorLock) {
+ v = mVibratorTokens.get(token);
+ if (v == null) {
+ v = new VibratorToken(deviceId, token, mNextVibratorTokenValue++);
+ try {
+ token.linkToDeath(v, 0);
+ } catch (RemoteException ex) {
+ // give up
+ throw new RuntimeException(ex);
+ }
+ mVibratorTokens.put(token, v);
+ }
+ }
+
+ synchronized (v) {
+ v.mVibrating = true;
+ nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue);
+ }
+ }
+
+ // Binder call
+ @Override
+ public void cancelVibrate(int deviceId, IBinder token) {
+ VibratorToken v;
+ synchronized (mVibratorLock) {
+ v = mVibratorTokens.get(token);
+ if (v == null || v.mDeviceId != deviceId) {
+ return; // nothing to cancel
+ }
+ }
+
+ cancelVibrateIfNeeded(v);
+ }
+
+ void onVibratorTokenDied(VibratorToken v) {
+ synchronized (mVibratorLock) {
+ mVibratorTokens.remove(v.mToken);
+ }
+
+ cancelVibrateIfNeeded(v);
+ }
+
+ private void cancelVibrateIfNeeded(VibratorToken v) {
+ synchronized (v) {
+ if (v.mVibrating) {
+ nativeCancelVibrate(mPtr, v.mDeviceId, v.mTokenValue);
+ v.mVibrating = false;
+ }
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
@@ -1108,4 +1176,26 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
}
}
}
+
+ private final class VibratorToken implements DeathRecipient {
+ public final int mDeviceId;
+ public final IBinder mToken;
+ public final int mTokenValue;
+
+ public boolean mVibrating;
+
+ public VibratorToken(int deviceId, IBinder token, int tokenValue) {
+ mDeviceId = deviceId;
+ mToken = token;
+ mTokenValue = tokenValue;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibrator token died.");
+ }
+ onVibratorTokenDied(this);
+ }
+ }
}
diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp
index f1536fd..3795074 100644
--- a/services/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/jni/com_android_server_input_InputManagerService.cpp
@@ -1226,6 +1226,38 @@ static void nativeSetShowTouches(JNIEnv* env,
im->setShowTouches(enabled);
}
+static void nativeVibrate(JNIEnv* env,
+ jclass clazz, jint ptr, jint deviceId, jlongArray patternObj,
+ jint repeat, jint token) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ size_t patternSize = env->GetArrayLength(patternObj);
+ if (patternSize > MAX_VIBRATE_PATTERN_SIZE) {
+ ALOGI("Skipped requested vibration because the pattern size is %d "
+ "which is more than the maximum supported size of %d.",
+ patternSize, MAX_VIBRATE_PATTERN_SIZE);
+ return; // limit to reasonable size
+ }
+
+ jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical(
+ patternObj, NULL));
+ nsecs_t pattern[patternSize];
+ for (size_t i = 0; i < patternSize; i++) {
+ pattern[i] = max(jlong(0), min(patternMillis[i],
+ MAX_VIBRATE_PATTERN_DELAY_NSECS / 1000000LL)) * 1000000LL;
+ }
+ env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT);
+
+ im->getInputManager()->getReader()->vibrate(deviceId, pattern, patternSize, repeat, token);
+}
+
+static void nativeCancelVibrate(JNIEnv* env,
+ jclass clazz, jint ptr, jint deviceId, jint token) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->getInputManager()->getReader()->cancelVibrate(deviceId, token);
+}
+
static jstring nativeDump(JNIEnv* env, jclass clazz, jint ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1287,6 +1319,10 @@ static JNINativeMethod gInputManagerMethods[] = {
(void*) nativeSetPointerSpeed },
{ "nativeSetShowTouches", "(IZ)V",
(void*) nativeSetShowTouches },
+ { "nativeVibrate", "(II[JII)V",
+ (void*) nativeVibrate },
+ { "nativeCancelVibrate", "(III)V",
+ (void*) nativeCancelVibrate },
{ "nativeDump", "(I)Ljava/lang/String;",
(void*) nativeDump },
{ "nativeMonitor", "(I)V",