idlcli: vibrator: Add Blocking Options

Test: Manually on Pixel CS40L25 and DRV2624 Targets
Signed-off-by: Harpreet \"Eli\" Sangha <eliptus@google.com>
Change-Id: I3fbe889b82e80c48cc1a4ffd16f2efe0aa4672c5
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index ca5142d..6c30a9e 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -16,8 +16,12 @@
 #ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
 #define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
 
+#include <future>
+
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
 #include <aidl/android/hardware/vibrator/IVibrator.h>
 #include <android/binder_manager.h>
+#include <android/binder_process.h>
 #include <android/hardware/vibrator/1.3/IVibrator.h>
 
 #include "utils.h"
@@ -101,6 +105,18 @@
 namespace V1_3 = ::android::hardware::vibrator::V1_3;
 namespace aidl = ::aidl::android::hardware::vibrator;
 
+class VibratorCallback : public aidl::BnVibratorCallback {
+public:
+    ndk::ScopedAStatus onComplete() override {
+        mPromise.set_value();
+        return ndk::ScopedAStatus::ok();
+    }
+    void waitForComplete() { mPromise.get_future().wait(); }
+
+private:
+    std::promise<void> mPromise;
+};
+
 } // namespace vibrator
 } // namespace idlcli
 
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index 4721a5f..0b4c93c 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -28,10 +28,13 @@
 class CommandCompose : public Command {
     std::string getDescription() const override { return "Compose vibration."; }
 
-    std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; }
+    std::string getUsageSummary() const override {
+        return "[options] <delay> <primitive> <scale> ...";
+    }
 
     UsageDetails getUsageDetails() const override {
         UsageDetails details{
+                {"-b", {"Block for duration of vibration."}},
                 {"<delay>", {"In milliseconds"}},
                 {"<primitive>", {"Primitive ID."}},
                 {"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}},
@@ -41,6 +44,17 @@
     }
 
     Status doArgs(Args &args) override {
+        while (args.get<std::string>().value_or("").find("-") == 0) {
+            auto opt = *args.pop<std::string>();
+            if (opt == "--") {
+                break;
+            } else if (opt == "-b") {
+                mBlocking = true;
+            } else {
+                std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+                return USAGE;
+            }
+        }
         while (!args.empty()) {
             CompositeEffect effect;
             if (auto delay = args.pop<decltype(effect.delayMs)>()) {
@@ -76,21 +90,33 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        std::string statusStr;
-        Status ret;
-        if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
-            statusStr = status.getDescription();
-            ret = status.isOk() ? OK : ERROR;
-        } else {
+        auto hal = getHal<aidl::IVibrator>();
+
+        if (!hal) {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << statusStr << std::endl;
+        ABinderProcess_setThreadPoolMaxThreadCount(1);
+        ABinderProcess_startThreadPool();
 
-        return ret;
+        std::shared_ptr<VibratorCallback> callback;
+
+        if (mBlocking) {
+            callback = ndk::SharedRefBase::make<VibratorCallback>();
+        }
+
+        auto status = hal->call(&aidl::IVibrator::compose, mComposite, callback);
+
+        if (status.isOk() && callback) {
+            callback->waitForComplete();
+        }
+
+        std::cout << "Status: " << status.getDescription() << std::endl;
+
+        return status.isOk() ? OK : ERROR;
     }
 
+    bool mBlocking;
     std::vector<CompositeEffect> mComposite;
 };
 
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index 4e7e493..8212fc1 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -13,9 +13,14 @@
  * limitations under the License.
  */
 
+#include <thread>
+
 #include "utils.h"
 #include "vibrator.h"
 
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
 namespace android {
 namespace idlcli {
 
@@ -26,16 +31,28 @@
 class CommandOn : public Command {
     std::string getDescription() const override { return "Turn on vibrator."; }
 
-    std::string getUsageSummary() const override { return "<duration>"; }
+    std::string getUsageSummary() const override { return "[options] <duration>"; }
 
     UsageDetails getUsageDetails() const override {
         UsageDetails details{
+                {"-b", {"Block for duration of vibration."}},
                 {"<duration>", {"In milliseconds."}},
         };
         return details;
     }
 
     Status doArgs(Args &args) override {
+        while (args.get<std::string>().value_or("").find("-") == 0) {
+            auto opt = *args.pop<std::string>();
+            if (opt == "--") {
+                break;
+            } else if (opt == "-b") {
+                mBlocking = true;
+            } else {
+                std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+                return USAGE;
+            }
+        }
         if (auto duration = args.pop<decltype(mDuration)>()) {
             mDuration = *duration;
         } else {
@@ -52,9 +69,21 @@
     Status doMain(Args && /*args*/) override {
         std::string statusStr;
         Status ret;
+        std::shared_ptr<VibratorCallback> callback;
 
         if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
+            ABinderProcess_setThreadPoolMaxThreadCount(1);
+            ABinderProcess_startThreadPool();
+
+            int32_t cap;
+            hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+            if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) {
+                callback = ndk::SharedRefBase::make<VibratorCallback>();
+            }
+
+            auto status = hal->call(&aidl::IVibrator::on, mDuration, callback);
+
             statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else if (auto hal = getHal<V1_0::IVibrator>()) {
@@ -65,11 +94,20 @@
             return UNAVAILABLE;
         }
 
+        if (ret == OK && mBlocking) {
+            if (callback) {
+                callback->waitForComplete();
+            } else {
+                sleep_for(milliseconds(mDuration));
+            }
+        }
+
         std::cout << "Status: " << statusStr << std::endl;
 
         return ret;
     }
 
+    bool mBlocking;
     uint32_t mDuration;
 };
 
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 69c7e37..4225263 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -13,9 +13,14 @@
  * limitations under the License.
  */
 
+#include <thread>
+
 #include "utils.h"
 #include "vibrator.h"
 
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
 namespace android {
 namespace idlcli {
 
@@ -57,10 +62,11 @@
 class CommandPerform : public Command {
     std::string getDescription() const override { return "Perform vibration effect."; }
 
-    std::string getUsageSummary() const override { return "<effect> <strength>"; }
+    std::string getUsageSummary() const override { return "[options] <effect> <strength>"; }
 
     UsageDetails getUsageDetails() const override {
         UsageDetails details{
+                {"-b", {"Block for duration of vibration."}},
                 {"<effect>", {"Effect ID."}},
                 {"<strength>", {"0-2."}},
         };
@@ -68,6 +74,17 @@
     }
 
     Status doArgs(Args &args) override {
+        while (args.get<std::string>().value_or("").find("-") == 0) {
+            auto opt = *args.pop<std::string>();
+            if (opt == "--") {
+                break;
+            } else if (opt == "-b") {
+                mBlocking = true;
+            } else {
+                std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+                return USAGE;
+            }
+        }
         if (auto effect = args.pop<decltype(mEffect)>()) {
             mEffect = *effect;
             std::cout << "Effect: " << toString(mEffect) << std::endl;
@@ -93,12 +110,24 @@
         std::string statusStr;
         uint32_t lengthMs;
         Status ret;
+        std::shared_ptr<VibratorCallback> callback;
 
         if (auto hal = getHal<aidl::IVibrator>()) {
+            ABinderProcess_setThreadPoolMaxThreadCount(1);
+            ABinderProcess_startThreadPool();
+
+            int32_t cap;
+            hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+            if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) {
+                callback = ndk::SharedRefBase::make<VibratorCallback>();
+            }
+
             int32_t aidlLengthMs;
-            auto status =
-                    hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
-                              static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
+            auto status = hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
+                                    static_cast<aidl::EffectStrength>(mStrength), callback,
+                                    &aidlLengthMs);
+
             statusStr = status.getDescription();
             lengthMs = static_cast<uint32_t>(aidlLengthMs);
             ret = status.isOk() ? OK : ERROR;
@@ -130,12 +159,21 @@
             ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR;
         }
 
+        if (ret == OK && mBlocking) {
+            if (callback) {
+                callback->waitForComplete();
+            } else {
+                sleep_for(milliseconds(lengthMs));
+            }
+        }
+
         std::cout << "Status: " << statusStr << std::endl;
         std::cout << "Length: " << lengthMs << std::endl;
 
         return ret;
     }
 
+    bool mBlocking;
     Effect mEffect;
     EffectStrength mStrength;
 };