Vibrator Service: Allow HAL Version Checking

Broke up the halCall() API into two components, one that implements the
initial service retrieval and one that implements the retry on
connection loss. This allows the service retrival API to double as test
for supported version with little overhead.

Test: Manually via CLI
Change-Id: I12cf8838e933187d473157c9576d1b13b03913d4
Signed-off-by: Harpreet \"Eli\" Sangha <eliptus@google.com>
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index d5fbd2b..7aa9c7c 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -56,37 +56,57 @@
     return Return<R>{Status::fromExceptionCode(Status::EX_NULL_POINTER)};
 }
 
-// Helper used to transparently deal with the vibrator HAL becoming unavailable.
+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;
+    }
+
+    // Helper used to transparently deal with the vibrator HAL becoming unavailable.
+    template<class R, class... Args0, class... Args1>
+    Return<R> call(Return<R> (I::* fn)(Args0...), Args1&&... args1) {
+        // Return<R> doesn't have a default constructor, so make a Return<R> with
+        // STATUS::EX_NONE.
+        using ::android::hardware::Status;
+        Return<R> ret{Status::fromExceptionCode(Status::EX_NONE)};
+
+        // Note that ret is guaranteed to be changed after this loop.
+        for (int i = 0; i < NUM_TRIES; ++i) {
+            ret = (mHal == nullptr) ? NullptrStatus<R>()
+                    : (*mHal.*fn)(std::forward<Args1>(args1)...);
+
+            if (ret.isOk()) {
+                break;
+            }
+
+            ALOGE("Failed to issue command to vibrator HAL. Retrying.");
+            // Restoring connection to the HAL.
+            mHal = I::tryGetService();
+        }
+        return ret;
+    }
+
+  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) {
-    // Assume that if getService returns a nullptr, HAL is not available on the
-    // device.
-    static sp<I> sHal = I::getService();
-    static bool sAvailable = sHal != nullptr;
-
-    if (!sAvailable) {
-        return NullptrStatus<R>();
-    }
-
-    // Return<R> doesn't have a default constructor, so make a Return<R> with
-    // STATUS::EX_NONE.
-    using ::android::hardware::Status;
-    Return<R> ret{Status::fromExceptionCode(Status::EX_NONE)};
-
-    // Note that ret is guaranteed to be changed after this loop.
-    for (int i = 0; i < NUM_TRIES; ++i) {
-        ret = (sHal == nullptr) ? NullptrStatus<R>()
-                : (*sHal.*fn)(std::forward<Args1>(args1)...);
-
-        if (ret.isOk()) {
-            break;
-        }
-
-        ALOGE("Failed to issue command to vibrator HAL. Retrying.");
-        // Restoring connection to the HAL.
-        sHal = I::tryGetService();
-    }
-    return ret;
+    auto hal = getHal<I>();
+    return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
 }
 
 template<class R>