Support combined vibration effects with input device.

Support combined vibration effects with multi-channel FF effect input
device.

Bug: 161629089
Test: atest InputDeviceVibratorTest
Change-Id: I566f6cdd601f716b34c2f70e3705340ef8906897
diff --git a/services/inputflinger/VibrationElement.cpp b/services/inputflinger/VibrationElement.cpp
index aaf5834..17e1ad4 100644
--- a/services/inputflinger/VibrationElement.cpp
+++ b/services/inputflinger/VibrationElement.cpp
@@ -24,13 +24,28 @@
 using android::base::StringPrintf;
 
 namespace android {
+// VibrationElement implementations
+VibrationElement::VibrationElement(size_t channelNum) {
+    channels.reserve(channelNum);
+}
+
+VibrationElement::VibrationElement(const VibrationElement& other) {
+    duration = other.duration;
+    channels.resize(other.channels.size());
+    for (size_t i = 0; i < other.channels.size(); i++) {
+        channels[i].first = other.channels[i].first;
+        channels[i].second = other.channels[i].second;
+    }
+}
 
 const std::string VibrationElement::toString() const {
     std::string dump;
     dump += StringPrintf("[duration=%lldms, channels=[", duration.count());
 
     for (auto it = channels.begin(); it != channels.end(); ++it) {
-        dump += std::to_string(*it);
+        dump += std::to_string(it->first);
+        dump += " : ";
+        dump += std::to_string(it->second);
         if (std::next(it) != channels.end()) {
             dump += ", ";
         }
@@ -40,17 +55,79 @@
     return dump;
 }
 
-uint16_t VibrationElement::getMagnitude(size_t channelIdx) const {
-    if (channelIdx >= channels.size()) {
+uint16_t VibrationElement::getMagnitude(int32_t vibratorId) const {
+    auto it =
+            std::find_if(channels.begin(), channels.end(),
+                         [vibratorId](const std::pair<int32_t /*vibratorId*/, uint8_t /*amplitude*/>
+                                              pair) { return pair.first == vibratorId; });
+    if (it == channels.end()) {
         return 0;
     }
     // convert range [0,255] to [0,65535] (android framework to linux ff ranges)
-    return static_cast<uint16_t>(channels[channelIdx]) << 8;
+    return static_cast<uint16_t>(it->second) << 8;
 }
 
 bool VibrationElement::isOn() const {
     return std::any_of(channels.begin(), channels.end(),
-                       [](uint16_t channel) { return channel != 0; });
+                       [](const auto& channel) { return channel.second != 0; });
+}
+
+void VibrationElement::addChannel(int32_t vibratorId, uint8_t amplitude) {
+    channels.push_back(std::make_pair(vibratorId, amplitude));
+}
+
+bool VibrationElement::operator==(const VibrationElement& other) const {
+    if (duration != other.duration || channels.size() != other.channels.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < CHANNEL_SIZE; i++) {
+        if (channels[i] != other.channels[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool VibrationElement::operator!=(const VibrationElement& other) const {
+    return !(*this == other);
+}
+
+// VibrationSequence implementations
+VibrationSequence::VibrationSequence(size_t length) {
+    pattern.reserve(length);
+}
+
+void VibrationSequence::operator=(const VibrationSequence& other) {
+    pattern = other.pattern;
+}
+
+bool VibrationSequence::operator==(const VibrationSequence& other) const {
+    if (pattern.size() != other.pattern.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < pattern.size(); i++) {
+        if (pattern[i] != other.pattern[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void VibrationSequence::addElement(VibrationElement element) {
+    pattern.push_back(element);
+}
+
+const std::string VibrationSequence::toString() const {
+    std::string dump;
+    dump += "[";
+
+    for (const auto& element : pattern) {
+        dump += element.toString();
+        dump += " ";
+    }
+
+    dump += "]";
+    return dump;
 }
 
 } // namespace android
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index ffd8bf2..6cce8ec 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -101,10 +101,14 @@
     virtual void requestRefreshConfiguration(uint32_t changes) = 0;
 
     /* Controls the vibrator of a particular input device. */
-    virtual void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern,
-                         ssize_t repeat, int32_t token) = 0;
+    virtual void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
+                         int32_t token) = 0;
     virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
 
+    virtual bool isVibrating(int32_t deviceId) = 0;
+
+    virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
+
     /* Return true if the device can send input events to the specified display. */
     virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
 };
diff --git a/services/inputflinger/include/VibrationElement.h b/services/inputflinger/include/VibrationElement.h
index b60ffac..736041e 100644
--- a/services/inputflinger/include/VibrationElement.h
+++ b/services/inputflinger/include/VibrationElement.h
@@ -21,6 +21,7 @@
 #include <chrono>
 #include <cstdint>
 #include <string>
+#include <vector>
 
 namespace android {
 
@@ -32,13 +33,43 @@
 struct VibrationElement {
     std::chrono::milliseconds duration;
     // Channel amplitude range 0-255.
-    std::array<uint8_t, CHANNEL_SIZE> channels = {0, 0};
+    std::vector<std::pair<int32_t /*vibratorId*/, uint8_t /*amplitude*/>> channels;
+
+    explicit VibrationElement(size_t channelNum);
+
+    VibrationElement(const VibrationElement& other);
+
+    bool operator==(const VibrationElement& other) const;
+
+    bool operator!=(const VibrationElement& other) const;
+
+    void addChannel(int32_t vibratorId, uint8_t amplitude);
 
     const std::string toString() const;
-    uint16_t getMagnitude(size_t channelIndex) const;
+
+    uint16_t getMagnitude(int32_t vibratorId) const;
+
     bool isOn() const;
 };
 
+/*
+ * Describes a sequence of rumble effect
+ */
+struct VibrationSequence {
+    // Pattern of vibration elements
+    std::vector<VibrationElement> pattern;
+
+    explicit VibrationSequence(size_t length);
+
+    void operator=(const VibrationSequence& other);
+
+    bool operator==(const VibrationSequence& other) const;
+
+    void addElement(VibrationElement element);
+
+    const std::string toString() const;
+};
+
 } // namespace android
 
 #endif // _VIBRATION_ELEMENT_H
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 9671dec..f864c0e 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -61,8 +61,8 @@
 // v4l2 devices go directly into /dev
 static const char* VIDEO_DEVICE_PATH = "/dev";
 
-static constexpr size_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
-static constexpr size_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
+static constexpr int32_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
+static constexpr int32_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
 
 static inline const char* toString(bool value) {
     return value ? "true" : "false";
@@ -475,25 +475,25 @@
 }
 
 InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     return device != nullptr ? device->identifier : InputDeviceIdentifier();
 }
 
 Flags<InputDeviceClass> EventHub::getDeviceClasses(int32_t deviceId) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     return device != nullptr ? device->classes : Flags<InputDeviceClass>(0);
 }
 
 int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     return device != nullptr ? device->controllerNumber : 0;
 }
 
 void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr && device->configuration) {
         *outConfiguration = *device->configuration;
@@ -507,7 +507,7 @@
     outAxisInfo->clear();
 
     if (axis >= 0 && axis <= ABS_MAX) {
-        std::lock_guard<std::mutex> lock(mLock);
+        std::scoped_lock _l(mLock);
 
         Device* device = getDeviceLocked(deviceId);
         if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
@@ -534,7 +534,7 @@
 
 bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
     if (axis >= 0 && axis <= REL_MAX) {
-        std::lock_guard<std::mutex> lock(mLock);
+        std::scoped_lock _l(mLock);
         Device* device = getDeviceLocked(deviceId);
         return device != nullptr ? device->relBitmask.test(axis) : false;
     }
@@ -542,7 +542,7 @@
 }
 
 bool EventHub::hasInputProperty(int32_t deviceId, int property) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     Device* device = getDeviceLocked(deviceId);
     return property >= 0 && property <= INPUT_PROP_MAX && device != nullptr
@@ -552,7 +552,7 @@
 
 int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
     if (scanCode >= 0 && scanCode <= KEY_MAX) {
-        std::lock_guard<std::mutex> lock(mLock);
+        std::scoped_lock _l(mLock);
 
         Device* device = getDeviceLocked(deviceId);
         if (device != nullptr && device->hasValidFd() && device->keyBitmask.test(scanCode)) {
@@ -565,7 +565,7 @@
 }
 
 int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
@@ -588,7 +588,7 @@
 
 int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
     if (sw >= 0 && sw <= SW_MAX) {
-        std::lock_guard<std::mutex> lock(mLock);
+        std::scoped_lock _l(mLock);
 
         Device* device = getDeviceLocked(deviceId);
         if (device != nullptr && device->hasValidFd() && device->swBitmask.test(sw)) {
@@ -604,7 +604,7 @@
     *outValue = 0;
 
     if (axis >= 0 && axis <= ABS_MAX) {
-        std::lock_guard<std::mutex> lock(mLock);
+        std::scoped_lock _l(mLock);
 
         Device* device = getDeviceLocked(deviceId);
         if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
@@ -624,7 +624,7 @@
 
 bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
                                      uint8_t* outFlags) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr && device->keyMap.haveKeyLayout()) {
@@ -652,7 +652,7 @@
 
 status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
                           int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     status_t status = NAME_NOT_FOUND;
 
@@ -692,7 +692,7 @@
 }
 
 status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
 
     if (device != nullptr && device->keyMap.haveKeyLayout()) {
@@ -706,13 +706,13 @@
 }
 
 void EventHub::setExcludedDevices(const std::vector<std::string>& devices) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     mExcludedDevices = devices;
 }
 
 bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr && scanCode >= 0 && scanCode <= KEY_MAX) {
         return device->keyBitmask.test(scanCode);
@@ -721,7 +721,7 @@
 }
 
 bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     int32_t sc;
     if (device != nullptr && device->mapLed(led, &sc) == NO_ERROR) {
@@ -731,7 +731,7 @@
 }
 
 void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr && device->hasValidFd()) {
         device->setLedStateLocked(led, on);
@@ -742,7 +742,7 @@
                                         std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
     outVirtualKeys.clear();
 
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr && device->virtualKeyMap) {
         const std::vector<VirtualKeyDefinition> virtualKeys =
@@ -752,7 +752,7 @@
 }
 
 const std::shared_ptr<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr) {
         return device->getKeyCharacterMap();
@@ -761,7 +761,7 @@
 }
 
 bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr && map != nullptr && device->keyMap.keyCharacterMap != nullptr) {
         device->keyMap.keyCharacterMap->combine(*map);
@@ -822,7 +822,7 @@
 }
 
 void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr && device->hasValidFd()) {
         ff_effect effect;
@@ -857,7 +857,7 @@
 }
 
 void EventHub::cancelVibrate(int32_t deviceId) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr && device->hasValidFd()) {
         if (device->ffEffectPlaying) {
@@ -878,6 +878,18 @@
     }
 }
 
+std::vector<int32_t> EventHub::getVibratorIds(int32_t deviceId) {
+    std::scoped_lock _l(mLock);
+    std::vector<int32_t> vibrators;
+    Device* device = getDeviceLocked(deviceId);
+    if (device != nullptr && device->hasValidFd() &&
+        device->classes.test(InputDeviceClass::VIBRATOR)) {
+        vibrators.push_back(FF_STRONG_MAGNITUDE_CHANNEL_IDX);
+        vibrators.push_back(FF_WEAK_MAGNITUDE_CHANNEL_IDX);
+    }
+    return vibrators;
+}
+
 EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const {
     for (const auto& [id, device] : mDevices) {
         if (descriptor == device->identifier.descriptor) {
@@ -930,7 +942,7 @@
 size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
     ALOG_ASSERT(bufferSize >= 1);
 
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     struct input_event readBuffer[bufferSize];
 
@@ -1184,7 +1196,7 @@
 }
 
 std::vector<TouchVideoFrame> EventHub::getVideoFrames(int32_t deviceId) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     Device* device = getDeviceLocked(deviceId);
     if (device == nullptr || !device->videoDevice) {
@@ -1593,7 +1605,7 @@
 }
 
 bool EventHub::isDeviceEnabled(int32_t deviceId) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device == nullptr) {
         ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
@@ -1603,7 +1615,7 @@
 }
 
 status_t EventHub::enableDevice(int32_t deviceId) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device == nullptr) {
         ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
@@ -1625,7 +1637,7 @@
 }
 
 status_t EventHub::disableDevice(int32_t deviceId) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device == nullptr) {
         ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
@@ -1809,7 +1821,7 @@
 void EventHub::requestReopenDevices() {
     ALOGV("requestReopenDevices() called");
 
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     mNeedToReopenDevices = true;
 }
 
@@ -1817,7 +1829,7 @@
     dump += "Event Hub State:\n";
 
     { // acquire lock
-        std::lock_guard<std::mutex> lock(mLock);
+        std::scoped_lock _l(mLock);
 
         dump += StringPrintf(INDENT "BuiltInKeyboardId: %d\n", mBuiltInKeyboardId);
 
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 271bc2f..d25d64a 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -429,10 +429,9 @@
     return result;
 }
 
-void InputDevice::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
-                          int32_t token) {
-    for_each_mapper([pattern, repeat, token](InputMapper& mapper) {
-        mapper.vibrate(pattern, repeat, token);
+void InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {
+    for_each_mapper([sequence, repeat, token](InputMapper& mapper) {
+        mapper.vibrate(sequence, repeat, token);
     });
 }
 
@@ -440,6 +439,27 @@
     for_each_mapper([token](InputMapper& mapper) { mapper.cancelVibrate(token); });
 }
 
+bool InputDevice::isVibrating() {
+    bool vibrating = false;
+    for_each_mapper([&vibrating](InputMapper& mapper) { vibrating |= mapper.isVibrating(); });
+    return vibrating;
+}
+
+/* There's no guarantee the IDs provided by the different mappers are unique, so if we have two
+ * different vibration mappers then we could have duplicate IDs.
+ * Alternatively, if we have a merged device that has multiple evdev nodes with FF_* capabilities,
+ * we would definitely have duplicate IDs.
+ */
+std::vector<int32_t> InputDevice::getVibratorIds() {
+    std::vector<int32_t> vibrators;
+    for_each_mapper([&vibrators](InputMapper& mapper) {
+        std::vector<int32_t> devVibs = mapper.getVibratorIds();
+        vibrators.reserve(vibrators.size() + devVibs.size());
+        vibrators.insert(vibrators.end(), devVibs.begin(), devVibs.end());
+    });
+    return vibrators;
+}
+
 void InputDevice::cancelTouch(nsecs_t when) {
     for_each_mapper([when](InputMapper& mapper) { mapper.cancelTouch(when); });
 }
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 6216d78..a6b5e2d 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -56,7 +56,7 @@
     mQueuedListener = new QueuedInputListener(listener);
 
     { // acquire lock
-        std::lock_guard<std::mutex> lock(mLock);
+        std::scoped_lock _l(mLock);
 
         refreshConfigurationLocked(0);
         updateGlobalMetaStateLocked();
@@ -89,7 +89,7 @@
     bool inputDevicesChanged = false;
     std::vector<InputDeviceInfo> inputDevices;
     { // acquire lock
-        std::lock_guard<std::mutex> lock(mLock);
+        std::scoped_lock _l(mLock);
 
         oldGeneration = mGeneration;
         timeoutMillis = -1;
@@ -108,7 +108,7 @@
     size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
 
     { // acquire lock
-        std::lock_guard<std::mutex> lock(mLock);
+        std::scoped_lock _l(mLock);
         mReaderIsAliveCondition.notify_all();
 
         if (count) {
@@ -407,7 +407,7 @@
 }
 
 void InputReader::dispatchExternalStylusState(const StylusState& state) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     for (auto& devicePair : mDevices) {
         std::shared_ptr<InputDevice>& device = devicePair.second;
         device->updateExternalStylusState(state);
@@ -482,7 +482,7 @@
 }
 
 std::vector<InputDeviceInfo> InputReader::getInputDevices() const {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     return getInputDevicesLocked();
 }
 
@@ -501,19 +501,19 @@
 }
 
 int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState);
 }
 
 int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState);
 }
 
 int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState);
 }
@@ -545,7 +545,7 @@
 }
 
 void InputReader::toggleCapsLockState(int32_t deviceId) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
     InputDevice* device = findInputDeviceLocked(deviceId);
     if (!device) {
         ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
@@ -561,7 +561,7 @@
 
 bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
                           const int32_t* keyCodes, uint8_t* outFlags) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     memset(outFlags, 0, numCodes);
     return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags);
@@ -588,7 +588,7 @@
 }
 
 void InputReader::requestRefreshConfiguration(uint32_t changes) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     if (changes) {
         bool needWake = !mConfigurationChangesToRefresh;
@@ -600,17 +600,18 @@
     }
 }
 
-void InputReader::vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern,
-                          ssize_t repeat, int32_t token) {
-    std::lock_guard<std::mutex> lock(mLock);
+void InputReader::vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
+                          int32_t token) {
+    std::scoped_lock _l(mLock);
+
     InputDevice* device = findInputDeviceLocked(deviceId);
     if (device) {
-        device->vibrate(pattern, repeat, token);
+        device->vibrate(sequence, repeat, token);
     }
 }
 
 void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     InputDevice* device = findInputDeviceLocked(deviceId);
     if (device) {
@@ -618,8 +619,28 @@
     }
 }
 
+bool InputReader::isVibrating(int32_t deviceId) {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (device) {
+        return device->isVibrating();
+    }
+    return false;
+}
+
+std::vector<int32_t> InputReader::getVibratorIds(int32_t deviceId) {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (device) {
+        return device->getVibratorIds();
+    }
+    return {};
+}
+
 bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     InputDevice* device = findInputDeviceLocked(deviceId);
     if (device) {
@@ -630,7 +651,7 @@
 }
 
 bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     InputDevice* device = findInputDeviceLocked(deviceId);
     if (!device) {
@@ -658,7 +679,7 @@
 }
 
 void InputReader::dump(std::string& dump) {
-    std::lock_guard<std::mutex> lock(mLock);
+    std::scoped_lock _l(mLock);
 
     mEventHub->dump(dump);
     dump += "\n";
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index a3f881e..9e38d0a 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -233,6 +233,7 @@
     /* Control the vibrator. */
     virtual void vibrate(int32_t deviceId, const VibrationElement& effect) = 0;
     virtual void cancelVibrate(int32_t deviceId) = 0;
+    virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
 
     /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
     virtual void requestReopenDevices() = 0;
@@ -381,6 +382,7 @@
 
     void vibrate(int32_t deviceId, const VibrationElement& effect) override final;
     void cancelVibrate(int32_t deviceId) override final;
+    std::vector<int32_t> getVibratorIds(int32_t deviceId) override final;
 
     void requestReopenDevices() override final;
 
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 7d160eb..8b14b06 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -82,8 +82,10 @@
     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 std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token);
+    void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
     void cancelVibrate(int32_t token);
+    bool isVibrating();
+    std::vector<int32_t> getVibratorIds();
     void cancelTouch(nsecs_t when);
 
     int32_t getMetaState();
@@ -272,6 +274,8 @@
     }
     inline void cancelVibrate() { return mEventHub->cancelVibrate(mId); }
 
+    inline std::vector<int32_t> getVibratorIds() { return mEventHub->getVibratorIds(mId); }
+
     inline bool hasAbsoluteAxis(int32_t code) const {
         RawAbsoluteAxisInfo info;
         mEventHub->getAbsoluteAxisInfo(mId, code, &info);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 9b07681d..b16b86c 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -77,10 +77,14 @@
 
     void requestRefreshConfiguration(uint32_t changes) override;
 
-    void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern, ssize_t repeat,
+    void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
                  int32_t token) override;
     void cancelVibrate(int32_t deviceId, int32_t token) override;
 
+    bool isVibrating(int32_t deviceId) override;
+
+    std::vector<int32_t> getVibratorIds(int32_t deviceId) override;
+
     bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
 
 protected:
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 1db829f..913cef7 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -56,11 +56,18 @@
     return false;
 }
 
-void InputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
-                          int32_t token) {}
+void InputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {}
 
 void InputMapper::cancelVibrate(int32_t token) {}
 
+bool InputMapper::isVibrating() {
+    return false;
+}
+
+std::vector<int32_t> InputMapper::getVibratorIds() {
+    return {};
+}
+
 void InputMapper::cancelTouch(nsecs_t when) {}
 
 int32_t InputMapper::getMetaState() {
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 56ab928..088dbd8 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -63,9 +63,10 @@
     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 std::vector<VibrationElement>& pattern, ssize_t repeat,
-                         int32_t token);
+    virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
     virtual void cancelVibrate(int32_t token);
+    virtual bool isVibrating();
+    virtual std::vector<int32_t> getVibratorIds();
     virtual void cancelTouch(nsecs_t when);
 
     virtual int32_t getMetaState();
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index ac7c266..f25e59a 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -21,7 +21,7 @@
 namespace android {
 
 VibratorInputMapper::VibratorInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext), mVibrating(false) {}
+      : InputMapper(deviceContext), mVibrating(false), mSequence(0) {}
 
 VibratorInputMapper::~VibratorInputMapper() {}
 
@@ -39,17 +39,15 @@
     // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
 }
 
-void VibratorInputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
+void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat,
                                   int32_t token) {
 #if DEBUG_VIBRATOR
-    std::string patternStr;
-    dumpPattern(patternStr);
     ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
-          patternStr.c_str(), repeat, token);
+          sequence.toString().c_str(), repeat, token);
 #endif
 
     mVibrating = true;
-    mPattern = pattern;
+    mSequence = sequence;
     mRepeat = repeat;
     mToken = token;
     mIndex = -1;
@@ -67,6 +65,14 @@
     }
 }
 
+bool VibratorInputMapper::isVibrating() {
+    return mVibrating;
+}
+
+std::vector<int32_t> VibratorInputMapper::getVibratorIds() {
+    return getDeviceContext().getVibratorIds();
+}
+
 void VibratorInputMapper::timeoutExpired(nsecs_t when) {
     if (mVibrating) {
         if (when >= mNextStepTime) {
@@ -79,7 +85,7 @@
 
 void VibratorInputMapper::nextStep() {
     mIndex += 1;
-    if (size_t(mIndex) >= mPattern.size()) {
+    if (size_t(mIndex) >= mSequence.pattern.size()) {
         if (mRepeat < 0) {
             // We are done.
             stopVibrating();
@@ -88,7 +94,7 @@
         mIndex = mRepeat;
     }
 
-    const VibrationElement& element = mPattern[mIndex];
+    const VibrationElement& element = mSequence.pattern[mIndex];
     if (element.isOn()) {
 #if DEBUG_VIBRATOR
         std::string description = element.toString();
@@ -125,23 +131,10 @@
     dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating));
     if (mVibrating) {
         dump += INDENT3 "Pattern: ";
-        dumpPattern(dump);
+        dump += mSequence.toString();
         dump += "\n";
         dump += StringPrintf(INDENT3 "Repeat Index: %zd\n", mRepeat);
     }
 }
 
-void VibratorInputMapper::dumpPattern(std::string& dump) const {
-    dump += "[";
-
-    for (auto it = mPattern.begin(); it != mPattern.end(); ++it) {
-        dump += it->toString();
-        if (std::next(it) != mPattern.end()) {
-            dump += ", ";
-        }
-    }
-
-    dump += "]";
-}
-
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index bfa5ec1..7ce621a 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -30,21 +30,21 @@
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void process(const RawEvent* rawEvent) override;
 
-    virtual void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
-                         int32_t token) override;
+    virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) override;
     virtual void cancelVibrate(int32_t token) override;
+    virtual bool isVibrating() override;
+    virtual std::vector<int32_t> getVibratorIds() override;
     virtual void timeoutExpired(nsecs_t when) override;
     virtual void dump(std::string& dump) override;
 
 private:
     bool mVibrating;
-    std::vector<VibrationElement> mPattern;
+    VibrationSequence mSequence;
     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 5a4c881..cd61750 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -27,6 +27,7 @@
 #include <TestInputListener.h>
 #include <TouchInputMapper.h>
 #include <UinputDevice.h>
+#include <VibratorInputMapper.h>
 #include <android-base/thread_annotations.h>
 #include <gtest/gtest.h>
 #include <inttypes.h>
@@ -396,6 +397,7 @@
     std::vector<std::string> mExcludedDevices;
     List<RawEvent> mEvents GUARDED_BY(mLock);
     std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
+    std::vector<int32_t> mVibrators = {0, 1};
 
 public:
     virtual ~FakeEventHub() {
@@ -810,6 +812,8 @@
 
     void cancelVibrate(int32_t) override {}
 
+    std::vector<int32_t> getVibratorIds(int32_t deviceId) override { return mVibrators; };
+
     virtual bool isExternal(int32_t) const {
         return false;
     }
@@ -1816,6 +1820,31 @@
     ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
 }
 
+class FakeVibratorInputMapper : public FakeInputMapper {
+public:
+    FakeVibratorInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
+          : FakeInputMapper(deviceContext, sources) {}
+
+    std::vector<int32_t> getVibratorIds() override { return getDeviceContext().getVibratorIds(); }
+};
+
+TEST_F(InputReaderTest, VibratorGetVibratorIds) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::VIBRATOR;
+    constexpr int32_t eventHubId = 1;
+    const char* DEVICE_LOCATION = "BLUETOOTH";
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+    FakeVibratorInputMapper& mapper =
+            device->addMapper<FakeVibratorInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+    mReader->pushNextDevice(device);
+
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+    ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
+
+    ASSERT_EQ(mapper.getVibratorIds().size(), 2U);
+    ASSERT_EQ(mReader->getVibratorIds(deviceId).size(), 2U);
+}
+
 // --- InputReaderIntegrationTest ---
 
 // These tests create and interact with the InputReader only through its interface.
@@ -2496,6 +2525,46 @@
     ASSERT_EQ(uint32_t(0), args.policyFlags);
 }
 
+// --- VibratorInputMapperTest ---
+class VibratorInputMapperTest : public InputMapperTest {
+protected:
+    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::VIBRATOR); }
+};
+
+TEST_F(VibratorInputMapperTest, GetSources) {
+    VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+
+    ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
+}
+
+TEST_F(VibratorInputMapperTest, GetVibratorIds) {
+    VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+
+    ASSERT_EQ(mapper.getVibratorIds().size(), 2U);
+}
+
+TEST_F(VibratorInputMapperTest, Vibrate) {
+    constexpr uint8_t DEFAULT_AMPLITUDE = 192;
+    VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+
+    VibrationElement pattern(2);
+    VibrationSequence sequence(2);
+    pattern.duration = std::chrono::milliseconds(200);
+    pattern.channels = {{0 /* vibratorId */, DEFAULT_AMPLITUDE / 2},
+                        {1 /* vibratorId */, DEFAULT_AMPLITUDE}};
+    sequence.addElement(pattern);
+    pattern.duration = std::chrono::milliseconds(500);
+    pattern.channels = {{0 /* vibratorId */, DEFAULT_AMPLITUDE / 4},
+                        {1 /* vibratorId */, DEFAULT_AMPLITUDE}};
+    sequence.addElement(pattern);
+
+    std::vector<int64_t> timings = {0, 1};
+    std::vector<uint8_t> amplitudes = {DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE / 2};
+
+    ASSERT_FALSE(mapper.isVibrating());
+    mapper.vibrate(sequence, -1 /* repeat */, 0 /* token */);
+    ASSERT_TRUE(mapper.isVibrating());
+}
 
 // --- KeyboardInputMapperTest ---