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
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 5930f0a..d1a3e9a 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -100,6 +100,7 @@
"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 0000000..a69f5d0
--- /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 0fa8787..5a832e7 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 @@
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 0000000..8a134ee
--- /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 fe428f1..cef2b4c 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -702,7 +702,7 @@
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 @@
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 4b19e5e..b4eaf7f 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -424,10 +424,10 @@
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 06e3743..16fe865 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -559,12 +559,12 @@
}
}
-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 baff6e3..c5dfcfd 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 @@
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 @@
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 71313fc..6cb86bd 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -81,7 +81,7 @@
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 @@
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 108b9c2..9cb2052 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -78,7 +78,7 @@
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 a8fe39a..1db829f 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -56,7 +56,7 @@
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 949c7ea..d9fc5cc 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 @@
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 7665680..8c1e224 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -39,23 +39,17 @@
// 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::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 @@
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 @@
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::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 f69fdde..bfa5ec1 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -30,7 +30,7 @@
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 @@
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 b2c16d0..21dd3c7 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -821,8 +821,7 @@
return false;
}
- virtual void vibrate(int32_t, nsecs_t) {
- }
+ virtual void vibrate(int32_t, const VibrationElement&) {}
virtual void cancelVibrate(int32_t) {
}