diff options
author | 2019-08-12 22:07:00 +0000 | |
---|---|---|
committer | 2020-07-21 11:08:26 -0700 | |
commit | cacd69a0846b7d506e47d9d6e1f7e53784e2d829 (patch) | |
tree | cd311190f2a31c2115cfe1c872c08b423aa106fd | |
parent | dc07032296a6cd8525b5c7c1648ed7aed6f7769c (diff) |
InputFlinger: Support amplitude control for InputDeviceVibrator
Add support for sending multi-channel rumble amplitudes to input
devices supporting FF_RUMBLE.
Bug: 38511270
Bug: 136215622
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);
Change-Id: I2f059e085c106cbca2372c72d09a9f579d35e4c7
-rw-r--r-- | services/inputflinger/Android.bp | 1 | ||||
-rw-r--r-- | services/inputflinger/VibrationElement.cpp | 67 | ||||
-rw-r--r-- | services/inputflinger/include/InputReaderBase.h | 14 | ||||
-rw-r--r-- | services/inputflinger/include/VibrationElement.h | 41 | ||||
-rw-r--r-- | services/inputflinger/reader/EventHub.cpp | 9 | ||||
-rw-r--r-- | services/inputflinger/reader/InputDevice.cpp | 6 | ||||
-rw-r--r-- | services/inputflinger/reader/InputReader.cpp | 4 | ||||
-rw-r--r-- | services/inputflinger/reader/include/EventHub.h | 10 | ||||
-rw-r--r-- | services/inputflinger/reader/include/InputDevice.h | 6 | ||||
-rw-r--r-- | services/inputflinger/reader/include/InputReader.h | 2 | ||||
-rw-r--r-- | services/inputflinger/reader/mapper/InputMapper.cpp | 2 | ||||
-rw-r--r-- | services/inputflinger/reader/mapper/InputMapper.h | 4 | ||||
-rw-r--r-- | services/inputflinger/reader/mapper/VibratorInputMapper.cpp | 51 | ||||
-rw-r--r-- | services/inputflinger/reader/mapper/VibratorInputMapper.h | 6 | ||||
-rw-r--r-- | services/inputflinger/tests/InputReader_test.cpp | 3 |
15 files changed, 179 insertions, 47 deletions
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 5930f0a884..d1a3e9a2c6 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -100,6 +100,7 @@ filegroup { "InputListener.cpp", "InputReaderBase.cpp", "InputThread.cpp", + "VibrationElement.cpp" ], } diff --git a/services/inputflinger/VibrationElement.cpp b/services/inputflinger/VibrationElement.cpp new file mode 100644 index 0000000000..a69f5d06c5 --- /dev/null +++ b/services/inputflinger/VibrationElement.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "VibrationElement.h" + +#include <android-base/stringprintf.h> + +#include <algorithm> +#include <cinttypes> + +using android::base::StringPrintf; + +namespace android { + +// The sentinel to use the default amplitude +static const int DEFAULT_AMPLITUDE = -1; + +// The vibration magnitude for the "DEFAULT_AMPLITUDE" magnitude constant. +static const uint16_t DEFAULT_MAGNITUDE = 0xc000; + +void VibrationElement::dump(std::string& dump) const { + dump += StringPrintf("[duration=%lldms, channels=[", duration.count()); + + if (channels.size()) { + dump += std::to_string(channels[0]); + std::for_each(channels.begin() + 1, channels.end(), [&dump](int channel) { + dump += ", "; + dump += std::to_string(channel); + }); + } + dump += "]]"; +} + +uint16_t VibrationElement::getChannel(int id) const { + if (id >= (int)channels.size()) { + return 0; + } + + // android framework uses DEFAULT_AMPLITUDE to signal that the vibration + // should use some built-in default value, denoted here as DEFAULT_MAGNITUDE + if (channels[id] == DEFAULT_AMPLITUDE) { + return DEFAULT_MAGNITUDE; + } + + // convert range [0,255] to [0,65535] (android framework to linux ff ranges) + return ((uint16_t)channels[id]) << 8; +} + +bool VibrationElement::isOn() const { + return std::any_of(channels.begin(), channels.end(), + [](uint16_t channel) { return channel != 0; }); +} + +} // namespace android diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 0fa878759b..5a832e7a8e 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -17,23 +17,24 @@ #ifndef _UI_INPUT_READER_BASE_H #define _UI_INPUT_READER_BASE_H -#include "PointerControllerInterface.h" - #include <input/DisplayViewport.h> #include <input/Input.h> #include <input/InputDevice.h> #include <input/VelocityControl.h> #include <input/VelocityTracker.h> +#include <stddef.h> +#include <unistd.h> #include <utils/Errors.h> #include <utils/RefBase.h> -#include <stddef.h> -#include <unistd.h> #include <optional> #include <set> #include <unordered_map> #include <vector> +#include "PointerControllerInterface.h" +#include "VibrationElement.h" + // Maximum supported size of a vibration pattern. // Must be at least 2. #define MAX_VIBRATE_PATTERN_SIZE 100 @@ -41,6 +42,7 @@ // Maximum allowable delay value in a vibration pattern before // which the delay will be truncated. #define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL) +#define MAX_VIBRATE_PATTERN_DELAY_MSECS (1000000 * 1000LL) namespace android { @@ -104,8 +106,8 @@ public: 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 vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern, + ssize_t repeat, int32_t token) = 0; virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0; /* Return true if the device can send input events to the specified display. */ diff --git a/services/inputflinger/include/VibrationElement.h b/services/inputflinger/include/VibrationElement.h new file mode 100644 index 0000000000..8a134eec9c --- /dev/null +++ b/services/inputflinger/include/VibrationElement.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _VIBRATION_ELEMENT_H +#define _VIBRATION_ELEMENT_H + +#include <chrono> +#include <cstdint> +#include <string> +#include <vector> + +namespace android { + +/* + * Describes a rumble effect + */ +struct VibrationElement { + std::chrono::milliseconds duration; + std::vector<int> channels; + + void dump(std::string& dump) const; + uint16_t getChannel(int id) const; + bool isOn() const; +}; + +} // namespace android + +#endif // _VIBRATION_ELEMENT_H diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index fe428f101f..cef2b4ca14 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -702,7 +702,7 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { identifier.descriptor.c_str()); } -void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { +void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->hasValidFd()) { @@ -710,9 +710,10 @@ void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { 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; + // evdev FF_RUMBLE effect only supports two channels of vibration. + effect.u.rumble.strong_magnitude = element.getChannel(0); + effect.u.rumble.weak_magnitude = element.getChannel(1); + effect.replay.length = element.duration.count(); effect.replay.delay = 0; if (ioctl(device->fd, EVIOCSFF, &effect)) { ALOGW("Could not upload force feedback effect to device %s due to error %d.", diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 4b19e5e353..b4eaf7fb1b 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -424,10 +424,10 @@ bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, return result; } -void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, +void InputDevice::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token) { - for_each_mapper([pattern, patternSize, repeat, token](InputMapper& mapper) { - mapper.vibrate(pattern, patternSize, repeat, token); + for_each_mapper([pattern, repeat, token](InputMapper& mapper) { + mapper.vibrate(pattern, repeat, token); }); } diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 06e374353c..16fe865d38 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -559,12 +559,12 @@ void InputReader::requestRefreshConfiguration(uint32_t changes) { } } -void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, +void InputReader::vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token) { AutoMutex _l(mLock); InputDevice* device = findInputDevice(deviceId); if (device) { - device->vibrate(pattern, patternSize, repeat, token); + device->vibrate(pattern, repeat, token); } } diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index baff6e3ec3..c5dfcfd560 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -27,6 +27,8 @@ #include <input/KeyLayoutMap.h> #include <input/Keyboard.h> #include <input/VirtualKeyMap.h> +#include <linux/input.h> +#include <sys/epoll.h> #include <utils/BitSet.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> @@ -35,10 +37,8 @@ #include <utils/Mutex.h> #include <utils/PropertyMap.h> -#include <linux/input.h> -#include <sys/epoll.h> - #include "TouchVideoDevice.h" +#include "VibrationElement.h" namespace android { @@ -228,7 +228,7 @@ public: virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0; /* Control the vibrator. */ - virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0; + virtual void vibrate(int32_t deviceId, const VibrationElement& effect) = 0; virtual void cancelVibrate(int32_t deviceId) = 0; /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */ @@ -374,7 +374,7 @@ public: virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) override; - virtual void vibrate(int32_t deviceId, nsecs_t duration) override; + virtual void vibrate(int32_t deviceId, const VibrationElement& effect) override; virtual void cancelVibrate(int32_t deviceId) override; virtual void requestReopenDevices() override; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 71313fc86f..6cb86bd258 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -81,7 +81,7 @@ 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 vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token); void cancelVibrate(int32_t token); void cancelTouch(nsecs_t when); @@ -262,7 +262,9 @@ public: inline bool setKeyboardLayoutOverlay(const sp<KeyCharacterMap>& map) { return mEventHub->setKeyboardLayoutOverlay(mId, map); } - inline void vibrate(nsecs_t duration) { return mEventHub->vibrate(mId, duration); } + inline void vibrate(const VibrationElement& element) { + return mEventHub->vibrate(mId, element); + } inline void cancelVibrate() { return mEventHub->cancelVibrate(mId); } inline bool hasAbsoluteAxis(int32_t code) const { diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 108b9c236c..9cb2052693 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -78,7 +78,7 @@ public: virtual void requestRefreshConfiguration(uint32_t changes) override; - virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, + virtual void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token) override; virtual void cancelVibrate(int32_t deviceId, int32_t token) override; diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index a8fe39aa10..1db829f4e3 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -56,7 +56,7 @@ bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, return false; } -void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, +void InputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token) {} void InputMapper::cancelVibrate(int32_t token) {} diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index 949c7ea34e..d9fc5ccc16 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -22,6 +22,7 @@ #include "InputListener.h" #include "InputReaderContext.h" #include "StylusState.h" +#include "VibrationElement.h" namespace android { @@ -62,7 +63,8 @@ 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 vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, + int32_t token); virtual void cancelVibrate(int32_t token); virtual void cancelTouch(nsecs_t when); diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp index 7665680feb..8c1e224e09 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -39,23 +39,17 @@ 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, +void VibratorInputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token) { #if DEBUG_VIBRATOR std::string patternStr; - for (size_t i = 0; i < patternSize; i++) { - if (i != 0) { - patternStr += ", "; - } - patternStr += StringPrintf("%" PRId64, pattern[i]); - } + dumpPattern(patternStr); ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(), patternStr.c_str(), repeat, token); #endif mVibrating = true; - memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t)); - mPatternSize = patternSize; + mPattern = pattern; mRepeat = repeat; mToken = token; mIndex = -1; @@ -85,7 +79,7 @@ void VibratorInputMapper::timeoutExpired(nsecs_t when) { void VibratorInputMapper::nextStep() { mIndex += 1; - if (size_t(mIndex) >= mPatternSize) { + if (size_t(mIndex) >= mPattern.size()) { if (mRepeat < 0) { // We are done. stopVibrating(); @@ -94,13 +88,15 @@ void VibratorInputMapper::nextStep() { mIndex = mRepeat; } - bool vibratorOn = mIndex & 1; - nsecs_t duration = mPattern[mIndex]; - if (vibratorOn) { + const VibrationElement& element = mPattern[mIndex]; + if (element.isOn()) { #if DEBUG_VIBRATOR - ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration); + std::string description; + element.dump(description); + ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(), + description.c_str()); #endif - getDeviceContext().vibrate(duration); + getDeviceContext().vibrate(element); } else { #if DEBUG_VIBRATOR ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); @@ -108,10 +104,12 @@ void VibratorInputMapper::nextStep() { getDeviceContext().cancelVibrate(); } nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - mNextStepTime = now + duration; + std::chrono::nanoseconds duration = + std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration); + mNextStepTime = now + duration.count(); getContext()->requestTimeoutAtTime(mNextStepTime); #if DEBUG_VIBRATOR - ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f); + ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count()); #endif } @@ -126,6 +124,25 @@ void VibratorInputMapper::stopVibrating() { void VibratorInputMapper::dump(std::string& dump) { dump += INDENT2 "Vibrator Input Mapper:\n"; dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating)); + if (mVibrating) { + dump += INDENT3 "Pattern: "; + dumpPattern(dump); + dump += "\n"; + dump += StringPrintf(INDENT3 "Repeat Index: %zd\n", mRepeat); + } +} + +void VibratorInputMapper::dumpPattern(std::string& dump) const { + dump += "["; + + if (mPattern.size() > 0) { + mPattern[0].dump(dump); + std::for_each(mPattern.begin() + 1, mPattern.end(), [&dump](const auto& element) { + dump += ", "; + element.dump(dump); + }); + } + dump += "]"; } } // namespace android diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h index f69fdde22e..bfa5ec1bdc 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.h +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h @@ -30,7 +30,7 @@ public: virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; virtual void process(const RawEvent* rawEvent) override; - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + virtual void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token) override; virtual void cancelVibrate(int32_t token) override; virtual void timeoutExpired(nsecs_t when) override; @@ -38,13 +38,13 @@ public: private: bool mVibrating; - nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE]; - size_t mPatternSize; + std::vector<VibrationElement> mPattern; ssize_t mRepeat; int32_t mToken; ssize_t mIndex; nsecs_t mNextStepTime; + void dumpPattern(std::string& dump) const; void nextStep(); void stopVibrating(); }; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index b2c16d05c9..21dd3c798b 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -821,8 +821,7 @@ private: return false; } - virtual void vibrate(int32_t, nsecs_t) { - } + virtual void vibrate(int32_t, const VibrationElement&) {} virtual void cancelVibrate(int32_t) { } |