diff options
author | Jeff Brown <jeffbrown@google.com> | 2012-04-13 04:09:27 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2012-04-13 17:01:15 -0700 |
commit | a47425a13c19f95057df78b8bb65bb25657e8753 (patch) | |
tree | 675c0d6bf611f2427bb3d11315d410bf9087b20a /services | |
parent | c2346134bb519a54d50655cbef940fc3fdec60a9 (diff) | |
download | frameworks_base-a47425a13c19f95057df78b8bb65bb25657e8753.zip frameworks_base-a47425a13c19f95057df78b8bb65bb25657e8753.tar.gz frameworks_base-a47425a13c19f95057df78b8bb65bb25657e8753.tar.bz2 |
Add support for input devices that have vibrators.
Added a getVibrator() method to InputDevice which returns a Vibrator
associated with that input device. Its uses the same API as the
system vibrator which makes it easy for applications to be modified
to use one or the other.
Bug: 6334179
Change-Id: Ifc7f13dbcb778670f3f1c07ccc562334e6109d2e
Diffstat (limited to 'services')
-rw-r--r-- | services/input/EventHub.cpp | 66 | ||||
-rw-r--r-- | services/input/EventHub.h | 14 | ||||
-rw-r--r-- | services/input/InputReader.cpp | 172 | ||||
-rw-r--r-- | services/input/InputReader.h | 51 | ||||
-rw-r--r-- | services/input/tests/InputReader_test.cpp | 6 | ||||
-rw-r--r-- | services/java/com/android/server/input/InputManagerService.java | 90 | ||||
-rw-r--r-- | services/jni/com_android_server_input_InputManagerService.cpp | 36 |
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", |