Merge "Introduce IVibratorManager.aidl"
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 7a9feb2..ff925d9 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -534,6 +534,13 @@
             <instance>default</instance>
         </interface>
     </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.vibrator</name>
+        <interface>
+            <name>IVibratorManager</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.vr</name>
         <version>1.0</version>
diff --git a/tests/extension/vibrator/aidl/default/Android.bp b/tests/extension/vibrator/aidl/default/Android.bp
index ed40d25..80f7727 100644
--- a/tests/extension/vibrator/aidl/default/Android.bp
+++ b/tests/extension/vibrator/aidl/default/Android.bp
@@ -19,7 +19,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.vibrator-ndk_platform",
-        "android.hardware.tests.extension.vibrator-ndk_platform",
+        "android.hardware.vibrator-unstable-ndk_platform",
+        "android.hardware.tests.extension.vibrator-unstable-ndk_platform",
     ],
 }
diff --git a/vibrator/aidl/TEST_MAPPING b/vibrator/aidl/TEST_MAPPING
index 5ae32e7..2414b84 100644
--- a/vibrator/aidl/TEST_MAPPING
+++ b/vibrator/aidl/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "VtsHalVibratorTargetTest"
+    },
+    {
+      "name": "VtsHalVibratorManagerTargetTest"
     }
   ]
 }
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
new file mode 100644
index 0000000..99cd448
--- /dev/null
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
@@ -0,0 +1,35 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.vibrator;
+@VintfStability
+interface IVibratorManager {
+  int getCapabilities();
+  int[] getVibratorIds();
+  android.hardware.vibrator.IVibrator getVibrator(in int vibratorId);
+  void prepareSynced(in int[] vibratorIds);
+  void triggerSynced(in android.hardware.vibrator.IVibratorCallback callback);
+  void cancelSynced();
+  const int CAP_SYNC = 1;
+  const int CAP_PREPARE_ON = 2;
+  const int CAP_PREPARE_PERFORM = 4;
+  const int CAP_PREPARE_COMPOSE = 8;
+  const int CAP_MIXED_TRIGGER_ON = 16;
+  const int CAP_MIXED_TRIGGER_PERFORM = 32;
+  const int CAP_MIXED_TRIGGER_COMPOSE = 64;
+  const int CAP_TRIGGER_CALLBACK = 128;
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibratorManager.aidl b/vibrator/aidl/android/hardware/vibrator/IVibratorManager.aidl
new file mode 100644
index 0000000..eb5e9cc
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/IVibratorManager.aidl
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+package android.hardware.vibrator;
+
+import android.hardware.vibrator.IVibrator;
+import android.hardware.vibrator.IVibratorCallback;
+
+@VintfStability
+interface IVibratorManager {
+    /**
+     * Whether prepare/trigger synced are supported.
+     */
+    const int CAP_SYNC = 1 << 0;
+    /**
+     * Whether IVibrator 'on' can be used with 'prepareSynced' function.
+     */
+    const int CAP_PREPARE_ON = 1 << 1;
+    /**
+     * Whether IVibrator 'perform' can be used with 'prepareSynced' function.
+     */
+    const int CAP_PREPARE_PERFORM = 1 << 2;
+    /**
+     * Whether IVibrator 'compose' can be used with 'prepareSynced' function.
+     */
+    const int CAP_PREPARE_COMPOSE = 1 << 3;
+    /**
+     * Whether IVibrator 'on' can be triggered with other functions in sync with 'triggerSynced'.
+     */
+    const int CAP_MIXED_TRIGGER_ON = 1 << 4;
+    /**
+     * Whether IVibrator 'perform' can be triggered with other functions in sync with 'triggerSynced'.
+     */
+    const int CAP_MIXED_TRIGGER_PERFORM = 1 << 5;
+    /**
+     * Whether IVibrator 'compose' can be triggered with other functions in sync with 'triggerSynced'.
+     */
+    const int CAP_MIXED_TRIGGER_COMPOSE = 1 << 6;
+    /**
+     * Whether on w/ IVibratorCallback can be used w/ 'trigerSynced' function.
+     */
+    const int CAP_TRIGGER_CALLBACK = 1 << 7;
+
+    /**
+     * Determine capabilities of the vibrator manager HAL (CAP_* mask)
+     */
+    int getCapabilities();
+
+    /**
+     * List the id of available vibrators. This result should be static and not change.
+     */
+    int[] getVibratorIds();
+
+    /**
+     * Return an available vibrator identified with given id.
+     */
+    IVibrator getVibrator(in int vibratorId);
+
+    /**
+     * Start preparation for a synced vibration
+     *
+     * This function must only be called after the previous synced vibration was triggered or
+     * canceled (through cancelSynced()).
+     *
+     * Doing this operation while any of the specified vibrators is already on is undefined behavior.
+     * Clients should explicitly call off in each vibrator.
+     *
+     * @param vibratorIds ids of the vibrators to play vibrations in sync.
+     */
+    void prepareSynced(in int[] vibratorIds);
+
+    /**
+     * Trigger a prepared synced vibration
+     *
+     * Trigger a previously-started preparation for synced vibration, if any.
+     * A callback is only expected to be supported when getCapabilities CAP_TRIGGER_CALLBACK
+     * is specified.
+     *
+     * @param callback A callback used to inform Frameworks of state change, if supported.
+     */
+    void triggerSynced(in IVibratorCallback callback);
+
+    /**
+     * Cancel preparation of synced vibration
+     *
+     * Cancel a previously-started preparation for synced vibration, if any.
+     */
+    void cancelSynced();
+}
diff --git a/vibrator/aidl/default/Android.bp b/vibrator/aidl/default/Android.bp
index 9e6d9cf..f9d45bb 100644
--- a/vibrator/aidl/default/Android.bp
+++ b/vibrator/aidl/default/Android.bp
@@ -4,10 +4,13 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.vibrator-ndk_platform",
+        "android.hardware.vibrator-unstable-ndk_platform",
     ],
     export_include_dirs: ["include"],
-    srcs: ["Vibrator.cpp"],
+    srcs: [
+        "Vibrator.cpp",
+        "VibratorManager.cpp",
+    ],
     visibility: [
         ":__subpackages__",
         "//hardware/interfaces/tests/extension/vibrator:__subpackages__",
@@ -23,7 +26,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.vibrator-ndk_platform",
+        "android.hardware.vibrator-unstable-ndk_platform",
     ],
     static_libs: [
         "libvibratorexampleimpl",
diff --git a/vibrator/aidl/default/VibratorManager.cpp b/vibrator/aidl/default/VibratorManager.cpp
new file mode 100644
index 0000000..7cf9e6a
--- /dev/null
+++ b/vibrator/aidl/default/VibratorManager.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "vibrator-impl/VibratorManager.h"
+
+#include <android-base/logging.h>
+#include <thread>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+static constexpr int32_t kDefaultVibratorId = 1;
+
+ndk::ScopedAStatus VibratorManager::getCapabilities(int32_t* _aidl_return) {
+    LOG(INFO) << "Vibrator manager reporting capabilities";
+    *_aidl_return =
+            IVibratorManager::CAP_SYNC | IVibratorManager::CAP_PREPARE_ON |
+            IVibratorManager::CAP_PREPARE_PERFORM | IVibratorManager::CAP_PREPARE_COMPOSE |
+            IVibratorManager::CAP_MIXED_TRIGGER_ON | IVibratorManager::CAP_MIXED_TRIGGER_PERFORM |
+            IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE | IVibratorManager::CAP_TRIGGER_CALLBACK;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VibratorManager::getVibratorIds(std::vector<int32_t>* _aidl_return) {
+    LOG(INFO) << "Vibrator manager getting vibrator ids";
+    *_aidl_return = {kDefaultVibratorId};
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VibratorManager::getVibrator(int32_t vibratorId,
+                                                std::shared_ptr<IVibrator>* _aidl_return) {
+    LOG(INFO) << "Vibrator manager getting vibrator " << vibratorId;
+    if (vibratorId == kDefaultVibratorId) {
+        *_aidl_return = mDefaultVibrator;
+        return ndk::ScopedAStatus::ok();
+    } else {
+        *_aidl_return = nullptr;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+}
+
+ndk::ScopedAStatus VibratorManager::prepareSynced(const std::vector<int32_t>& vibratorIds) {
+    LOG(INFO) << "Vibrator Manager prepare synced";
+    if (vibratorIds.size() == 1 && vibratorIds[0] == kDefaultVibratorId) {
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+}
+
+ndk::ScopedAStatus VibratorManager::triggerSynced(
+        const std::shared_ptr<IVibratorCallback>& callback) {
+    LOG(INFO) << "Vibrator Manager trigger synced";
+    std::thread([=] {
+        if (callback != nullptr) {
+            LOG(INFO) << "Notifying perform complete";
+            callback->onComplete();
+        }
+    }).detach();
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VibratorManager::cancelSynced() {
+    LOG(INFO) << "Vibrator Manager cancel synced";
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/vibrator/aidl/default/include/vibrator-impl/VibratorManager.h b/vibrator/aidl/default/include/vibrator-impl/VibratorManager.h
new file mode 100644
index 0000000..319eb05
--- /dev/null
+++ b/vibrator/aidl/default/include/vibrator-impl/VibratorManager.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/vibrator/BnVibratorManager.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+class VibratorManager : public BnVibratorManager {
+  public:
+    VibratorManager(std::shared_ptr<IVibrator> vibrator) : mDefaultVibrator(std::move(vibrator)){};
+    ndk::ScopedAStatus getCapabilities(int32_t* _aidl_return) override;
+    ndk::ScopedAStatus getVibratorIds(std::vector<int32_t>* _aidl_return) override;
+    ndk::ScopedAStatus getVibrator(int32_t vibratorId,
+                                   std::shared_ptr<IVibrator>* _aidl_return) override;
+    ndk::ScopedAStatus prepareSynced(const std::vector<int32_t>& vibratorIds) override;
+    ndk::ScopedAStatus triggerSynced(const std::shared_ptr<IVibratorCallback>& callback) override;
+    ndk::ScopedAStatus cancelSynced() override;
+
+  private:
+    std::shared_ptr<IVibrator> mDefaultVibrator;
+};
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/vibrator/aidl/default/main.cpp b/vibrator/aidl/default/main.cpp
index ebb0905..bd834d2 100644
--- a/vibrator/aidl/default/main.cpp
+++ b/vibrator/aidl/default/main.cpp
@@ -15,19 +15,29 @@
  */
 
 #include "vibrator-impl/Vibrator.h"
+#include "vibrator-impl/VibratorManager.h"
 
 #include <android-base/logging.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 
 using aidl::android::hardware::vibrator::Vibrator;
+using aidl::android::hardware::vibrator::VibratorManager;
 
 int main() {
     ABinderProcess_setThreadPoolMaxThreadCount(0);
-    std::shared_ptr<Vibrator> vib = ndk::SharedRefBase::make<Vibrator>();
 
-    const std::string instance = std::string() + Vibrator::descriptor + "/default";
-    binder_status_t status = AServiceManager_addService(vib->asBinder().get(), instance.c_str());
+    // make a default vibrator service
+    auto vib = ndk::SharedRefBase::make<Vibrator>();
+    const std::string vibName = std::string() + Vibrator::descriptor + "/default";
+    binder_status_t status = AServiceManager_addService(vib->asBinder().get(), vibName.c_str());
+    CHECK(status == STATUS_OK);
+
+    // make the vibrator manager service with a different vibrator
+    auto managedVib = ndk::SharedRefBase::make<Vibrator>();
+    auto vibManager = ndk::SharedRefBase::make<VibratorManager>(std::move(managedVib));
+    const std::string vibManagerName = std::string() + VibratorManager::descriptor + "/default";
+    status = AServiceManager_addService(vibManager->asBinder().get(), vibManagerName.c_str());
     CHECK(status == STATUS_OK);
 
     ABinderProcess_joinThreadPool();
diff --git a/vibrator/aidl/default/vibrator-default.xml b/vibrator/aidl/default/vibrator-default.xml
index 49b11ec..9f9cd40 100644
--- a/vibrator/aidl/default/vibrator-default.xml
+++ b/vibrator/aidl/default/vibrator-default.xml
@@ -3,4 +3,8 @@
         <name>android.hardware.vibrator</name>
         <fqname>IVibrator/default</fqname>
     </hal>
+    <hal format="aidl">
+        <name>android.hardware.vibrator</name>
+        <fqname>IVibratorManager/default</fqname>
+    </hal>
 </manifest>
diff --git a/vibrator/aidl/vts/Android.bp b/vibrator/aidl/vts/Android.bp
index 28cb4d9..d06b50e 100644
--- a/vibrator/aidl/vts/Android.bp
+++ b/vibrator/aidl/vts/Android.bp
@@ -9,7 +9,26 @@
         "libbinder",
     ],
     static_libs: [
-        "android.hardware.vibrator-cpp",
+        "android.hardware.vibrator-unstable-cpp",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
+
+cc_test {
+    name: "VtsHalVibratorManagerTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalVibratorManagerTargetTest.cpp"],
+    shared_libs: [
+        "libbinder",
+    ],
+    static_libs: [
+        "android.hardware.vibrator-unstable-cpp",
     ],
     test_suites: [
         "general-tests",
diff --git a/vibrator/aidl/vts/VtsHalVibratorManagerTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorManagerTargetTest.cpp
new file mode 100644
index 0000000..9789188
--- /dev/null
+++ b/vibrator/aidl/vts/VtsHalVibratorManagerTargetTest.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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 <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <android/hardware/vibrator/BnVibratorCallback.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <android/hardware/vibrator/IVibratorManager.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include <cmath>
+#include <future>
+
+using android::ProcessState;
+using android::sp;
+using android::String16;
+using android::binder::Status;
+using android::hardware::vibrator::BnVibratorCallback;
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::IVibratorManager;
+using std::chrono::high_resolution_clock;
+
+const std::vector<Effect> kEffects{android::enum_range<Effect>().begin(),
+                                   android::enum_range<Effect>().end()};
+const std::vector<EffectStrength> kEffectStrengths{android::enum_range<EffectStrength>().begin(),
+                                                   android::enum_range<EffectStrength>().end()};
+const std::vector<CompositePrimitive> kPrimitives{android::enum_range<CompositePrimitive>().begin(),
+                                                  android::enum_range<CompositePrimitive>().end()};
+
+class CompletionCallback : public BnVibratorCallback {
+  public:
+    CompletionCallback(const std::function<void()>& callback) : mCallback(callback) {}
+    Status onComplete() override {
+        mCallback();
+        return Status::ok();
+    }
+
+  private:
+    std::function<void()> mCallback;
+};
+
+class VibratorAidl : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        manager = android::waitForDeclaredService<IVibratorManager>(String16(GetParam().c_str()));
+        ASSERT_NE(manager, nullptr);
+        ASSERT_TRUE(manager->getCapabilities(&capabilities).isOk());
+        EXPECT_TRUE(manager->getVibratorIds(&vibratorIds).isOk());
+    }
+
+    sp<IVibratorManager> manager;
+    int32_t capabilities;
+    std::vector<int32_t> vibratorIds;
+};
+
+TEST_P(VibratorAidl, ValidateExistingVibrators) {
+    sp<IVibrator> vibrator;
+    for (auto& id : vibratorIds) {
+        EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+        ASSERT_NE(vibrator, nullptr);
+    }
+}
+
+TEST_P(VibratorAidl, GetVibratorWithInvalidId) {
+    int32_t invalidId = *max_element(vibratorIds.begin(), vibratorIds.end()) + 1;
+    sp<IVibrator> vibrator;
+    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+              manager->getVibrator(invalidId, &vibrator).exceptionCode());
+    ASSERT_EQ(vibrator, nullptr);
+}
+
+TEST_P(VibratorAidl, ValidatePrepareSyncedExistingVibrators) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (vibratorIds.empty()) return;
+    EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+}
+
+TEST_P(VibratorAidl, PrepareSyncedEmptySetIsInvalid) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    std::vector<int32_t> emptyIds;
+    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, manager->prepareSynced(emptyIds).exceptionCode());
+}
+
+TEST_P(VibratorAidl, PrepareSyncedNotSupported) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) {
+        EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION,
+                  manager->prepareSynced(vibratorIds).exceptionCode());
+    }
+}
+
+TEST_P(VibratorAidl, PrepareOnNotSupported) {
+    if (vibratorIds.empty()) return;
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (!(capabilities & IVibratorManager::CAP_PREPARE_ON)) {
+        uint32_t durationMs = 250;
+        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+        sp<IVibrator> vibrator;
+        for (auto& id : vibratorIds) {
+            EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+            ASSERT_NE(vibrator, nullptr);
+            EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION,
+                      vibrator->on(durationMs, nullptr).exceptionCode());
+        }
+        EXPECT_TRUE(manager->cancelSynced().isOk());
+    }
+}
+
+TEST_P(VibratorAidl, PreparePerformNotSupported) {
+    if (vibratorIds.empty()) return;
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (!(capabilities & IVibratorManager::CAP_PREPARE_ON)) {
+        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+        sp<IVibrator> vibrator;
+        for (auto& id : vibratorIds) {
+            EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+            ASSERT_NE(vibrator, nullptr);
+            int32_t lengthMs = 0;
+            Status status = vibrator->perform(kEffects[0], kEffectStrengths[0], nullptr, &lengthMs);
+            EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, status.exceptionCode());
+        }
+        EXPECT_TRUE(manager->cancelSynced().isOk());
+    }
+}
+
+TEST_P(VibratorAidl, PrepareComposeNotSupported) {
+    if (vibratorIds.empty()) return;
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (!(capabilities & IVibratorManager::CAP_PREPARE_ON)) {
+        std::vector<CompositeEffect> composite;
+        CompositeEffect effect;
+        effect.delayMs = 10;
+        effect.primitive = kPrimitives[0];
+        effect.scale = 1.0f;
+        composite.emplace_back(effect);
+
+        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+        sp<IVibrator> vibrator;
+        for (auto& id : vibratorIds) {
+            EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+            ASSERT_NE(vibrator, nullptr);
+            Status status = vibrator->compose(composite, nullptr);
+            EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, status.exceptionCode());
+        }
+        EXPECT_TRUE(manager->cancelSynced().isOk());
+    }
+}
+
+TEST_P(VibratorAidl, TriggerWithCallback) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (!(capabilities & IVibratorManager::CAP_PREPARE_ON)) return;
+    if (!(capabilities & IVibratorManager::CAP_TRIGGER_CALLBACK)) return;
+    if (vibratorIds.empty()) return;
+
+    std::promise<void> completionPromise;
+    std::future<void> completionFuture{completionPromise.get_future()};
+    sp<CompletionCallback> callback =
+            new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+    uint32_t durationMs = 250;
+    std::chrono::milliseconds timeout{durationMs * 2};
+
+    EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+    sp<IVibrator> vibrator;
+    for (auto& id : vibratorIds) {
+        EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+        ASSERT_NE(vibrator, nullptr);
+        EXPECT_TRUE(vibrator->on(durationMs, nullptr).isOk());
+    }
+
+    EXPECT_TRUE(manager->triggerSynced(callback).isOk());
+    EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
+    EXPECT_TRUE(manager->cancelSynced().isOk());
+}
+
+TEST_P(VibratorAidl, TriggerSyncNotSupported) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) {
+        EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION,
+                  manager->triggerSynced(nullptr).exceptionCode());
+    }
+}
+
+TEST_P(VibratorAidl, TriggerCallbackNotSupported) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (!(capabilities & IVibratorManager::CAP_TRIGGER_CALLBACK)) {
+        sp<CompletionCallback> callback = new CompletionCallback([] {});
+        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+        EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION,
+                  manager->triggerSynced(callback).exceptionCode());
+    }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VibratorAidl);
+INSTANTIATE_TEST_SUITE_P(
+        Vibrator, VibratorAidl,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IVibratorManager::descriptor)),
+        android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index 888a403..dfd2524 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -18,6 +18,7 @@
 
 #include <android/hardware/vibrator/BnVibratorCallback.h>
 #include <android/hardware/vibrator/IVibrator.h>
+#include <android/hardware/vibrator/IVibratorManager.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 
@@ -34,6 +35,7 @@
 using android::hardware::vibrator::Effect;
 using android::hardware::vibrator::EffectStrength;
 using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::IVibratorManager;
 using std::chrono::high_resolution_clock;
 
 const std::vector<Effect> kEffects{android::enum_range<Effect>().begin(),
@@ -77,10 +79,28 @@
     std::function<void()> mCallback;
 };
 
-class VibratorAidl : public testing::TestWithParam<std::string> {
+class VibratorAidl : public testing::TestWithParam<std::tuple<int32_t, int32_t>> {
   public:
     virtual void SetUp() override {
-        vibrator = android::waitForDeclaredService<IVibrator>(String16(GetParam().c_str()));
+        int32_t managerIdx = std::get<0>(GetParam());
+        int32_t vibratorId = std::get<1>(GetParam());
+        auto managerAidlNames = android::getAidlHalInstanceNames(IVibratorManager::descriptor);
+
+        if (managerIdx < 0) {
+            // Testing a unmanaged vibrator, using vibratorId as index from registered HALs
+            auto vibratorAidlNames = android::getAidlHalInstanceNames(IVibrator::descriptor);
+            ASSERT_LT(vibratorId, vibratorAidlNames.size());
+            auto vibratorName = String16(vibratorAidlNames[vibratorId].c_str());
+            vibrator = android::waitForDeclaredService<IVibrator>(vibratorName);
+        } else {
+            // Testing a managed vibrator, using vibratorId to retrieve it from the manager
+            ASSERT_LT(managerIdx, managerAidlNames.size());
+            auto managerName = String16(managerAidlNames[managerIdx].c_str());
+            auto vibratorManager = android::waitForDeclaredService<IVibratorManager>(managerName);
+            auto vibratorResult = vibratorManager->getVibrator(vibratorId, &vibrator);
+            ASSERT_TRUE(vibratorResult.isOk());
+        }
+
         ASSERT_NE(vibrator, nullptr);
         ASSERT_TRUE(vibrator->getCapabilities(&capabilities).isOk());
     }
@@ -518,10 +538,41 @@
     }
 }
 
+std::vector<std::tuple<int32_t, int32_t>> GenerateVibratorMapping() {
+    std::vector<std::tuple<int32_t, int32_t>> tuples;
+    auto managerAidlNames = android::getAidlHalInstanceNames(IVibratorManager::descriptor);
+    std::vector<int32_t> vibratorIds;
+
+    for (int i = 0; i < managerAidlNames.size(); i++) {
+        auto managerName = String16(managerAidlNames[i].c_str());
+        auto vibratorManager = android::waitForDeclaredService<IVibratorManager>(managerName);
+        if (vibratorManager->getVibratorIds(&vibratorIds).isOk()) {
+            for (auto& vibratorId : vibratorIds) {
+                tuples.push_back(std::make_tuple(i, vibratorId));
+            }
+        }
+    }
+
+    auto vibratorAidlNames = android::getAidlHalInstanceNames(IVibrator::descriptor);
+    for (int i = 0; i < vibratorAidlNames.size(); i++) {
+        tuples.push_back(std::make_tuple(-1, i));
+    }
+
+    return tuples;
+}
+
+std::string PrintGeneratedTest(const testing::TestParamInfo<VibratorAidl::ParamType>& info) {
+    const auto& [managerIdx, vibratorId] = info.param;
+    if (managerIdx < 0) {
+        return std::string("TOP_LEVEL_VIBRATOR_") + std::to_string(vibratorId);
+    }
+    return std::string("MANAGER_") + std::to_string(managerIdx) + "_VIBRATOR_ID_" +
+           std::to_string(vibratorId);
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VibratorAidl);
-INSTANTIATE_TEST_SUITE_P(Vibrator, VibratorAidl,
-                         testing::ValuesIn(android::getAidlHalInstanceNames(IVibrator::descriptor)),
-                         android::PrintInstanceNameToString);
+INSTANTIATE_TEST_SUITE_P(Vibrator, VibratorAidl, testing::ValuesIn(GenerateVibratorMapping()),
+                         PrintGeneratedTest);
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);