Add CLI for Directly Invoking IDL APIs

Initial implementation supports IVibrator only.

Test: Manual Execution
Change-Id: Iaaa2593e7926dc202a89a59c111869cd65f31332
Signed-off-by: Harpreet \"Eli\" Sangha <eliptus@google.com>
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
new file mode 100644
index 0000000..40b8dc7
--- /dev/null
+++ b/cmds/idlcli/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2019 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.
+
+cc_defaults {
+    name: "idlcli-defaults",
+    shared_libs: [
+        "android.hardware.vibrator@1.0",
+        "android.hardware.vibrator@1.1",
+        "android.hardware.vibrator@1.2",
+        "android.hardware.vibrator@1.3",
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+    cflags: [
+        "-DLOG_TAG=\"idlcli\"",
+    ],
+}
+
+cc_library {
+    name: "libidlcli",
+    defaults: ["idlcli-defaults"],
+    srcs: [
+        "CommandVibrator.cpp",
+        "vibrator/CommandOff.cpp",
+        "vibrator/CommandOn.cpp",
+        "vibrator/CommandPerform.cpp",
+        "vibrator/CommandSetAmplitude.cpp",
+        "vibrator/CommandSetExternalControl.cpp",
+        "vibrator/CommandSupportsAmplitudeControl.cpp",
+        "vibrator/CommandSupportsExternalControl.cpp",
+    ],
+    visibility: [":__subpackages__"],
+}
+
+cc_binary {
+    name: "idlcli",
+    defaults: ["idlcli-defaults"],
+    srcs: ["main.cpp"],
+    whole_static_libs: ["libidlcli"],
+}
diff --git a/cmds/idlcli/CommandVibrator.cpp b/cmds/idlcli/CommandVibrator.cpp
new file mode 100644
index 0000000..a7a70c3
--- /dev/null
+++ b/cmds/idlcli/CommandVibrator.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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 "utils.h"
+
+namespace android {
+namespace idlcli {
+
+class IdlCli;
+
+class CommandVibrator : public CommandWithSubcommands<CommandVibrator> {
+    std::string getDescription() const override { return "Invoke Vibrator HIDL APIs."; }
+
+    std::string getUsageSummary() const override { return "<api> [arguments]"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<api>", CommandRegistry<CommandVibrator>::List()},
+        };
+        return details;
+    }
+};
+
+static const auto Command = CommandRegistry<IdlCli>::Register<CommandVibrator>("vibrator");
+
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/IdlCli.h b/cmds/idlcli/IdlCli.h
new file mode 100644
index 0000000..dd84304
--- /dev/null
+++ b/cmds/idlcli/IdlCli.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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 FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_
+#define FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_
+
+#include "utils.h"
+
+namespace android {
+namespace idlcli {
+
+class IdlCli : public CommandWithSubcommands<IdlCli> {
+    std::string getDescription() const override { return "Invoke IDL APIs."; }
+
+    std::string getUsageSummary() const override { return "<idl> [arguments]"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<idl>", CommandRegistry<IdlCli>::List()},
+        };
+        return details;
+    }
+};
+
+} // namespace idlcli
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_
diff --git a/cmds/idlcli/main.cpp b/cmds/idlcli/main.cpp
new file mode 100644
index 0000000..9ed9d82
--- /dev/null
+++ b/cmds/idlcli/main.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 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 "IdlCli.h"
+#include "utils.h"
+
+int main(const int argc, const char* const argv[]) {
+    using namespace ::android::idlcli;
+    return IdlCli{}.main(Args{argc, argv});
+}
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
new file mode 100644
index 0000000..a8e5954
--- /dev/null
+++ b/cmds/idlcli/utils.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2019 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 FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
+#define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
+
+#include <hidl/HidlSupport.h>
+
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace idlcli {
+
+namespace overrides {
+
+namespace details {
+
+template <typename T>
+inline std::istream &operator>>(std::istream &stream, T &out) {
+    auto pos = stream.tellg();
+    auto tmp = +out;
+    auto min = +std::numeric_limits<T>::min();
+    auto max = +std::numeric_limits<T>::max();
+    stream >> tmp;
+    if (!stream) {
+        return stream;
+    }
+    if (tmp < min || tmp > max) {
+        stream.seekg(pos);
+        stream.setstate(std::ios_base::failbit);
+        return stream;
+    }
+    out = tmp;
+    return stream;
+}
+
+} // namespace details
+
+// override for default behavior of treating as a character
+inline std::istream &operator>>(std::istream &stream, int8_t &out) {
+    return details::operator>>(stream, out);
+}
+
+// override for default behavior of treating as a character
+inline std::istream &operator>>(std::istream &stream, uint8_t &out) {
+    return details::operator>>(stream, out);
+}
+
+} // namespace overrides
+
+template <typename T, typename R = hardware::hidl_enum_range<T>>
+inline std::istream &operator>>(std::istream &stream, T &out) {
+    using overrides::operator>>;
+    auto validRange = R();
+    auto pos = stream.tellg();
+    std::underlying_type_t<T> in;
+    T tmp;
+    stream >> in;
+    if (!stream) {
+        return stream;
+    }
+    tmp = static_cast<T>(in);
+    if (tmp < *validRange.begin() || tmp > *std::prev(validRange.end())) {
+        stream.seekg(pos);
+        stream.setstate(std::ios_base::failbit);
+        return stream;
+    }
+    out = tmp;
+    return stream;
+}
+
+enum Status : unsigned int {
+    OK,
+    USAGE,
+    UNAVAILABLE,
+    ERROR,
+};
+
+class Args {
+public:
+    Args(const int argc, const char *const argv[]) {
+        for (int argi = 0; argi < argc; argi++) {
+            mArgs.emplace_back(std::string_view(argv[argi]));
+        }
+    }
+
+    template <typename T = std::string>
+    std::optional<T> get() {
+        return get<T>(false);
+    }
+
+    template <typename T = std::string>
+    std::optional<T> pop() {
+        return get<T>(true);
+    }
+
+    bool empty() { return mArgs.empty(); }
+
+private:
+    template <typename T>
+    std::optional<T> get(bool erase) {
+        using idlcli::operator>>;
+        using overrides::operator>>;
+        T retValue;
+
+        if (mArgs.empty()) {
+            return {};
+        }
+
+        std::stringstream stream{std::string{mArgs.front()}};
+        stream >> std::setbase(0) >> retValue;
+        if (!stream || !stream.eof()) {
+            return {};
+        }
+
+        if (erase) {
+            mArgs.erase(mArgs.begin());
+        }
+
+        return retValue;
+    }
+
+    std::vector<std::string_view> mArgs;
+};
+
+class Command {
+protected:
+    struct Usage {
+        std::string name;
+        std::vector<std::string> details;
+    };
+    using UsageDetails = std::vector<Usage>;
+
+public:
+    virtual ~Command() = default;
+
+    Status main(Args &&args) {
+        Status status = doArgsAndMain(std::move(args));
+        if (status == USAGE) {
+            printUsage();
+            return ERROR;
+        }
+        if (status == UNAVAILABLE) {
+            std::cerr << "The requested operation is unavailable." << std::endl;
+            return ERROR;
+        }
+        return status;
+    }
+
+private:
+    virtual std::string getDescription() const = 0;
+    virtual std::string getUsageSummary() const = 0;
+    virtual UsageDetails getUsageDetails() const = 0;
+    virtual Status doArgs(Args &args) = 0;
+    virtual Status doMain(Args &&args) = 0;
+
+    void printUsage() const {
+        std::cerr << "Description:\n  " << getDescription() << std::endl;
+        std::cerr << "Usage:\n  " << mName << " " << getUsageSummary() << std::endl;
+
+        std::cerr << "Details:" << std::endl;
+        size_t entryNameWidth = 0;
+        for (auto &entry : getUsageDetails()) {
+            entryNameWidth = std::max(entryNameWidth, entry.name.length());
+        }
+        for (auto &entry : getUsageDetails()) {
+            auto prefix = entry.name;
+            for (auto &line : entry.details) {
+                std::cerr << "  " << std::left << std::setw(entryNameWidth + 8) << prefix << line
+                          << std::endl;
+                prefix = "";
+            }
+        }
+    }
+
+    Status doArgsAndMain(Args &&args) {
+        Status status;
+        mName = *args.pop();
+        if ((status = doArgs(args)) != OK) {
+            return status;
+        }
+        if ((status = doMain(std::move(args))) != OK) {
+            return status;
+        }
+        return OK;
+    }
+
+protected:
+    std::string mName;
+};
+
+template <typename T>
+class CommandRegistry {
+private:
+    using CommandCreator = std::function<std::unique_ptr<Command>()>;
+
+public:
+    template <typename U>
+    static CommandCreator Register(const std::string name) {
+        Instance()->mCommands[name] = [] { return std::make_unique<U>(); };
+        return Instance()->mCommands[name];
+    }
+
+    static std::unique_ptr<Command> Create(const std::string name) {
+        auto it = Instance()->mCommands.find(name);
+        if (it == Instance()->mCommands.end()) {
+            return nullptr;
+        }
+        return it->second();
+    }
+
+    static auto List() {
+        std::vector<std::string> list;
+        for (auto &it : Instance()->mCommands) {
+            list.push_back(it.first);
+        }
+        std::sort(list.begin(), list.end());
+        return list;
+    }
+
+private:
+    static CommandRegistry *Instance() {
+        static CommandRegistry sRegistry;
+        return &sRegistry;
+    }
+
+private:
+    std::map<const std::string, CommandCreator> mCommands;
+};
+
+template <typename T>
+class CommandWithSubcommands : public Command {
+private:
+    Status doArgs(Args &args) override {
+        mCommand = CommandRegistry<T>::Create(*args.get());
+        if (!mCommand) {
+            std::cerr << "Invalid Command!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args &&args) override { return mCommand->main(std::move(args)); }
+
+protected:
+    std::unique_ptr<Command> mCommand;
+};
+
+} // namespace idlcli
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
new file mode 100644
index 0000000..bcb207b
--- /dev/null
+++ b/cmds/idlcli/vibrator.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 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 FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+
+#include "utils.h"
+
+#include "log/log.h"
+
+namespace android {
+
+using hardware::Return;
+
+static constexpr int NUM_TRIES = 2;
+
+// Creates a Return<R> with STATUS::EX_NULL_POINTER.
+template <class R>
+inline Return<R> NullptrStatus() {
+    using ::android::hardware::Status;
+    return Return<R>{Status::fromExceptionCode(Status::EX_NULL_POINTER)};
+}
+
+template <typename I>
+class HalWrapper {
+public:
+    static std::unique_ptr<HalWrapper> Create() {
+        // Assume that if getService returns a nullptr, HAL is not available on the
+        // device.
+        auto hal = I::getService();
+        return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
+    }
+
+    template <class R, class... Args0, class... Args1>
+    Return<R> call(Return<R> (I::*fn)(Args0...), Args1&&... args1) {
+        return (*mHal.*fn)(std::forward<Args1>(args1)...);
+    }
+
+private:
+    HalWrapper(sp<I>&& hal) : mHal(std::move(hal)) {}
+
+private:
+    sp<I> mHal;
+};
+
+template <typename I>
+static auto getHal() {
+    static auto sHalWrapper = HalWrapper<I>::Create();
+    return sHalWrapper.get();
+}
+
+template <class R, class I, class... Args0, class... Args1>
+Return<R> halCall(Return<R> (I::*fn)(Args0...), Args1&&... args1) {
+    auto hal = getHal<I>();
+    return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
+}
+
+namespace idlcli {
+namespace vibrator {
+
+namespace V1_0 = ::android::hardware::vibrator::V1_0;
+namespace V1_1 = ::android::hardware::vibrator::V1_1;
+namespace V1_2 = ::android::hardware::vibrator::V1_2;
+namespace V1_3 = ::android::hardware::vibrator::V1_3;
+
+} // namespace vibrator
+} // namespace idlcli
+
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp
new file mode 100644
index 0000000..a674f01
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandOff.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandOff : public Command {
+    std::string getDescription() const override { return "Turn off vibrator."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::off);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandOff>("off");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
new file mode 100644
index 0000000..2164b7d
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandOn : public Command {
+    std::string getDescription() const override { return "Turn on vibrator."; }
+
+    std::string getUsageSummary() const override { return "<duration>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<duration>", {"In milliseconds."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto duration = args.pop<decltype(mDuration)>()) {
+            mDuration = *duration;
+        } else {
+            std::cerr << "Missing or Invalid Duration!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::on, mDuration);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    uint32_t mDuration;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandOn>("on");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
new file mode 100644
index 0000000..688cbd8
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using V1_0::EffectStrength;
+using V1_3::Effect;
+
+class CommandPerform : public Command {
+    std::string getDescription() const override { return "Perform vibration effect."; }
+
+    std::string getUsageSummary() const override { return "<effect> <strength>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<effect>", {"Effect ID."}},
+                {"<strength>", {"0-2."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto effect = args.pop<decltype(mEffect)>()) {
+            mEffect = *effect;
+            std::cout << "Effect: " << toString(mEffect) << std::endl;
+        } else {
+            std::cerr << "Missing or Invalid Effect!" << std::endl;
+            return USAGE;
+        }
+        if (auto strength = args.pop<decltype(mStrength)>()) {
+            mStrength = *strength;
+            std::cout << "Strength: " << toString(mStrength) << std::endl;
+        } else {
+            std::cerr << "Missing or Invalid Strength!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        Return<void> ret;
+        V1_0::Status status;
+        uint32_t lengthMs;
+        auto callback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
+            status = retStatus;
+            lengthMs = retLengthMs;
+        };
+
+        if (auto hal = getHal<V1_3::IVibrator>()) {
+            ret = hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
+                            mStrength, callback);
+        } else if (auto hal = getHal<V1_2::IVibrator>()) {
+            ret = hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
+                            mStrength, callback);
+        } else if (auto hal = getHal<V1_1::IVibrator>()) {
+            ret = hal->call(&V1_1::IVibrator::perform_1_1, static_cast<V1_1::Effect_1_1>(mEffect),
+                            mStrength, callback);
+        } else if (auto hal = getHal<V1_0::IVibrator>()) {
+            ret = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
+                            mStrength, callback);
+        } else {
+            ret = NullptrStatus<void>();
+        }
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(status) << std::endl;
+        std::cout << "Length: " << lengthMs << std::endl;
+
+        return status == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    Effect mEffect;
+    EffectStrength mStrength;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandPerform>("perform");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
new file mode 100644
index 0000000..38a1dc2
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSetAmplitude : public Command {
+    std::string getDescription() const override { return "Set vibration amplitude."; }
+
+    std::string getUsageSummary() const override { return "<amplitude>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<amplitude>", {"1-255."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto amplitude = args.pop<decltype(mAmplitude)>()) {
+            mAmplitude = *amplitude;
+        } else {
+            std::cerr << "Missing or Invalid Amplitude!" << std::endl;
+            return USAGE;
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::setAmplitude, mAmplitude);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    uint8_t mAmplitude;
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSetAmplitude>("setAmplitude");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
new file mode 100644
index 0000000..5fb1fac
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSetExternalControl : public Command {
+    std::string getDescription() const override {
+        return "Enable/disable vibration external control.";
+    }
+
+    std::string getUsageSummary() const override { return "<enable>"; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<enable>", {"0/1."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (auto enable = args.pop<decltype(mEnable)>()) {
+            mEnable = *enable;
+        } else {
+            std::cerr << "Missing Enable!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_3::IVibrator::setExternalControl, mEnable);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << toString(ret) << std::endl;
+
+        return ret == V1_0::Status::OK ? OK : ERROR;
+    }
+
+    bool mEnable;
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSetExternalControl>("setExternalControl");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
new file mode 100644
index 0000000..cdc529a
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSupportsAmplitudeControl : public Command {
+    std::string getDescription() const override { return "Check support for amplitude control."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_0::IVibrator::supportsAmplitudeControl);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Result: " << std::boolalpha << ret << std::endl;
+
+        return OK;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSupportsAmplitudeControl>(
+                "supportsAmplitudeControl");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
new file mode 100644
index 0000000..ed15d76
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandSupportsExternalControl : public Command {
+    std::string getDescription() const override { return "Check support for external control."; }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        auto ret = halCall(&V1_3::IVibrator::supportsExternalControl);
+
+        if (!ret.isOk()) {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Result: " << std::boolalpha << ret << std::endl;
+
+        return OK;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandSupportsExternalControl>(
+                "supportsExternalControl");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android