Merge "Warm cache with additional shaders for render engine" into sc-dev
diff --git a/libs/binder/Debug.h b/libs/binder/Debug.h
index 68d811b..7ca087e 100644
--- a/libs/binder/Debug.h
+++ b/libs/binder/Debug.h
@@ -37,8 +37,7 @@
     size_t alignment=0, bool cArrayStyle=false,
     debugPrintFunc func = nullptr, void* cookie = nullptr);
 
-
-ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf);
+extern "C" ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf);
 
 // ---------------------------------------------------------------------------
 } // namespace android
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 7490b88..b58d919 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -43,6 +43,9 @@
       "name": "aidl_integration_test"
     },
     {
+      "name": "memunreachable_binder_test"
+    },
+    {
       "name": "libbinderthreadstateutils_test"
     },
     {
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index a44c261..8941e49 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -36,11 +36,6 @@
 
 __BEGIN_DECLS
 
-#ifndef __ANDROID_API__
-#error Android builds must be compiled against a specific API. If this is an \
- android platform host build, you must use libbinder_ndk_host_user.
-#endif
-
 typedef uint32_t binder_flags_t;
 enum {
     /**
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 05b25e7..b4dc08a 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -32,6 +32,16 @@
 
 __BEGIN_DECLS
 
+#ifndef __ANDROID_API__
+#error Android builds must be compiled against a specific API. If this is an \
+ android platform host build, you must use libbinder_ndk_host_user.
+#endif
+
+/**
+ * Low-level status types for use in binder. This is the least preferable way to
+ * return an error for binder services (where binder_exception_t should be used,
+ * particularly EX_SERVICE_SPECIFIC).
+ */
 enum {
     STATUS_OK = 0,
 
@@ -62,6 +72,10 @@
  */
 typedef int32_t binder_status_t;
 
+/**
+ * Top level exceptions types for Android binder errors, mapping to Java
+ * exceptions. Also see Parcel.java.
+ */
 enum {
     EX_NONE = 0,
     EX_SECURITY = -1,
@@ -170,7 +184,8 @@
 /**
  * New status with binder_status_t. This is typically for low level failures when a binder_status_t
  * is returned by an API on AIBinder or AParcel, and that is to be returned from a method returning
- * an AStatus instance.
+ * an AStatus instance. This is the least preferable way to return errors.
+ * Prefer exceptions (particularly service-specific errors) when possible.
  *
  * Available since API level 29.
  *
diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
index d54c1a1..6372449 100644
--- a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
@@ -20,9 +20,7 @@
 
 __BEGIN_DECLS
 
-#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
-#error this is only for platform code
-#endif
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
 
 /**
  * Gets whether or not FDs are allowed by this AParcel
@@ -33,6 +31,9 @@
  */
 bool AParcel_getAllowFds(const AParcel*);
 
+#endif
+
+#if !defined(__ANDROID_APEX__)
 /**
  * Data written to the parcel will be zero'd before being deleted or realloced.
  *
@@ -43,5 +44,6 @@
  * \param parcel The parcel to clear associated data from.
  */
 void AParcel_markSensitive(const AParcel* parcel);
+#endif
 
 __END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 8d08275..f1db653 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -117,6 +117,7 @@
     ABinderProcess_setupPolling; # apex
     AIBinder_getCallingSid; # apex
     AIBinder_setRequestingSid; # apex
+    AParcel_markSensitive; # llndk
     AServiceManager_isDeclared; # apex llndk
     AServiceManager_forEachDeclaredInstance; # apex llndk
     AServiceManager_registerLazyService; # llndk
@@ -139,7 +140,6 @@
 LIBBINDER_NDK_PLATFORM {
   global:
     AParcel_getAllowFds;
-    AParcel_markSensitive;
     extern "C++" {
         AIBinder_fromPlatformBinder*;
         AIBinder_toPlatformBinder*;
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ceab6ec..97f8f47 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -477,6 +477,26 @@
         return reply.readInt32();
     }
 
+    virtual status_t overrideHdrTypes(const sp<IBinder>& display,
+                                      const std::vector<ui::Hdr>& hdrTypes) {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, display);
+
+        std::vector<int32_t> hdrTypesVector;
+        for (ui::Hdr i : hdrTypes) {
+            hdrTypesVector.push_back(static_cast<int32_t>(i));
+        }
+        SAFE_PARCEL(data.writeInt32Vector, hdrTypesVector);
+
+        status_t result = remote()->transact(BnSurfaceComposer::OVERRIDE_HDR_TYPES, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("overrideHdrTypes failed to transact: %d", result);
+            return result;
+        }
+        return result;
+    }
+
     status_t enableVSyncInjections(bool enable) override {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1963,6 +1983,20 @@
             SAFE_PARCEL(reply->writeInt32, extraBuffers);
             return NO_ERROR;
         }
+        case OVERRIDE_HDR_TYPES: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            SAFE_PARCEL(data.readStrongBinder, &display);
+
+            std::vector<int32_t> hdrTypes;
+            SAFE_PARCEL(data.readInt32Vector, &hdrTypes);
+
+            std::vector<ui::Hdr> hdrTypesVector;
+            for (int i : hdrTypes) {
+                hdrTypesVector.push_back(static_cast<ui::Hdr>(i));
+            }
+            return overrideHdrTypes(display, hdrTypesVector);
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index d7b2c2e..0b01084 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1985,6 +1985,11 @@
     return ComposerService::getComposerService()->getAnimationFrameStats(outStats);
 }
 
+status_t SurfaceComposerClient::overrideHdrTypes(const sp<IBinder>& display,
+                                                 const std::vector<ui::Hdr>& hdrTypes) {
+    return ComposerService::getComposerService()->overrideHdrTypes(display, hdrTypes);
+}
+
 status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
                                                                       ui::PixelFormat* outFormat,
                                                                       ui::Dataspace* outDataspace,
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 35990d1..9f9ca74 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -267,6 +267,13 @@
      */
     virtual status_t getAnimationFrameStats(FrameStats* outStats) const = 0;
 
+    /* Overrides the supported HDR modes for the given display device.
+     *
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t overrideHdrTypes(const sp<IBinder>& display,
+                                      const std::vector<ui::Hdr>& hdrTypes) = 0;
+
     virtual status_t enableVSyncInjections(bool enable) = 0;
 
     virtual status_t injectVSync(nsecs_t when) = 0;
@@ -570,6 +577,7 @@
         GET_DYNAMIC_DISPLAY_INFO,
         ADD_FPS_LISTENER,
         REMOVE_FPS_LISTENER,
+        OVERRIDE_HDR_TYPES,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index a643e9e..c38375c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -572,6 +572,9 @@
     static status_t clearAnimationFrameStats();
     static status_t getAnimationFrameStats(FrameStats* outStats);
 
+    static status_t overrideHdrTypes(const sp<IBinder>& display,
+                                     const std::vector<ui::Hdr>& hdrTypes);
+
     static void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation,
                                      const Rect& layerStackRect, const Rect& displayRect);
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 8d7f8c9..5ac3f19 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -772,6 +772,10 @@
     status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
         return NO_ERROR;
     }
+    status_t overrideHdrTypes(const sp<IBinder>& /*display*/,
+                              const std::vector<ui::Hdr>& /*hdrTypes*/) override {
+        return NO_ERROR;
+    }
     status_t enableVSyncInjections(bool /*enable*/) override {
         return NO_ERROR;
     }
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index f4a2a3f..b7b2cc6 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -354,8 +354,20 @@
     return mCompositionDisplay->getDisplayColorProfile()->getSupportedPerFrameMetadata();
 }
 
-const HdrCapabilities& DisplayDevice::getHdrCapabilities() const {
-    return mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities();
+void DisplayDevice::overrideHdrTypes(const std::vector<ui::Hdr>& hdrTypes) {
+    mOverrideHdrTypes = hdrTypes;
+}
+
+HdrCapabilities DisplayDevice::getHdrCapabilities() const {
+    const HdrCapabilities& capabilities =
+            mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities();
+    std::vector<ui::Hdr> hdrTypes = capabilities.getSupportedHdrTypes();
+    if (!mOverrideHdrTypes.empty()) {
+        hdrTypes = mOverrideHdrTypes;
+    }
+    return HdrCapabilities(hdrTypes, capabilities.getDesiredMaxLuminance(),
+                           capabilities.getDesiredMaxAverageLuminance(),
+                           capabilities.getDesiredMinLuminance());
 }
 
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 7156613..68846d3 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -126,13 +126,15 @@
     bool hasHLGSupport() const;
     bool hasDolbyVisionSupport() const;
 
+    void overrideHdrTypes(const std::vector<ui::Hdr>& hdrTypes);
+
     // The returned HdrCapabilities is the combination of HDR capabilities from
     // hardware composer and RenderEngine. When the DisplayDevice supports wide
     // color gamut, RenderEngine is able to simulate HDR support in Display P3
     // color space for both PQ and HLG HDR contents. The minimum and maximum
     // luminance will be set to sDefaultMinLumiance and sDefaultMaxLumiance
     // respectively if hardware composer doesn't return meaningful values.
-    const HdrCapabilities& getHdrCapabilities() const;
+    HdrCapabilities getHdrCapabilities() const;
 
     // Return true if intent is supported by the display.
     bool hasRenderIntent(ui::RenderIntent intent) const;
@@ -217,6 +219,8 @@
     const bool mIsPrimary;
 
     std::optional<DeviceProductInfo> mDeviceProductInfo;
+
+    std::vector<ui::Hdr> mOverrideHdrTypes;
 };
 
 struct DisplayDeviceState {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a0dbf6f..525b043 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1287,6 +1287,21 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken,
+                                          const std::vector<ui::Hdr>& hdrTypes) {
+    Mutex::Autolock lock(mStateLock);
+
+    auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+        return NAME_NOT_FOUND;
+    }
+
+    display->overrideHdrTypes(hdrTypes);
+    dispatchDisplayHotplugEvent(display->getPhysicalId(), true /* connected */);
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
                                                                ui::PixelFormat* outFormat,
                                                                ui::Dataspace* outDataspace,
@@ -5014,6 +5029,7 @@
         case DESTROY_DISPLAY:
         case ENABLE_VSYNC_INJECTIONS:
         case GET_ANIMATION_FRAME_STATS:
+        case OVERRIDE_HDR_TYPES:
         case GET_HDR_CAPABILITIES:
         case SET_DESIRED_DISPLAY_MODE_SPECS:
         case GET_DESIRED_DISPLAY_MODE_SPECS:
@@ -5030,9 +5046,11 @@
         case NOTIFY_POWER_BOOST:
         case SET_GLOBAL_SHADOW_SETTINGS:
         case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
-            // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
-            // necessary permission dynamically. Don't use the permission cache for this check.
-            bool usePermissionCache = code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN;
+            // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests,
+            // which acquire the necessary permission dynamically. Don't use the permission cache
+            // for this check.
+            bool usePermissionCache =
+                    code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN && code != OVERRIDE_HDR_TYPES;
             if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
                 IPCThreadState* ipc = IPCThreadState::self();
                 ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8d2f66d..e4ff6c9 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -642,6 +642,8 @@
     void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
     status_t clearAnimationFrameStats() override;
     status_t getAnimationFrameStats(FrameStats* outStats) const override;
+    status_t overrideHdrTypes(const sp<IBinder>& displayToken,
+                              const std::vector<ui::Hdr>& hdrTypes) override;
     status_t enableVSyncInjections(bool enable) override;
     status_t injectVSync(nsecs_t when) override;
     status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override;
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 70f9876..c1795f5 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -87,45 +87,6 @@
 
 // -------------------------------------------------------------------------------------------------
 
-static constexpr int MAX_RETRIES = 1;
-
-template <typename T>
-HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) {
-    if (result.isFailed()) {
-        ALOGE("%s failed: %s", functionName, result.errorMessage());
-        std::lock_guard<std::mutex> lock(mConnectedHalMutex);
-        mConnectedHal->tryReconnect();
-    }
-    return result;
-}
-
-template <typename T>
-HalResult<T> HalController::apply(HalController::hal_fn<T>& halFn, const char* functionName) {
-    std::shared_ptr<HalWrapper> hal = nullptr;
-    {
-        std::lock_guard<std::mutex> lock(mConnectedHalMutex);
-        if (mConnectedHal == nullptr) {
-            // Init was never called, so connect to HAL for the first time during this call.
-            mConnectedHal = mConnector(mCallbackScheduler);
-
-            if (mConnectedHal == nullptr) {
-                ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
-                return HalResult<T>::unsupported();
-            }
-        }
-        hal = mConnectedHal;
-    }
-
-    HalResult<T> ret = processHalResult(halFn(hal), functionName);
-    for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) {
-        ret = processHalResult(halFn(hal), functionName);
-    }
-
-    return ret;
-}
-
-// -------------------------------------------------------------------------------------------------
-
 bool HalController::init() {
     std::lock_guard<std::mutex> lock(mConnectedHalMutex);
     if (mConnectedHal == nullptr) {
@@ -134,11 +95,6 @@
     return mConnectedHal != nullptr;
 }
 
-HalResult<void> HalController::ping() {
-    hal_fn<void> pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
-    return apply(pingFn, "ping");
-}
-
 void HalController::tryReconnect() {
     std::lock_guard<std::mutex> lock(mConnectedHalMutex);
     if (mConnectedHal == nullptr) {
@@ -148,96 +104,6 @@
     }
 }
 
-HalResult<void> HalController::on(milliseconds timeout,
-                                  const std::function<void()>& completionCallback) {
-    hal_fn<void> onFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->on(timeout, completionCallback);
-    };
-    return apply(onFn, "on");
-}
-
-HalResult<void> HalController::off() {
-    hal_fn<void> offFn = [](std::shared_ptr<HalWrapper> hal) { return hal->off(); };
-    return apply(offFn, "off");
-}
-
-HalResult<void> HalController::setAmplitude(float amplitude) {
-    hal_fn<void> setAmplitudeFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->setAmplitude(amplitude);
-    };
-    return apply(setAmplitudeFn, "setAmplitude");
-}
-
-HalResult<void> HalController::setExternalControl(bool enabled) {
-    hal_fn<void> setExternalControlFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->setExternalControl(enabled);
-    };
-    return apply(setExternalControlFn, "setExternalControl");
-}
-
-HalResult<void> HalController::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
-    hal_fn<void> alwaysOnEnableFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->alwaysOnEnable(id, effect, strength);
-    };
-    return apply(alwaysOnEnableFn, "alwaysOnEnable");
-}
-
-HalResult<void> HalController::alwaysOnDisable(int32_t id) {
-    hal_fn<void> alwaysOnDisableFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->alwaysOnDisable(id);
-    };
-    return apply(alwaysOnDisableFn, "alwaysOnDisable");
-}
-
-HalResult<Capabilities> HalController::getCapabilities() {
-    hal_fn<Capabilities> getCapabilitiesFn = [](std::shared_ptr<HalWrapper> hal) {
-        return hal->getCapabilities();
-    };
-    return apply(getCapabilitiesFn, "getCapabilities");
-}
-
-HalResult<std::vector<Effect>> HalController::getSupportedEffects() {
-    hal_fn<std::vector<Effect>> getSupportedEffectsFn = [](std::shared_ptr<HalWrapper> hal) {
-        return hal->getSupportedEffects();
-    };
-    return apply(getSupportedEffectsFn, "getSupportedEffects");
-}
-
-HalResult<std::vector<CompositePrimitive>> HalController::getSupportedPrimitives() {
-    hal_fn<std::vector<CompositePrimitive>> getSupportedPrimitivesFn =
-            [](std::shared_ptr<HalWrapper> hal) { return hal->getSupportedPrimitives(); };
-    return apply(getSupportedPrimitivesFn, "getSupportedPrimitives");
-}
-
-HalResult<float> HalController::getResonantFrequency() {
-    hal_fn<float> getResonantFrequencyFn = [](std::shared_ptr<HalWrapper> hal) {
-        return hal->getResonantFrequency();
-    };
-    return apply(getResonantFrequencyFn, "getResonantFrequency");
-}
-
-HalResult<float> HalController::getQFactor() {
-    hal_fn<float> getQFactorFn = [](std::shared_ptr<HalWrapper> hal) { return hal->getQFactor(); };
-    return apply(getQFactorFn, "getQFactor");
-}
-
-HalResult<milliseconds> HalController::performEffect(
-        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
-    hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->performEffect(effect, strength, completionCallback);
-    };
-    return apply(performEffectFn, "performEffect");
-}
-
-HalResult<milliseconds> HalController::performComposedEffect(
-        const std::vector<CompositeEffect>& primitiveEffects,
-        const std::function<void()>& completionCallback) {
-    hal_fn<milliseconds> performComposedEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
-        return hal->performComposedEffect(primitiveEffects, completionCallback);
-    };
-    return apply(performComposedEffectFn, "performComposedEffect");
-}
-
 }; // namespace vibrator
 
 }; // namespace android
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index baee74f..1010aa5 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -19,16 +19,19 @@
 #include <android/hardware/vibrator/1.3/IVibrator.h>
 #include <android/hardware/vibrator/IVibrator.h>
 #include <hardware/vibrator.h>
+#include <cmath>
 
 #include <utils/Log.h>
 
 #include <vibratorservice/VibratorCallbackScheduler.h>
 #include <vibratorservice/VibratorHalWrapper.h>
 
+using android::hardware::vibrator::Braking;
 using android::hardware::vibrator::CompositeEffect;
 using android::hardware::vibrator::CompositePrimitive;
 using android::hardware::vibrator::Effect;
 using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::PrimitivePwle;
 
 using std::chrono::milliseconds;
 
@@ -45,20 +48,6 @@
 // -------------------------------------------------------------------------------------------------
 
 template <class T>
-HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) {
-    if (cache.has_value()) {
-        // Return copy of cached value.
-        return HalResult<T>::ok(*cache);
-    }
-    HalResult<T> ret = loadFn();
-    if (ret.isOk()) {
-        // Cache copy of returned value.
-        cache.emplace(ret.value());
-    }
-    return ret;
-}
-
-template <class T>
 bool isStaticCastValid(Effect effect) {
     T castEffect = static_cast<T>(effect);
     auto iter = hardware::hidl_enum_range<T>();
@@ -133,6 +122,117 @@
 
 // -------------------------------------------------------------------------------------------------
 
+Info HalWrapper::getInfo() {
+    getCapabilities();
+    getPrimitiveDurations();
+    std::lock_guard<std::mutex> lock(mInfoMutex);
+    if (mInfoCache.mSupportedEffects.isFailed()) {
+        mInfoCache.mSupportedEffects = getSupportedEffectsInternal();
+    }
+    if (mInfoCache.mSupportedBraking.isFailed()) {
+        mInfoCache.mSupportedBraking = getSupportedBrakingInternal();
+    }
+    if (mInfoCache.mMinFrequency.isFailed()) {
+        mInfoCache.mMinFrequency = getMinFrequencyInternal();
+    }
+    if (mInfoCache.mResonantFrequency.isFailed()) {
+        mInfoCache.mResonantFrequency = getResonantFrequencyInternal();
+    }
+    if (mInfoCache.mFrequencyResolution.isFailed()) {
+        mInfoCache.mFrequencyResolution = getFrequencyResolutionInternal();
+    }
+    if (mInfoCache.mQFactor.isFailed()) {
+        mInfoCache.mQFactor = getQFactorInternal();
+    }
+    if (mInfoCache.mMaxAmplitudes.isFailed()) {
+        mInfoCache.mMaxAmplitudes = getMaxAmplitudesInternal();
+    }
+    return mInfoCache.get();
+}
+
+HalResult<milliseconds> HalWrapper::performComposedEffect(const std::vector<CompositeEffect>&,
+                                                          const std::function<void()>&) {
+    ALOGV("Skipped performComposedEffect because it's not available in Vibrator HAL");
+    return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<void> HalWrapper::performPwleEffect(const std::vector<PrimitivePwle>&,
+                                              const std::function<void()>&) {
+    ALOGV("Skipped performPwleEffect because it's not available in Vibrator HAL");
+    return HalResult<void>::unsupported();
+}
+
+HalResult<Capabilities> HalWrapper::getCapabilities() {
+    std::lock_guard<std::mutex> lock(mInfoMutex);
+    if (mInfoCache.mCapabilities.isFailed()) {
+        mInfoCache.mCapabilities = getCapabilitiesInternal();
+    }
+    return mInfoCache.mCapabilities;
+}
+
+HalResult<std::vector<milliseconds>> HalWrapper::getPrimitiveDurations() {
+    std::lock_guard<std::mutex> lock(mInfoMutex);
+    if (mInfoCache.mSupportedPrimitives.isFailed()) {
+        mInfoCache.mSupportedPrimitives = getSupportedPrimitivesInternal();
+        if (mInfoCache.mSupportedPrimitives.isUnsupported()) {
+            mInfoCache.mPrimitiveDurations = HalResult<std::vector<milliseconds>>::unsupported();
+        }
+    }
+    if (mInfoCache.mPrimitiveDurations.isFailed() && mInfoCache.mSupportedPrimitives.isOk()) {
+        mInfoCache.mPrimitiveDurations =
+                getPrimitiveDurationsInternal(mInfoCache.mSupportedPrimitives.value());
+    }
+    return mInfoCache.mPrimitiveDurations;
+}
+
+HalResult<std::vector<Effect>> HalWrapper::getSupportedEffectsInternal() {
+    ALOGV("Skipped getSupportedEffects because it's not available in Vibrator HAL");
+    return HalResult<std::vector<Effect>>::unsupported();
+}
+
+HalResult<std::vector<Braking>> HalWrapper::getSupportedBrakingInternal() {
+    ALOGV("Skipped getSupportedBraking because it's not available in Vibrator HAL");
+    return HalResult<std::vector<Braking>>::unsupported();
+}
+
+HalResult<std::vector<CompositePrimitive>> HalWrapper::getSupportedPrimitivesInternal() {
+    ALOGV("Skipped getSupportedPrimitives because it's not available in Vibrator HAL");
+    return HalResult<std::vector<CompositePrimitive>>::unsupported();
+}
+
+HalResult<std::vector<milliseconds>> HalWrapper::getPrimitiveDurationsInternal(
+        const std::vector<CompositePrimitive>&) {
+    ALOGV("Skipped getPrimitiveDurations because it's not available in Vibrator HAL");
+    return HalResult<std::vector<milliseconds>>::unsupported();
+}
+
+HalResult<float> HalWrapper::getMinFrequencyInternal() {
+    ALOGV("Skipped getMinFrequency because it's not available in Vibrator HAL");
+    return HalResult<float>::unsupported();
+}
+
+HalResult<float> HalWrapper::getResonantFrequencyInternal() {
+    ALOGV("Skipped getResonantFrequency because it's not available in Vibrator HAL");
+    return HalResult<float>::unsupported();
+}
+
+HalResult<float> HalWrapper::getFrequencyResolutionInternal() {
+    ALOGV("Skipped getFrequencyResolution because it's not available in Vibrator HAL");
+    return HalResult<float>::unsupported();
+}
+
+HalResult<float> HalWrapper::getQFactorInternal() {
+    ALOGV("Skipped getQFactor because it's not available in Vibrator HAL");
+    return HalResult<float>::unsupported();
+}
+
+HalResult<std::vector<float>> HalWrapper::getMaxAmplitudesInternal() {
+    ALOGV("Skipped getMaxAmplitudes because it's not available in Vibrator HAL");
+    return HalResult<std::vector<float>>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
 HalResult<void> AidlHalWrapper::ping() {
     return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder());
 }
@@ -184,37 +284,6 @@
     return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id));
 }
 
-HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
-    std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
-    return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this),
-                                    mCapabilities);
-}
-
-HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() {
-    std::lock_guard<std::mutex> lock(mSupportedEffectsMutex);
-    return loadCached<std::vector<Effect>>(std::bind(&AidlHalWrapper::getSupportedEffectsInternal,
-                                                     this),
-                                           mSupportedEffects);
-}
-
-HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitives() {
-    std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex);
-    return loadCached<std::vector<
-            CompositePrimitive>>(std::bind(&AidlHalWrapper::getSupportedPrimitivesInternal, this),
-                                 mSupportedPrimitives);
-}
-
-HalResult<float> AidlHalWrapper::getResonantFrequency() {
-    std::lock_guard<std::mutex> lock(mResonantFrequencyMutex);
-    return loadCached<float>(std::bind(&AidlHalWrapper::getResonantFrequencyInternal, this),
-                             mResonantFrequency);
-}
-
-HalResult<float> AidlHalWrapper::getQFactor() {
-    std::lock_guard<std::mutex> lock(mQFactorMutex);
-    return loadCached<float>(std::bind(&AidlHalWrapper::getQFactorInternal, this), mQFactor);
-}
-
 HalResult<milliseconds> AidlHalWrapper::performEffect(
         Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
     HalResult<Capabilities> capabilities = getCapabilities();
@@ -235,44 +304,32 @@
 }
 
 HalResult<milliseconds> AidlHalWrapper::performComposedEffect(
-        const std::vector<CompositeEffect>& primitiveEffects,
+        const std::vector<CompositeEffect>& primitives,
         const std::function<void()>& completionCallback) {
     // This method should always support callbacks, so no need to double check.
     auto cb = new HalCallbackWrapper(completionCallback);
+
+    auto durations = getPrimitiveDurations().valueOr({});
     milliseconds duration(0);
-    for (const auto& effect : primitiveEffects) {
-        auto durationResult = getPrimitiveDuration(effect.primitive);
-        if (durationResult.isOk()) {
-            duration += durationResult.value();
+    for (const auto& effect : primitives) {
+        auto primitiveIdx = static_cast<size_t>(effect.primitive);
+        if (primitiveIdx < durations.size()) {
+            duration += durations[primitiveIdx];
+        } else {
+            // Make sure the returned duration is positive to indicate successful vibration.
+            duration += milliseconds(1);
         }
         duration += milliseconds(effect.delayMs);
     }
-    return HalResult<milliseconds>::fromStatus(getHal()->compose(primitiveEffects, cb), duration);
+
+    return HalResult<milliseconds>::fromStatus(getHal()->compose(primitives, cb), duration);
 }
 
-HalResult<milliseconds> AidlHalWrapper::getPrimitiveDuration(CompositePrimitive primitive) {
-    std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex);
-    if (mPrimitiveDurations.empty()) {
-        constexpr auto primitiveRange = enum_range<CompositePrimitive>();
-        constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
-        mPrimitiveDurations.resize(primitiveCount);
-    }
-    auto primitiveIdx = static_cast<size_t>(primitive);
-    if (primitiveIdx >= mPrimitiveDurations.size()) {
-        // Safety check, should not happen if enum_range is correct.
-        return HalResult<milliseconds>::unsupported();
-    }
-    auto& cache = mPrimitiveDurations[primitiveIdx];
-    if (cache.has_value()) {
-        return HalResult<milliseconds>::ok(*cache);
-    }
-    int32_t duration;
-    auto result = getHal()->getPrimitiveDuration(primitive, &duration);
-    if (result.isOk()) {
-        // Cache copy of returned value.
-        cache.emplace(duration);
-    }
-    return HalResult<milliseconds>::fromStatus(result, milliseconds(duration));
+HalResult<void> AidlHalWrapper::performPwleEffect(const std::vector<PrimitivePwle>& primitives,
+                                                  const std::function<void()>& completionCallback) {
+    // This method should always support callbacks, so no need to double check.
+    auto cb = new HalCallbackWrapper(completionCallback);
+    return HalResult<void>::fromStatus(getHal()->composePwle(primitives, cb));
 }
 
 HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
@@ -287,24 +344,72 @@
     return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
 }
 
+HalResult<std::vector<Braking>> AidlHalWrapper::getSupportedBrakingInternal() {
+    std::vector<Braking> supportedBraking;
+    auto result = getHal()->getSupportedBraking(&supportedBraking);
+    return HalResult<std::vector<Braking>>::fromStatus(result, supportedBraking);
+}
+
 HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() {
     std::vector<CompositePrimitive> supportedPrimitives;
     auto result = getHal()->getSupportedPrimitives(&supportedPrimitives);
     return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives);
 }
 
+HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsInternal(
+        const std::vector<CompositePrimitive>& supportedPrimitives) {
+    std::vector<milliseconds> durations;
+    constexpr auto primitiveRange = enum_range<CompositePrimitive>();
+    constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
+    durations.resize(primitiveCount);
+
+    for (auto primitive : supportedPrimitives) {
+        auto primitiveIdx = static_cast<size_t>(primitive);
+        if (primitiveIdx >= durations.size()) {
+            // Safety check, should not happen if enum_range is correct.
+            continue;
+        }
+        int32_t duration = 0;
+        auto status = getHal()->getPrimitiveDuration(primitive, &duration);
+        if (!status.isOk()) {
+            return HalResult<std::vector<milliseconds>>::failed(status.toString8().c_str());
+        }
+        durations[primitiveIdx] = milliseconds(duration);
+    }
+
+    return HalResult<std::vector<milliseconds>>::ok(durations);
+}
+
+HalResult<float> AidlHalWrapper::getMinFrequencyInternal() {
+    float minFrequency = 0;
+    auto result = getHal()->getFrequencyMinimum(&minFrequency);
+    return HalResult<float>::fromStatus(result, minFrequency);
+}
+
 HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() {
     float f0 = 0;
     auto result = getHal()->getResonantFrequency(&f0);
     return HalResult<float>::fromStatus(result, f0);
 }
 
+HalResult<float> AidlHalWrapper::getFrequencyResolutionInternal() {
+    float frequencyResolution = 0;
+    auto result = getHal()->getFrequencyResolution(&frequencyResolution);
+    return HalResult<float>::fromStatus(result, frequencyResolution);
+}
+
 HalResult<float> AidlHalWrapper::getQFactorInternal() {
     float qFactor = 0;
     auto result = getHal()->getQFactor(&qFactor);
     return HalResult<float>::fromStatus(result, qFactor);
 }
 
+HalResult<std::vector<float>> AidlHalWrapper::getMaxAmplitudesInternal() {
+    std::vector<float> amplitudes;
+    auto result = getHal()->getBandwidthAmplitudeMap(&amplitudes);
+    return HalResult<std::vector<float>>::fromStatus(result, amplitudes);
+}
+
 sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
     std::lock_guard<std::mutex> lock(mHandleMutex);
     return mHandle;
@@ -370,44 +475,6 @@
 }
 
 template <typename I>
-HalResult<Capabilities> HidlHalWrapper<I>::getCapabilities() {
-    std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
-    return loadCached<Capabilities>(std::bind(&HidlHalWrapper<I>::getCapabilitiesInternal, this),
-                                    mCapabilities);
-}
-
-template <typename I>
-HalResult<std::vector<Effect>> HidlHalWrapper<I>::getSupportedEffects() {
-    ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available");
-    return HalResult<std::vector<Effect>>::unsupported();
-}
-
-template <typename I>
-HalResult<std::vector<CompositePrimitive>> HidlHalWrapper<I>::getSupportedPrimitives() {
-    ALOGV("Skipped getSupportedPrimitives because Vibrator HAL AIDL is not available");
-    return HalResult<std::vector<CompositePrimitive>>::unsupported();
-}
-
-template <typename I>
-HalResult<float> HidlHalWrapper<I>::getResonantFrequency() {
-    ALOGV("Skipped getResonantFrequency because Vibrator HAL AIDL is not available");
-    return HalResult<float>::unsupported();
-}
-
-template <typename I>
-HalResult<float> HidlHalWrapper<I>::getQFactor() {
-    ALOGV("Skipped getQFactor because Vibrator HAL AIDL is not available");
-    return HalResult<float>::unsupported();
-}
-
-template <typename I>
-HalResult<std::chrono::milliseconds> HidlHalWrapper<I>::performComposedEffect(
-        const std::vector<CompositeEffect>&, const std::function<void()>&) {
-    ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
-    return HalResult<std::chrono::milliseconds>::unsupported();
-}
-
-template <typename I>
 HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() {
     hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
     Capabilities capabilities =
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index 8a08e5b..a9d499d 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -30,7 +30,8 @@
 const constexpr char* MISSING_VIBRATOR_MESSAGE_PREFIX = "No vibrator with id=";
 
 HalResult<void> LegacyManagerHalWrapper::ping() {
-    return mController->ping();
+    auto pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
+    return mController->doWithRetry<void>(pingFn, "ping");
 }
 
 void LegacyManagerHalWrapper::tryReconnect() {
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
index f40980c..53f3daf 100644
--- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -20,6 +20,10 @@
 #include <vibratorservice/VibratorHalController.h>
 
 using ::android::enum_range;
+using ::android::hardware::vibrator::CompositeEffect;
+using ::android::hardware::vibrator::CompositePrimitive;
+using ::android::hardware::vibrator::Effect;
+using ::android::hardware::vibrator::EffectStrength;
 using ::benchmark::Counter;
 using ::benchmark::Fixture;
 using ::benchmark::kMicrosecond;
@@ -33,7 +37,7 @@
 public:
     void SetUp(State& /*state*/) override { mController.init(); }
 
-    void TearDown(State& /*state*/) override { mController.off(); }
+    void TearDown(State& state) override { turnVibratorOff(state); }
 
     static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
 
@@ -46,8 +50,8 @@
 
     auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
 
-    bool hasCapabilities(const vibrator::HalResult<vibrator::Capabilities>& result,
-                         vibrator::Capabilities&& query, State& state) {
+    bool hasCapabilities(vibrator::Capabilities&& query, State& state) {
+        auto result = mController.getInfo().capabilities;
         if (result.isFailed()) {
             state.SkipWithError(result.errorMessage());
             return false;
@@ -58,6 +62,10 @@
         return (result.value() & query) == query;
     }
 
+    void turnVibratorOff(State& state) {
+        checkHalResult(halCall<void>(mController, [](auto hal) { return hal->off(); }), state);
+    }
+
     template <class R>
     bool checkHalResult(const vibrator::HalResult<R>& result, State& state) {
         if (result.isFailed()) {
@@ -66,6 +74,12 @@
         }
         return true;
     }
+
+    template <class R>
+    vibrator::HalResult<R> halCall(vibrator::HalController& controller,
+                                   const vibrator::HalFunction<vibrator::HalResult<R>>& halFn) {
+        return controller.doWithRetry<R>(halFn, "benchmark");
+    }
 };
 
 #define BENCHMARK_WRAPPER(fixt, test, code)                \
@@ -93,7 +107,7 @@
 BENCHMARK_WRAPPER(VibratorBench, ping, {
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.ping();
+        auto ret = halCall<void>(mController, [](auto hal) { return hal->ping(); });
         state.PauseTiming();
         checkHalResult(ret, state);
     }
@@ -111,10 +125,11 @@
 
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.on(duration, callback);
+        auto ret =
+                halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(mController.off(), state);
+            turnVibratorOff(state);
         }
     }
 });
@@ -125,18 +140,18 @@
 
     for (auto _ : state) {
         state.PauseTiming();
-        if (!checkHalResult(mController.on(duration, callback), state)) {
+        auto ret =
+                halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
+        if (!checkHalResult(ret, state)) {
             continue;
         }
         state.ResumeTiming();
-        checkHalResult(mController.off(), state);
+        turnVibratorOff(state);
     }
 });
 
 BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
         return;
     }
 
@@ -148,22 +163,23 @@
         state.PauseTiming();
         vibrator::HalController controller;
         controller.init();
-        if (!checkHalResult(controller.on(duration, callback), state)) {
+        auto result =
+                halCall<void>(controller, [&](auto hal) { return hal->on(duration, callback); });
+        if (!checkHalResult(result, state)) {
             continue;
         }
         state.ResumeTiming();
-        auto ret = controller.setAmplitude(amplitude);
+        auto ret =
+                halCall<void>(controller, [&](auto hal) { return hal->setAmplitude(amplitude); });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(controller.off(), state);
+            turnVibratorOff(state);
         }
     }
 });
 
 BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
         return;
     }
 
@@ -171,19 +187,19 @@
     auto callback = []() {};
     auto amplitude = 1.0f;
 
-    checkHalResult(mController.on(duration, callback), state);
+    auto onResult =
+            halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
+    checkHalResult(onResult, state);
 
     for (auto _ : state) {
-        checkHalResult(mController.setAmplitude(amplitude), state);
+        auto ret =
+                halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
+        checkHalResult(ret, state);
     }
-
-    checkHalResult(mController.off(), state);
 });
 
 BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
         return;
     }
 
@@ -192,103 +208,85 @@
         vibrator::HalController controller;
         controller.init();
         state.ResumeTiming();
-        auto ret = controller.setExternalControl(true);
+        auto ret =
+                halCall<void>(controller, [](auto hal) { return hal->setExternalControl(true); });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(controller.setExternalControl(false), state);
+            auto result = halCall<void>(controller,
+                                        [](auto hal) { return hal->setExternalControl(false); });
+            checkHalResult(result, state);
         }
     }
 });
 
 BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
         return;
     }
 
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.setExternalControl(true);
+        auto result =
+                halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
         state.PauseTiming();
-        if (checkHalResult(ret, state)) {
-            checkHalResult(mController.setExternalControl(false), state);
+        if (checkHalResult(result, state)) {
+            auto ret = halCall<void>(mController,
+                                     [](auto hal) { return hal->setExternalControl(false); });
+            checkHalResult(ret, state);
         }
     }
 });
 
 BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) {
         return;
     }
 
     auto amplitude = 1.0f;
 
-    checkHalResult(mController.setExternalControl(true), state);
+    auto onResult =
+            halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
+    checkHalResult(onResult, state);
 
     for (auto _ : state) {
-        checkHalResult(mController.setAmplitude(amplitude), state);
+        auto ret =
+                halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
+        checkHalResult(ret, state);
     }
 
-    checkHalResult(mController.setExternalControl(false), state);
+    auto offResult =
+            halCall<void>(mController, [](auto hal) { return hal->setExternalControl(false); });
+    checkHalResult(offResult, state);
 });
 
-BENCHMARK_WRAPPER(VibratorBench, getCapabilities, {
+BENCHMARK_WRAPPER(VibratorBench, getInfo, {
     for (auto _ : state) {
         state.PauseTiming();
         vibrator::HalController controller;
         controller.init();
         state.ResumeTiming();
-        checkHalResult(controller.getCapabilities(), state);
+        auto result = controller.getInfo();
+        checkHalResult(result.capabilities, state);
+        checkHalResult(result.supportedEffects, state);
+        checkHalResult(result.supportedPrimitives, state);
+        checkHalResult(result.primitiveDurations, state);
+        checkHalResult(result.resonantFrequency, state);
+        checkHalResult(result.qFactor, state);
     }
 });
 
-BENCHMARK_WRAPPER(VibratorBench, getCapabilitiesCached, {
+BENCHMARK_WRAPPER(VibratorBench, getInfoCached, {
     // First call to cache values.
-    checkHalResult(mController.getCapabilities(), state);
+    mController.getInfo();
 
     for (auto _ : state) {
-        checkHalResult(mController.getCapabilities(), state);
-    }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedEffects, {
-    for (auto _ : state) {
-        state.PauseTiming();
-        vibrator::HalController controller;
-        controller.init();
-        state.ResumeTiming();
-        checkHalResult(controller.getSupportedEffects(), state);
-    }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedEffectsCached, {
-    // First call to cache values.
-    checkHalResult(mController.getSupportedEffects(), state);
-
-    for (auto _ : state) {
-        checkHalResult(mController.getSupportedEffects(), state);
-    }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitives, {
-    for (auto _ : state) {
-        state.PauseTiming();
-        vibrator::HalController controller;
-        controller.init();
-        state.ResumeTiming();
-        checkHalResult(controller.getSupportedPrimitives(), state);
-    }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitivesCached, {
-    // First call to cache values.
-    checkHalResult(mController.getSupportedPrimitives(), state);
-
-    for (auto _ : state) {
-        checkHalResult(mController.getSupportedPrimitives(), state);
+        auto result = mController.getInfo();
+        checkHalResult(result.capabilities, state);
+        checkHalResult(result.supportedEffects, state);
+        checkHalResult(result.supportedPrimitives, state);
+        checkHalResult(result.primitiveDurations, state);
+        checkHalResult(result.resonantFrequency, state);
+        checkHalResult(result.qFactor, state);
     }
 });
 
@@ -296,12 +294,12 @@
 public:
     static void DefaultArgs(Benchmark* b) {
         vibrator::HalController controller;
-        auto effectsResult = controller.getSupportedEffects();
+        auto effectsResult = controller.getInfo().supportedEffects;
         if (!effectsResult.isOk()) {
             return;
         }
 
-        std::vector<hardware::vibrator::Effect> supported = effectsResult.value();
+        std::vector<Effect> supported = effectsResult.value();
         b->ArgNames({"Effect", "Strength"});
 
         if (supported.empty()) {
@@ -309,11 +307,11 @@
             return;
         }
 
-        for (const auto& effect : enum_range<hardware::vibrator::Effect>()) {
+        for (const auto& effect : enum_range<Effect>()) {
             if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
                 continue;
             }
-            for (const auto& strength : enum_range<hardware::vibrator::EffectStrength>()) {
+            for (const auto& strength : enum_range<EffectStrength>()) {
                 b->Args({static_cast<long>(effect), static_cast<long>(strength)});
             }
         }
@@ -323,18 +321,16 @@
     bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
 
     auto getEffect(const State& state) const {
-        return static_cast<hardware::vibrator::Effect>(this->getOtherArg(state, 0));
+        return static_cast<Effect>(this->getOtherArg(state, 0));
     }
 
     auto getStrength(const State& state) const {
-        return static_cast<hardware::vibrator::EffectStrength>(this->getOtherArg(state, 1));
+        return static_cast<EffectStrength>(this->getOtherArg(state, 1));
     }
 };
 
 BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
         return;
     }
     if (!hasArgs(state)) {
@@ -347,18 +343,20 @@
 
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.alwaysOnEnable(id, effect, strength);
+        auto ret = halCall<void>(mController, [&](auto hal) {
+            return hal->alwaysOnEnable(id, effect, strength);
+        });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(mController.alwaysOnDisable(id), state);
+            auto disableResult =
+                    halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
+            checkHalResult(disableResult, state);
         }
     }
 });
 
 BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
         return;
     }
     if (!hasArgs(state)) {
@@ -371,11 +369,16 @@
 
     for (auto _ : state) {
         state.PauseTiming();
-        if (!checkHalResult(mController.alwaysOnEnable(id, effect, strength), state)) {
+        auto enableResult = halCall<void>(mController, [&](auto hal) {
+            return hal->alwaysOnEnable(id, effect, strength);
+        });
+        if (!checkHalResult(enableResult, state)) {
             continue;
         }
         state.ResumeTiming();
-        checkHalResult(mController.alwaysOnDisable(id), state);
+        auto disableResult =
+                halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
+        checkHalResult(disableResult, state);
     }
 });
 
@@ -390,10 +393,12 @@
 
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.performEffect(effect, strength, callback);
+        auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
+            return hal->performEffect(effect, strength, callback);
+        });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(mController.off(), state);
+            turnVibratorOff(state);
         }
     }
 });
@@ -402,12 +407,12 @@
 public:
     static void DefaultArgs(Benchmark* b) {
         vibrator::HalController controller;
-        auto primitivesResult = controller.getSupportedPrimitives();
+        auto primitivesResult = controller.getInfo().supportedPrimitives;
         if (!primitivesResult.isOk()) {
             return;
         }
 
-        std::vector<hardware::vibrator::CompositePrimitive> supported = primitivesResult.value();
+        std::vector<CompositePrimitive> supported = primitivesResult.value();
         b->ArgNames({"Primitive"});
 
         if (supported.empty()) {
@@ -415,11 +420,11 @@
             return;
         }
 
-        for (const auto& primitive : enum_range<hardware::vibrator::CompositePrimitive>()) {
+        for (const auto& primitive : enum_range<CompositePrimitive>()) {
             if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
                 continue;
             }
-            if (primitive == hardware::vibrator::CompositePrimitive::NOOP) {
+            if (primitive == CompositePrimitive::NOOP) {
                 continue;
             }
             b->Args({static_cast<long>(primitive)});
@@ -430,35 +435,35 @@
     bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
 
     auto getPrimitive(const State& state) const {
-        return static_cast<hardware::vibrator::CompositePrimitive>(this->getOtherArg(state, 0));
+        return static_cast<CompositePrimitive>(this->getOtherArg(state, 0));
     }
 };
 
 BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, {
-    auto result = mController.getCapabilities();
-
-    if (!hasCapabilities(result, vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
+    if (!hasCapabilities(vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
         return;
     }
     if (!hasArgs(state)) {
         return;
     }
 
-    hardware::vibrator::CompositeEffect effect;
+    CompositeEffect effect;
     effect.primitive = getPrimitive(state);
     effect.scale = 1.0f;
     effect.delayMs = static_cast<int32_t>(0);
 
-    std::vector<hardware::vibrator::CompositeEffect> effects;
+    std::vector<CompositeEffect> effects;
     effects.push_back(effect);
     auto callback = []() {};
 
     for (auto _ : state) {
         state.ResumeTiming();
-        auto ret = mController.performComposedEffect(effects, callback);
+        auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
+            return hal->performComposedEffect(effects, callback);
+        });
         state.PauseTiming();
         if (checkHalResult(ret, state)) {
-            checkHalResult(mController.off(), state);
+            turnVibratorOff(state);
         }
     }
 });
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index f884ff0..354e56c 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -29,19 +29,22 @@
 
 std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> scheduler);
 
+template <typename T>
+using HalFunction = std::function<T(std::shared_ptr<HalWrapper>)>;
+
 // Controller for Vibrator HAL handle.
 // This relies on a given Connector to connect to the underlying Vibrator HAL service and reconnects
 // after each failed api call. This also ensures connecting to the service is thread-safe.
-class HalController : public HalWrapper {
+class HalController {
 public:
     using Connector =
             std::function<std::shared_ptr<HalWrapper>(std::shared_ptr<CallbackScheduler>)>;
 
     HalController() : HalController(std::make_shared<CallbackScheduler>(), &connectHal) {}
     HalController(std::shared_ptr<CallbackScheduler> callbackScheduler, Connector connector)
-          : HalWrapper(std::move(callbackScheduler)),
-            mConnector(connector),
-            mConnectedHal(nullptr) {}
+          : mConnector(connector),
+            mConnectedHal(nullptr),
+            mCallbackScheduler(std::move(callbackScheduler)) {}
     virtual ~HalController() = default;
 
     /* Connects to the newest HAL version available, possibly waiting for the registered service to
@@ -51,53 +54,65 @@
      */
     virtual bool init();
 
-    /* reloads HAL service instance without waiting. This relies on the HAL version found by init()
+    /* Reloads HAL service instance without waiting. This relies on the HAL version found by init()
      * to rapidly reconnect to the specific HAL service, or defers to init() if it was never called.
      */
-    virtual void tryReconnect() override;
+    virtual void tryReconnect();
 
-    virtual HalResult<void> ping() override;
-    HalResult<void> on(std::chrono::milliseconds timeout,
-                       const std::function<void()>& completionCallback) final override;
-    HalResult<void> off() final override;
+    /* Returns info loaded from the connected HAL. This allows partial results to be returned if any
+     * of the Info fields has failed, but also retried on any failure.
+     */
+    Info getInfo() {
+        static Info sDefaultInfo = InfoCache().get();
+        return apply<Info>([](std::shared_ptr<HalWrapper> hal) { return hal->getInfo(); },
+                           sDefaultInfo, "getInfo");
+    }
 
-    HalResult<void> setAmplitude(float amplitude) final override;
-    HalResult<void> setExternalControl(bool enabled) final override;
-
-    HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
-                                   hardware::vibrator::EffectStrength strength) final override;
-    HalResult<void> alwaysOnDisable(int32_t id) final override;
-
-    HalResult<Capabilities> getCapabilities() final override;
-    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() final override;
-    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
-            final override;
-
-    HalResult<float> getResonantFrequency() final override;
-    HalResult<float> getQFactor() final override;
-
-    HalResult<std::chrono::milliseconds> performEffect(
-            hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
-            const std::function<void()>& completionCallback) final override;
-
-    HalResult<std::chrono::milliseconds> performComposedEffect(
-            const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
-            const std::function<void()>& completionCallback) final override;
+    /* Calls given HAL function, applying automatic retries to reconnect with the HAL when the
+     * result has failed. Parameter functionName is for logging purposes.
+     */
+    template <typename T>
+    HalResult<T> doWithRetry(const HalFunction<HalResult<T>>& halFn, const char* functionName) {
+        return apply(halFn, HalResult<T>::unsupported(), functionName);
+    }
 
 private:
+    static constexpr int MAX_RETRIES = 1;
+
     Connector mConnector;
     std::mutex mConnectedHalMutex;
     // Shared pointer to allow local copies to be used by different threads.
     std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex);
+    // Shared pointer to allow copies to be passed to possible recreated mConnectedHal instances.
+    std::shared_ptr<CallbackScheduler> mCallbackScheduler;
 
+    /* Calls given HAL function, applying automatic retries to reconnect with the HAL when the
+     * result has failed. Given default value is returned when no HAL is available, and given
+     * function name is for logging purposes.
+     */
     template <typename T>
-    HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
+    T apply(const HalFunction<T>& halFn, T defaultValue, const char* functionName) {
+        if (!init()) {
+            ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
+            return defaultValue;
+        }
+        std::shared_ptr<HalWrapper> hal;
+        {
+            std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+            hal = mConnectedHal;
+        }
 
-    template <typename T>
-    using hal_fn = std::function<HalResult<T>(std::shared_ptr<HalWrapper>)>;
+        for (int i = 0; i < MAX_RETRIES; i++) {
+            T result = halFn(hal);
+            if (result.checkAndLogFailure(functionName)) {
+                tryReconnect();
+            } else {
+                return result;
+            }
+        }
 
-    template <typename T>
-    HalResult<T> apply(hal_fn<T>& halFn, const char* functionName);
+        return halFn(hal);
+    }
 };
 
 }; // namespace vibrator
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 667702d..8720d9d 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -48,7 +48,7 @@
         if (status.isOk()) {
             return HalResult<T>::ok(data);
         }
-        return HalResult<T>::failed(std::string(status.toString8().c_str()));
+        return HalResult<T>::failed(status.toString8().c_str());
     }
     static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data);
 
@@ -61,10 +61,18 @@
 
     // This will throw std::bad_optional_access if this result is not ok.
     const T& value() const { return mValue.value(); }
+    const T valueOr(T&& defaultValue) const { return mValue.value_or(defaultValue); }
     bool isOk() const { return !mUnsupported && mValue.has_value(); }
     bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
     bool isUnsupported() const { return mUnsupported; }
     const char* errorMessage() const { return mErrorMessage.c_str(); }
+    bool checkAndLogFailure(const char* functionName) const {
+        if (isFailed()) {
+            ALOGE("%s failed: %s", functionName, errorMessage());
+            return true;
+        }
+        return false;
+    }
 
 private:
     std::optional<T> mValue;
@@ -96,6 +104,13 @@
     bool isFailed() const { return !mUnsupported && mFailed; }
     bool isUnsupported() const { return mUnsupported; }
     const char* errorMessage() const { return mErrorMessage.c_str(); }
+    bool checkAndLogFailure(const char* functionName) const {
+        if (isFailed()) {
+            ALOGE("%s failed: %s", functionName, errorMessage());
+            return true;
+        }
+        return false;
+    }
 
 private:
     std::string mErrorMessage;
@@ -133,7 +148,8 @@
     EXTERNAL_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL,
     EXTERNAL_AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL,
     COMPOSE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS,
-    ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL
+    COMPOSE_PWLE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_PWLE_EFFECTS,
+    ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL,
 };
 
 inline Capabilities operator|(Capabilities lhs, Capabilities rhs) {
@@ -156,6 +172,62 @@
 
 // -------------------------------------------------------------------------------------------------
 
+class Info {
+public:
+    const HalResult<Capabilities> capabilities;
+    const HalResult<std::vector<hardware::vibrator::Effect>> supportedEffects;
+    const HalResult<std::vector<hardware::vibrator::Braking>> supportedBraking;
+    const HalResult<std::vector<hardware::vibrator::CompositePrimitive>> supportedPrimitives;
+    const HalResult<std::vector<std::chrono::milliseconds>> primitiveDurations;
+    const HalResult<float> minFrequency;
+    const HalResult<float> resonantFrequency;
+    const HalResult<float> frequencyResolution;
+    const HalResult<float> qFactor;
+    const HalResult<std::vector<float>> maxAmplitudes;
+
+    bool checkAndLogFailure(const char*) const {
+        return capabilities.checkAndLogFailure("getCapabilities") ||
+                supportedEffects.checkAndLogFailure("getSupportedEffects") ||
+                supportedBraking.checkAndLogFailure("getSupportedBraking") ||
+                supportedPrimitives.checkAndLogFailure("getSupportedPrimitives") ||
+                primitiveDurations.checkAndLogFailure("getPrimitiveDuration") ||
+                minFrequency.checkAndLogFailure("getMinFrequency") ||
+                resonantFrequency.checkAndLogFailure("getResonantFrequency") ||
+                frequencyResolution.checkAndLogFailure("getFrequencyResolution") ||
+                qFactor.checkAndLogFailure("getQFactor") ||
+                maxAmplitudes.checkAndLogFailure("getMaxAmplitudes");
+    }
+};
+
+class InfoCache {
+public:
+    Info get() {
+        return {mCapabilities,        mSupportedEffects,    mSupportedBraking,
+                mSupportedPrimitives, mPrimitiveDurations,  mMinFrequency,
+                mResonantFrequency,   mFrequencyResolution, mQFactor,
+                mMaxAmplitudes};
+    }
+
+private:
+    static const constexpr char* MSG = "never loaded";
+    HalResult<Capabilities> mCapabilities = HalResult<Capabilities>::failed(MSG);
+    HalResult<std::vector<hardware::vibrator::Effect>> mSupportedEffects =
+            HalResult<std::vector<hardware::vibrator::Effect>>::failed(MSG);
+    HalResult<std::vector<hardware::vibrator::Braking>> mSupportedBraking =
+            HalResult<std::vector<hardware::vibrator::Braking>>::failed(MSG);
+    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives =
+            HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::failed(MSG);
+    HalResult<std::vector<std::chrono::milliseconds>> mPrimitiveDurations =
+            HalResult<std::vector<std::chrono::milliseconds>>::failed(MSG);
+    HalResult<float> mMinFrequency = HalResult<float>::failed(MSG);
+    HalResult<float> mResonantFrequency = HalResult<float>::failed(MSG);
+    HalResult<float> mFrequencyResolution = HalResult<float>::failed(MSG);
+    HalResult<float> mQFactor = HalResult<float>::failed(MSG);
+    HalResult<std::vector<float>> mMaxAmplitudes = HalResult<std::vector<float>>::failed(MSG);
+
+    friend class HalWrapper;
+};
+
 // Wrapper for Vibrator HAL handlers.
 class HalWrapper {
 public:
@@ -168,6 +240,8 @@
      */
     virtual void tryReconnect() = 0;
 
+    Info getInfo();
+
     virtual HalResult<void> ping() = 0;
     virtual HalResult<void> on(std::chrono::milliseconds timeout,
                                const std::function<void()>& completionCallback) = 0;
@@ -180,25 +254,43 @@
                                            hardware::vibrator::EffectStrength strength) = 0;
     virtual HalResult<void> alwaysOnDisable(int32_t id) = 0;
 
-    virtual HalResult<Capabilities> getCapabilities() = 0;
-    virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() = 0;
-    virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
-    getSupportedPrimitives() = 0;
-
-    virtual HalResult<float> getResonantFrequency() = 0;
-    virtual HalResult<float> getQFactor() = 0;
-
     virtual HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) = 0;
 
     virtual HalResult<std::chrono::milliseconds> performComposedEffect(
-            const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
-            const std::function<void()>& completionCallback) = 0;
+            const std::vector<hardware::vibrator::CompositeEffect>& primitives,
+            const std::function<void()>& completionCallback);
+
+    virtual HalResult<void> performPwleEffect(
+            const std::vector<hardware::vibrator::PrimitivePwle>& primitives,
+            const std::function<void()>& completionCallback);
 
 protected:
     // Shared pointer to allow CallbackScheduler to outlive this wrapper.
     const std::shared_ptr<CallbackScheduler> mCallbackScheduler;
+
+    // Load and cache vibrator info, returning cached result is present.
+    HalResult<Capabilities> getCapabilities();
+    HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurations();
+
+    // Request vibrator info to HAL skipping cache.
+    virtual HalResult<Capabilities> getCapabilitiesInternal() = 0;
+    virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
+    virtual HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal();
+    virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
+    getSupportedPrimitivesInternal();
+    virtual HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
+            const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives);
+    virtual HalResult<float> getMinFrequencyInternal();
+    virtual HalResult<float> getResonantFrequencyInternal();
+    virtual HalResult<float> getFrequencyResolutionInternal();
+    virtual HalResult<float> getQFactorInternal();
+    virtual HalResult<std::vector<float>> getMaxAmplitudesInternal();
+
+private:
+    std::mutex mInfoMutex;
+    InfoCache mInfoCache GUARDED_BY(mInfoMutex);
 };
 
 // Wrapper for the AIDL Vibrator HAL.
@@ -230,52 +322,38 @@
                                    hardware::vibrator::EffectStrength strength) override final;
     HalResult<void> alwaysOnDisable(int32_t id) override final;
 
-    HalResult<Capabilities> getCapabilities() override final;
-    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
-    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
-            override final;
-
-    HalResult<float> getResonantFrequency() override final;
-    HalResult<float> getQFactor() override final;
-
     HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) override final;
 
     HalResult<std::chrono::milliseconds> performComposedEffect(
-            const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+            const std::vector<hardware::vibrator::CompositeEffect>& primitives,
             const std::function<void()>& completionCallback) override final;
 
+    HalResult<void> performPwleEffect(
+            const std::vector<hardware::vibrator::PrimitivePwle>& primitives,
+            const std::function<void()>& completionCallback) override final;
+
+protected:
+    HalResult<Capabilities> getCapabilitiesInternal() override final;
+    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal() override final;
+    HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal()
+            override final;
+    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal()
+            override final;
+    HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
+            const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives)
+            override final;
+    HalResult<float> getMinFrequencyInternal() override final;
+    HalResult<float> getResonantFrequencyInternal() override final;
+    HalResult<float> getFrequencyResolutionInternal() override final;
+    HalResult<float> getQFactorInternal() override final;
+    HalResult<std::vector<float>> getMaxAmplitudesInternal() override final;
+
 private:
     const std::function<HalResult<sp<hardware::vibrator::IVibrator>>()> mReconnectFn;
     std::mutex mHandleMutex;
-    std::mutex mCapabilitiesMutex;
-    std::mutex mSupportedEffectsMutex;
-    std::mutex mSupportedPrimitivesMutex;
-    std::mutex mResonantFrequencyMutex;
-    std::mutex mQFactorMutex;
     sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
-    std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
-    std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
-            GUARDED_BY(mSupportedEffectsMutex);
-    std::optional<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives
-            GUARDED_BY(mSupportedPrimitivesMutex);
-    std::vector<std::optional<std::chrono::milliseconds>> mPrimitiveDurations
-            GUARDED_BY(mSupportedPrimitivesMutex);
-    std::optional<float> mResonantFrequency GUARDED_BY(mResonantFrequencyMutex);
-    std::optional<float> mQFactor GUARDED_BY(mQFactorMutex);
-
-    // Loads and caches from IVibrator.
-    HalResult<std::chrono::milliseconds> getPrimitiveDuration(
-            hardware::vibrator::CompositePrimitive primitive);
-
-    // Loads directly from IVibrator handle, skipping caches.
-    HalResult<Capabilities> getCapabilitiesInternal();
-    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
-    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal();
-
-    HalResult<float> getResonantFrequencyInternal();
-    HalResult<float> getQFactorInternal();
 
     sp<hardware::vibrator::IVibrator> getHal();
 };
@@ -302,26 +380,11 @@
                                    hardware::vibrator::EffectStrength strength) override final;
     HalResult<void> alwaysOnDisable(int32_t id) override final;
 
-    HalResult<Capabilities> getCapabilities() override final;
-    HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
-    HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
-            override final;
-
-    HalResult<float> getResonantFrequency() override final;
-    HalResult<float> getQFactor() override final;
-
-    HalResult<std::chrono::milliseconds> performComposedEffect(
-            const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
-            const std::function<void()>& completionCallback) override final;
-
 protected:
     std::mutex mHandleMutex;
-    std::mutex mCapabilitiesMutex;
     sp<I> mHandle GUARDED_BY(mHandleMutex);
-    std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
 
-    // Loads directly from IVibrator handle, skipping the mCapabilities cache.
-    virtual HalResult<Capabilities> getCapabilitiesInternal();
+    virtual HalResult<Capabilities> getCapabilitiesInternal() override;
 
     template <class T>
     using perform_fn =
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index e438d78..279496a 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -31,8 +31,6 @@
 
 #include "test_utils.h"
 
-using android::hardware::vibrator::CompositeEffect;
-using android::hardware::vibrator::CompositePrimitive;
 using android::hardware::vibrator::Effect;
 using android::hardware::vibrator::EffectStrength;
 
@@ -42,7 +40,11 @@
 using namespace std::chrono_literals;
 using namespace testing;
 
-static constexpr int MAX_ATTEMPTS = 2;
+static const auto ON_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) {
+    return hal->on(10ms, []() {});
+};
+static const auto OFF_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); };
+static const auto PING_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->ping(); };
 
 // -------------------------------------------------------------------------------------------------
 
@@ -63,21 +65,11 @@
     MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable,
                 (int32_t id, Effect effect, EffectStrength strength), (override));
     MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override));
-    MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilities, (), (override));
-    MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
-    MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (),
-                (override));
-
-    MOCK_METHOD(vibrator::HalResult<float>, getResonantFrequency, (), (override));
-    MOCK_METHOD(vibrator::HalResult<float>, getQFactor, (), (override));
-
     MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
                 (Effect effect, EffectStrength strength,
                  const std::function<void()>& completionCallback),
                 (override));
-    MOCK_METHOD(vibrator::HalResult<milliseconds>, performComposedEffect,
-                (const std::vector<CompositeEffect>& primitiveEffects,
-                 const std::function<void()>& completionCallback),
+    MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilitiesInternal, (),
                 (override));
 
     vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); }
@@ -104,64 +96,6 @@
     int32_t mConnectCounter;
     std::shared_ptr<MockHalWrapper> mMockHal;
     std::unique_ptr<vibrator::HalController> mController;
-
-    void setHalExpectations(int32_t cardinality, std::vector<CompositeEffect> compositeEffects,
-                            vibrator::HalResult<void> voidResult,
-                            vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
-                            vibrator::HalResult<std::vector<Effect>> effectsResult,
-                            vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult,
-                            vibrator::HalResult<float> resonantFrequencyResult,
-                            vibrator::HalResult<float> qFactorResult,
-                            vibrator::HalResult<milliseconds> durationResult) {
-        EXPECT_CALL(*mMockHal.get(), ping())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), off())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(1.0f)))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(),
-                    alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1)))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
-        EXPECT_CALL(*mMockHal.get(), getCapabilities())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(capabilitiesResult));
-        EXPECT_CALL(*mMockHal.get(), getSupportedEffects())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(effectsResult));
-        EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(primitivesResult));
-        EXPECT_CALL(*mMockHal.get(), getResonantFrequency())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(resonantFrequencyResult));
-        EXPECT_CALL(*mMockHal.get(), getQFactor())
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(qFactorResult));
-        EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(durationResult));
-        EXPECT_CALL(*mMockHal.get(), performComposedEffect(Eq(compositeEffects), _))
-                .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(durationResult));
-
-        if (cardinality > 1) {
-            // One reconnection call after each failure.
-            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(14 * cardinality));
-        }
-    }
 };
 
 // -------------------------------------------------------------------------------------------------
@@ -175,127 +109,52 @@
     ASSERT_EQ(1, mConnectCounter);
 }
 
+TEST_F(VibratorHalControllerTest, TestGetInfoRetriesOnAnyFailure) {
+    EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+    EXPECT_CALL(*mMockHal.get(), getCapabilitiesInternal())
+            .Times(Exactly(2))
+            .WillOnce(Return(vibrator::HalResult<vibrator::Capabilities>::failed("message")))
+            .WillRepeatedly(Return(vibrator::HalResult<vibrator::Capabilities>::ok(
+                    vibrator::Capabilities::ON_CALLBACK)));
+
+    auto result = mController->getInfo();
+    ASSERT_FALSE(result.capabilities.isFailed());
+
+    ASSERT_EQ(1, mConnectCounter);
+}
+
 TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
-    std::vector<Effect> effects = {Effect::CLICK, Effect::TICK};
-    std::vector<CompositePrimitive> primitives = {CompositePrimitive::CLICK,
-                                                  CompositePrimitive::THUD};
-    constexpr float F0 = 123.f;
-    constexpr float Q_FACTOR = 12.f;
-    const std::vector<CompositeEffect> compositeEffects =
-            {vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f),
-             vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)};
+    EXPECT_CALL(*mMockHal.get(), on(_, _))
+            .Times(Exactly(1))
+            .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
 
-    setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(),
-                       vibrator::HalResult<vibrator::Capabilities>::ok(
-                               vibrator::Capabilities::ON_CALLBACK),
-                       vibrator::HalResult<std::vector<Effect>>::ok(effects),
-                       vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives),
-                       vibrator::HalResult<float>::ok(F0), vibrator::HalResult<float>::ok(Q_FACTOR),
-                       vibrator::HalResult<milliseconds>::ok(100ms));
-
-    ASSERT_TRUE(mController->ping().isOk());
-    ASSERT_TRUE(mController->on(10ms, []() {}).isOk());
-    ASSERT_TRUE(mController->off().isOk());
-    ASSERT_TRUE(mController->setAmplitude(1.0f).isOk());
-    ASSERT_TRUE(mController->setExternalControl(true).isOk());
-    ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk());
-    ASSERT_TRUE(mController->alwaysOnDisable(1).isOk());
-
-    auto getCapabilitiesResult = mController->getCapabilities();
-    ASSERT_TRUE(getCapabilitiesResult.isOk());
-    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, getCapabilitiesResult.value());
-
-    auto getSupportedEffectsResult = mController->getSupportedEffects();
-    ASSERT_TRUE(getSupportedEffectsResult.isOk());
-    ASSERT_EQ(effects, getSupportedEffectsResult.value());
-
-    auto getSupportedPrimitivesResult = mController->getSupportedPrimitives();
-    ASSERT_TRUE(getSupportedPrimitivesResult.isOk());
-    ASSERT_EQ(primitives, getSupportedPrimitivesResult.value());
-
-    auto getResonantFrequencyResult = mController->getResonantFrequency();
-    ASSERT_TRUE(getResonantFrequencyResult.isOk());
-    ASSERT_EQ(F0, getResonantFrequencyResult.value());
-
-    auto getQFactorResult = mController->getQFactor();
-    ASSERT_TRUE(getQFactorResult.isOk());
-    ASSERT_EQ(Q_FACTOR, getQFactorResult.value());
-
-    auto performEffectResult =
-            mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
-    ASSERT_TRUE(performEffectResult.isOk());
-    ASSERT_EQ(100ms, performEffectResult.value());
-
-    auto performComposedEffectResult =
-            mController->performComposedEffect(compositeEffects, []() {});
-    ASSERT_TRUE(performComposedEffectResult.isOk());
-    ASSERT_EQ(100ms, performComposedEffectResult.value());
+    auto result = mController->doWithRetry<void>(ON_FN, "on");
+    ASSERT_TRUE(result.isOk());
 
     ASSERT_EQ(1, mConnectCounter);
 }
 
 TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
-    setHalExpectations(/* cardinality= */ 1, std::vector<CompositeEffect>(),
-                       vibrator::HalResult<void>::unsupported(),
-                       vibrator::HalResult<vibrator::Capabilities>::unsupported(),
-                       vibrator::HalResult<std::vector<Effect>>::unsupported(),
-                       vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(),
-                       vibrator::HalResult<float>::unsupported(),
-                       vibrator::HalResult<float>::unsupported(),
-                       vibrator::HalResult<milliseconds>::unsupported());
+    EXPECT_CALL(*mMockHal.get(), off())
+            .Times(Exactly(1))
+            .WillRepeatedly(Return(vibrator::HalResult<void>::unsupported()));
 
     ASSERT_EQ(0, mConnectCounter);
-
-    ASSERT_TRUE(mController->ping().isUnsupported());
-    ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
-    ASSERT_TRUE(mController->off().isUnsupported());
-    ASSERT_TRUE(mController->setAmplitude(1.0f).isUnsupported());
-    ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
-    ASSERT_TRUE(
-            mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
-    ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
-    ASSERT_TRUE(mController->getCapabilities().isUnsupported());
-    ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
-    ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
-    ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
-    ASSERT_TRUE(mController->getQFactor().isUnsupported());
-    ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
-                        .isUnsupported());
-    ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
-                        .isUnsupported());
-
+    auto result = mController->doWithRetry<void>(OFF_FN, "off");
+    ASSERT_TRUE(result.isUnsupported());
     ASSERT_EQ(1, mConnectCounter);
 }
 
 TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) {
-    setHalExpectations(MAX_ATTEMPTS, std::vector<CompositeEffect>(),
-                       vibrator::HalResult<void>::failed("message"),
-                       vibrator::HalResult<vibrator::Capabilities>::failed("message"),
-                       vibrator::HalResult<std::vector<Effect>>::failed("message"),
-                       vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"),
-                       vibrator::HalResult<float>::failed("message"),
-                       vibrator::HalResult<float>::failed("message"),
-                       vibrator::HalResult<milliseconds>::failed("message"));
+    EXPECT_CALL(*mMockHal.get(), on(_, _))
+            .Times(Exactly(2))
+            .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+    EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
 
     ASSERT_EQ(0, mConnectCounter);
 
-    ASSERT_TRUE(mController->ping().isFailed());
-    ASSERT_TRUE(mController->on(10ms, []() {}).isFailed());
-    ASSERT_TRUE(mController->off().isFailed());
-    ASSERT_TRUE(mController->setAmplitude(1.0f).isFailed());
-    ASSERT_TRUE(mController->setExternalControl(true).isFailed());
-    ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isFailed());
-    ASSERT_TRUE(mController->alwaysOnDisable(1).isFailed());
-    ASSERT_TRUE(mController->getCapabilities().isFailed());
-    ASSERT_TRUE(mController->getSupportedEffects().isFailed());
-    ASSERT_TRUE(mController->getSupportedPrimitives().isFailed());
-    ASSERT_TRUE(mController->getResonantFrequency().isFailed());
-    ASSERT_TRUE(mController->getQFactor().isFailed());
-    ASSERT_TRUE(
-            mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
-    ASSERT_TRUE(
-            mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}).isFailed());
-
+    auto result = mController->doWithRetry<void>(ON_FN, "on");
+    ASSERT_TRUE(result.isFailed());
     ASSERT_EQ(1, mConnectCounter);
 }
 
@@ -312,7 +171,9 @@
     }
 
     ASSERT_EQ(0, mConnectCounter);
-    ASSERT_TRUE(mController->ping().isOk());
+
+    auto result = mController->doWithRetry<void>(PING_FN, "ping");
+    ASSERT_TRUE(result.isOk());
     ASSERT_EQ(1, mConnectCounter);
 }
 
@@ -325,7 +186,10 @@
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() { ASSERT_TRUE(mController->ping().isOk()); }));
+        threads.push_back(std::thread([&]() {
+            auto result = mController->doWithRetry<void>(PING_FN, "ping");
+            ASSERT_TRUE(result.isOk());
+        }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 
@@ -341,33 +205,17 @@
     });
     ASSERT_EQ(0, mConnectCounter);
 
-    ASSERT_FALSE(mController->init());
-    ASSERT_TRUE(mController->ping().isUnsupported());
-    ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
-    ASSERT_TRUE(mController->off().isUnsupported());
-    ASSERT_TRUE(mController->setAmplitude(1.0f).isUnsupported());
-    ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
-    ASSERT_TRUE(
-            mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
-    ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
-    ASSERT_TRUE(mController->getCapabilities().isUnsupported());
-    ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
-    ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
-    ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
-    ASSERT_TRUE(mController->getQFactor().isUnsupported());
-    ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
-                        .isUnsupported());
-    ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
-                        .isUnsupported());
+    ASSERT_TRUE(mController->doWithRetry<void>(OFF_FN, "off").isUnsupported());
+    ASSERT_TRUE(mController->doWithRetry<void>(PING_FN, "ping").isUnsupported());
 
     // One connection attempt per api call.
-    ASSERT_EQ(15, mConnectCounter);
+    ASSERT_EQ(2, mConnectCounter);
 }
 
 TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
     {
         InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
+        EXPECT_CALL(*mMockHal.get(), on(_, _))
                 .Times(Exactly(1))
                 .WillRepeatedly([&](milliseconds timeout, std::function<void()> callback) {
                     mMockHal.get()->getCallbackScheduler()->schedule(callback, timeout);
@@ -380,14 +228,14 @@
         EXPECT_CALL(*mMockHal.get(), ping())
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
-        EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
     }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
 
-    ASSERT_TRUE(mController->on(10ms, callback).isOk());
-    ASSERT_TRUE(mController->ping().isFailed());
+    auto onFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->on(10ms, callback); };
+    ASSERT_TRUE(mController->doWithRetry<void>(onFn, "on").isOk());
+    ASSERT_TRUE(mController->doWithRetry<void>(PING_FN, "ping").isFailed());
     mMockHal.reset();
     ASSERT_EQ(0, *callbackCounter.get());
 
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index a8ddb99..d1db82b 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -76,20 +76,19 @@
     MOCK_METHOD(Status, compose,
                 (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
                 (override));
+    MOCK_METHOD(Status, composePwle,
+                (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb), (override));
     MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
     MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
     MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
     MOCK_METHOD(Status, getQFactor, (float * ret), (override));
     MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override));
-    MOCK_METHOD(Status, getFrequencyResolution, (float *freqResolutionHz), (override));
-    MOCK_METHOD(Status, getFrequencyMinimum, (float *freqMinimumHz), (override));
+    MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override));
+    MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override));
     MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override));
-    MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t *durationMs), (override));
-    MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t *maxSize), (override));
+    MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override));
+    MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override));
     MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override));
-    MOCK_METHOD(Status, composePwle,
-                (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb),
-                (override));
     MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
     MOCK_METHOD(std::string, getInterfaceHash, (), (override));
     MOCK_METHOD(IBinder*, onAsBinder, (), (override));
@@ -300,204 +299,143 @@
     ASSERT_TRUE(mWrapper->alwaysOnDisable(3).isFailed());
 }
 
-TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
+TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) {
+    constexpr float F_MIN = 100.f;
+    constexpr float F0 = 123.f;
+    constexpr float F_RESOLUTION = 0.5f;
+    constexpr float Q_FACTOR = 123.f;
+    std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
+    std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK};
+    std::vector<Braking> supportedBraking = {Braking::CLAB};
+    std::vector<float> amplitudes = {0.f, 1.f, 0.f};
+
+    std::vector<std::chrono::milliseconds> primitiveDurations;
+    constexpr auto primitiveRange = enum_range<CompositePrimitive>();
+    constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
+    primitiveDurations.resize(primitiveCount);
+    primitiveDurations[static_cast<size_t>(CompositePrimitive::CLICK)] = 10ms;
+
     EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
-            .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+            .Times(Exactly(2))
             .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
             .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedBraking), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(10), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F_RESOLUTION), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(amplitudes), Return(Status())));
 
-    ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported());
-    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+    vibrator::Info failed = mWrapper->getInfo();
+    ASSERT_TRUE(failed.capabilities.isFailed());
+    ASSERT_TRUE(failed.supportedEffects.isFailed());
+    ASSERT_TRUE(failed.supportedBraking.isFailed());
+    ASSERT_TRUE(failed.supportedPrimitives.isFailed());
+    ASSERT_TRUE(failed.primitiveDurations.isFailed());
+    ASSERT_TRUE(failed.minFrequency.isFailed());
+    ASSERT_TRUE(failed.resonantFrequency.isFailed());
+    ASSERT_TRUE(failed.frequencyResolution.isFailed());
+    ASSERT_TRUE(failed.qFactor.isFailed());
+    ASSERT_TRUE(failed.maxAmplitudes.isFailed());
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+    vibrator::Info successful = mWrapper->getInfo();
+    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, successful.capabilities.value());
+    ASSERT_EQ(supportedEffects, successful.supportedEffects.value());
+    ASSERT_EQ(supportedBraking, successful.supportedBraking.value());
+    ASSERT_EQ(supportedPrimitives, successful.supportedPrimitives.value());
+    ASSERT_EQ(primitiveDurations, successful.primitiveDurations.value());
+    ASSERT_EQ(F_MIN, successful.minFrequency.value());
+    ASSERT_EQ(F0, successful.resonantFrequency.value());
+    ASSERT_EQ(F_RESOLUTION, successful.frequencyResolution.value());
+    ASSERT_EQ(Q_FACTOR, successful.qFactor.value());
+    ASSERT_EQ(amplitudes, successful.maxAmplitudes.value());
 }
 
-TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesCachesResult) {
+TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) {
+    constexpr float F_MIN = 100.f;
+    constexpr float F0 = 123.f;
+    std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
+
     EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
             .Times(Exactly(1))
             .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
-
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getCapabilities();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
-        }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsDoesNotCacheFailedResult) {
-    std::vector<Effect> supportedEffects;
-    supportedEffects.push_back(Effect::CLICK);
-    supportedEffects.push_back(Effect::TICK);
-
-    EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
-            .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
-
-    ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
-    ASSERT_TRUE(mWrapper->getSupportedEffects().isFailed());
-
-    auto result = mWrapper->getSupportedEffects();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(supportedEffects, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsCachesResult) {
-    std::vector<Effect> supportedEffects;
-    supportedEffects.push_back(Effect::CLICK);
-    supportedEffects.push_back(Effect::TICK);
-
     EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
             .Times(Exactly(1))
             .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
-
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getSupportedEffects();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(supportedEffects, result.value());
-        }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    auto result = mWrapper->getSupportedEffects();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(supportedEffects, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesDoesNotCacheFailedResult) {
-    std::vector<CompositePrimitive> supportedPrimitives;
-    supportedPrimitives.push_back(CompositePrimitive::CLICK);
-    supportedPrimitives.push_back(CompositePrimitive::THUD);
-
-    EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
-            .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
-
-    ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
-    ASSERT_TRUE(mWrapper->getSupportedPrimitives().isFailed());
-
-    auto result = mWrapper->getSupportedPrimitives();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(supportedPrimitives, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesCachesResult) {
-    std::vector<CompositePrimitive> supportedPrimitives;
-    supportedPrimitives.push_back(CompositePrimitive::CLICK);
-    supportedPrimitives.push_back(CompositePrimitive::THUD);
-
+    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
     EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
             .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
-
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getSupportedPrimitives();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(supportedPrimitives, result.value());
-        }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    auto result = mWrapper->getSupportedPrimitives();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(supportedPrimitives, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyDoesNotCacheFailedResult) {
-    constexpr float F0 = 123.f;
-    EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
-            .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
-
-    ASSERT_TRUE(mWrapper->getResonantFrequency().isUnsupported());
-    ASSERT_TRUE(mWrapper->getResonantFrequency().isFailed());
-
-    auto result = mWrapper->getResonantFrequency();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(F0, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyCachesResult) {
-    constexpr float F0 = 123.f;
+            .WillRepeatedly(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+    EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
     EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
             .Times(Exactly(1))
             .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
-
-    std::vector<std::thread> threads;
-    for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getResonantFrequency();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(F0, result.value());
-        }));
-    }
-    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
-    auto result = mWrapper->getResonantFrequency();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(F0, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorDoesNotCacheFailedResult) {
-    constexpr float Q_FACTOR = 123.f;
-    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
-            .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
-
-    ASSERT_TRUE(mWrapper->getQFactor().isUnsupported());
-    ASSERT_TRUE(mWrapper->getQFactor().isFailed());
-
-    auto result = mWrapper->getQFactor();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(Q_FACTOR, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorCachesResult) {
-    constexpr float Q_FACTOR = 123.f;
-    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+    EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_))
             .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+            .WillRepeatedly(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+    EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+    EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getQFactor();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(Q_FACTOR, result.value());
-        }));
+        threads.push_back(
+                std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 
-    auto result = mWrapper->getQFactor();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(Q_FACTOR, result.value());
+    vibrator::Info info = mWrapper->getInfo();
+    ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, info.capabilities.value());
+    ASSERT_EQ(supportedEffects, info.supportedEffects.value());
+    ASSERT_TRUE(info.supportedBraking.isUnsupported());
+    ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
+    ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+    ASSERT_EQ(F_MIN, info.minFrequency.value());
+    ASSERT_EQ(F0, info.resonantFrequency.value());
+    ASSERT_TRUE(info.frequencyResolution.isUnsupported());
+    ASSERT_TRUE(info.qFactor.isUnsupported());
+    ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
@@ -580,6 +518,9 @@
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) {
+    std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK,
+                                                           CompositePrimitive::SPIN,
+                                                           CompositePrimitive::THUD};
     std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
     singleEffect.push_back(
             vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
@@ -590,24 +531,26 @@
 
     {
         InSequence seq;
-        EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
+        EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
                 .Times(Exactly(1))
-                .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
-
+                .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(Status())));
-        EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
-                .Times(Exactly(1))
-                .WillRepeatedly(Return(
-                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
-
         EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(3), Return(Status())));
+
+        EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
         EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -633,38 +576,91 @@
 }
 
 TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedCachesPrimitiveDurationsAndIgnoresFailures) {
+    std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::SPIN,
+                                                           CompositePrimitive::THUD};
     std::vector<CompositeEffect> multipleEffects;
     multipleEffects.push_back(
             vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 10ms, 0.5f));
     multipleEffects.push_back(
             vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 100ms, 1.0f));
 
-    EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
-            .Times(Exactly(1))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(Status())));
-    EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
-            .Times(Exactly(2))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
-    EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
-            .Times(Exactly(3))
-            .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+        EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+
+        EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+                .Times(Exactly(2))
+                .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+    }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
 
     auto result = mWrapper->performComposedEffect(multipleEffects, callback);
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(111ms, result.value()); // Failed primitive duration counted as 0.
+    ASSERT_EQ(112ms, result.value()); // Failed primitive durations counted as 1.
     ASSERT_EQ(1, *callbackCounter.get());
 
     result = mWrapper->performComposedEffect(multipleEffects, callback);
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(113ms, result.value()); // Second fetch succeeds and returns primitive duration.
+    ASSERT_EQ(114ms, result.value()); // Second fetch succeeds and returns primitive duration.
     ASSERT_EQ(2, *callbackCounter.get());
 
     result = mWrapper->performComposedEffect(multipleEffects, callback);
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(113ms, result.value()); // Cached durations not fetched again, same duration returned.
+    ASSERT_EQ(114ms, result.value()); // Cached durations not fetched again, same duration returned.
     ASSERT_EQ(3, *callbackCounter.get());
 }
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformPwleEffect) {
+    std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives;
+    multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms));
+    multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms));
+
+    {
+        InSequence seq;
+        EXPECT_CALL(*mMockHal.get(), composePwle(Eq(emptyPrimitives), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(Return(
+                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+        EXPECT_CALL(*mMockHal.get(), composePwle(Eq(multiplePrimitives), _))
+                .Times(Exactly(2))
+                .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+                .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+        ;
+    }
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    auto result = mWrapper->performPwleEffect(emptyPrimitives, callback);
+    ASSERT_TRUE(result.isUnsupported());
+    // Callback not triggered on failure
+    ASSERT_EQ(0, *callbackCounter.get());
+
+    result = mWrapper->performPwleEffect(multiplePrimitives, callback);
+    ASSERT_TRUE(result.isFailed());
+    // Callback not triggered for unsupported
+    ASSERT_EQ(0, *callbackCounter.get());
+
+    result = mWrapper->performPwleEffect(multiplePrimitives, callback);
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index 33c47cf..96b2582 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -31,11 +31,13 @@
 
 namespace V1_0 = android::hardware::vibrator::V1_0;
 
+using android::hardware::vibrator::Braking;
 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::PrimitivePwle;
 
 using namespace android;
 using namespace std::chrono_literals;
@@ -188,7 +190,7 @@
     ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoDoesNotCacheFailedResult) {
     EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
             .Times(Exactly(2))
             .WillOnce([]() {
@@ -196,49 +198,52 @@
             })
             .WillRepeatedly([]() { return hardware::Return<bool>(true); });
 
-    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+    vibrator::Info info = mWrapper->getInfo();
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
+    ASSERT_TRUE(info.supportedEffects.isUnsupported());
+    ASSERT_TRUE(info.supportedBraking.isUnsupported());
+    ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
+    ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+    ASSERT_TRUE(info.minFrequency.isUnsupported());
+    ASSERT_TRUE(info.resonantFrequency.isUnsupported());
+    ASSERT_TRUE(info.frequencyResolution.isUnsupported());
+    ASSERT_TRUE(info.qFactor.isUnsupported());
+    ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesWithoutAmplitudeControl) {
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) {
     EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
         return hardware::Return<bool>(false);
     });
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+    ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesCachesResult) {
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoCachesResult) {
     EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
         return hardware::Return<bool>(true);
     });
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getCapabilities();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
-        }));
+        threads.push_back(
+                std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedEffectsUnsupported) {
-    ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedPrimitivesUnsupported) {
-    ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
+    vibrator::Info info = mWrapper->getInfo();
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
+    ASSERT_TRUE(info.supportedEffects.isUnsupported());
+    ASSERT_TRUE(info.supportedBraking.isUnsupported());
+    ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
+    ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+    ASSERT_TRUE(info.minFrequency.isUnsupported());
+    ASSERT_TRUE(info.resonantFrequency.isUnsupported());
+    ASSERT_TRUE(info.frequencyResolution.isUnsupported());
+    ASSERT_TRUE(info.qFactor.isUnsupported());
+    ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
@@ -325,3 +330,18 @@
     // No callback is triggered.
     ASSERT_EQ(0, *callbackCounter.get());
 }
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformPwleEffectUnsupported) {
+    std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives;
+    multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms));
+    multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms));
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    ASSERT_TRUE(mWrapper->performPwleEffect(emptyPrimitives, callback).isUnsupported());
+    ASSERT_TRUE(mWrapper->performPwleEffect(multiplePrimitives, callback).isUnsupported());
+
+    // No callback is triggered.
+    ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
index 08652f4..a6f1a74 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -105,7 +105,7 @@
     ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesSuccessful) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoSuccessful) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -116,14 +116,12 @@
         });
     }
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
     ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL |
                       vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL,
-              result.value());
+              mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyAmplitudeControl) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
@@ -134,12 +132,10 @@
         });
     }
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyExternalControl) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
@@ -150,12 +146,10 @@
         });
     }
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value());
+    ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoNoCapabilities) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -166,12 +160,10 @@
         });
     }
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+    ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesFailed) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoFailed) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -190,11 +182,11 @@
                 });
     }
 
-    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
-    ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
+    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoCachesResult) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -207,20 +199,15 @@
 
     std::vector<std::thread> threads;
     for (int i = 0; i < 10; i++) {
-        threads.push_back(std::thread([&]() {
-            auto result = mWrapper->getCapabilities();
-            ASSERT_TRUE(result.isOk());
-            ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
-        }));
+        threads.push_back(
+                std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
 }
 
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoDoesNotCacheFailedResult) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -247,22 +234,16 @@
     }
 
     // Call to supportsAmplitudeControl failed.
-    auto result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isFailed());
+    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
 
     // Call to supportsExternalControl failed.
-    result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isFailed());
+    ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
 
     // Returns successful result from third call.
-    result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
 
     // Returns cached successful result.
-    result = mWrapper->getCapabilities();
-    ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
 }
 
 TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index b29376b..3de1576 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -40,6 +40,8 @@
 using namespace android;
 using namespace testing;
 
+static const auto OFF_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); };
+
 class MockBinder : public BBinder {
 public:
     MOCK_METHOD(status_t, linkToDeath,
@@ -70,20 +72,19 @@
     MOCK_METHOD(Status, compose,
                 (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
                 (override));
+    MOCK_METHOD(Status, composePwle,
+                (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb), (override));
     MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
     MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
     MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
     MOCK_METHOD(Status, getQFactor, (float * ret), (override));
     MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override));
-    MOCK_METHOD(Status, getFrequencyResolution, (float *freqResolutionHz), (override));
-    MOCK_METHOD(Status, getFrequencyMinimum, (float *freqMinimumHz), (override));
+    MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override));
+    MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override));
     MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override));
-    MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t *durationMs), (override));
-    MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t *maxSize), (override));
+    MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override));
+    MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override));
     MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override));
-    MOCK_METHOD(Status, composePwle,
-                (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb),
-                (override));
     MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
     MOCK_METHOD(std::string, getInterfaceHash, (), (override));
     MOCK_METHOD(IBinder*, onAsBinder, (), (override));
@@ -256,12 +257,11 @@
                             Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))))
             .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
 
-    EXPECT_CALL(*mMockVibrator.get(), getCapabilities(_))
+    EXPECT_CALL(*mMockVibrator.get(), off())
             .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
             .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(Return(Status()));
 
     // Get vibrator controller is successful even if first getVibrator.
     auto result = mWrapper->getVibrator(kVibratorId);
@@ -271,10 +271,10 @@
     auto vibrator = result.value();
     // First getVibrator call fails.
     ASSERT_FALSE(vibrator->init());
-    // First and second getCapabilities calls fail, reload IVibrator with getVibrator.
-    ASSERT_FALSE(vibrator->getCapabilities().isOk());
-    // Third call to getCapabilities worked after IVibrator reloaded.
-    ASSERT_TRUE(vibrator->getCapabilities().isOk());
+    // First and second off() calls fail, reload IVibrator with getVibrator.
+    ASSERT_TRUE(vibrator->doWithRetry<void>(OFF_FN, "off").isFailed());
+    // Third call to off() worked after IVibrator reloaded.
+    ASSERT_TRUE(vibrator->doWithRetry<void>(OFF_FN, "off").isOk());
 }
 
 TEST_F(VibratorManagerHalWrapperAidlTest, TestPrepareSynced) {
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
index 6c2aabb..0850ef3 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
@@ -41,7 +41,6 @@
     virtual ~MockHalController() = default;
 
     MOCK_METHOD(bool, init, (), (override));
-    MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
     MOCK_METHOD(void, tryReconnect, (), (override));
 };
 
@@ -63,13 +62,9 @@
 // -------------------------------------------------------------------------------------------------
 
 TEST_F(VibratorManagerHalWrapperLegacyTest, TestPing) {
-    EXPECT_CALL(*mMockController.get(), ping())
-            .Times(Exactly(2))
-            .WillOnce(Return(vibrator::HalResult<void>::failed("message")))
-            .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+    EXPECT_CALL(*mMockController.get(), init()).Times(Exactly(1)).WillOnce(Return(false));
 
-    ASSERT_TRUE(mWrapper->ping().isFailed());
-    ASSERT_TRUE(mWrapper->ping().isOk());
+    ASSERT_TRUE(mWrapper->ping().isUnsupported());
 }
 
 TEST_F(VibratorManagerHalWrapperLegacyTest, TestTryReconnect) {
@@ -85,8 +80,7 @@
 }
 
 TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorIds) {
-    std::vector<int32_t> expectedIds;
-    expectedIds.push_back(0);
+    std::vector<int> expectedIds = {0};
 
     EXPECT_CALL(*mMockController.get(), init())
             .Times(Exactly(2))
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
index 8d0b22e..1933a11 100644
--- a/services/vibratorservice/test/test_utils.h
+++ b/services/vibratorservice/test/test_utils.h
@@ -25,8 +25,12 @@
 
 namespace vibrator {
 
+using ::android::hardware::vibrator::ActivePwle;
+using ::android::hardware::vibrator::Braking;
+using ::android::hardware::vibrator::BrakingPwle;
 using ::android::hardware::vibrator::CompositeEffect;
 using ::android::hardware::vibrator::CompositePrimitive;
+using ::android::hardware::vibrator::PrimitivePwle;
 
 // -------------------------------------------------------------------------------------------------
 
@@ -53,6 +57,25 @@
         return effect;
     }
 
+    static PrimitivePwle createActivePwle(float startAmplitude, float startFrequency,
+                                          float endAmplitude, float endFrequency,
+                                          std::chrono::milliseconds duration) {
+        ActivePwle pwle;
+        pwle.startAmplitude = startAmplitude;
+        pwle.endAmplitude = endAmplitude;
+        pwle.startFrequency = startFrequency;
+        pwle.endFrequency = endFrequency;
+        pwle.duration = duration.count();
+        return pwle;
+    }
+
+    static PrimitivePwle createBrakingPwle(Braking braking, std::chrono::milliseconds duration) {
+        BrakingPwle pwle;
+        pwle.braking = braking;
+        pwle.duration = duration.count();
+        return pwle;
+    }
+
     static std::function<void()> createCountingCallback(int32_t* counter) {
         return [counter]() { *counter += 1; };
     }