Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10d9dc7a authored by Lais Andrade's avatar Lais Andrade
Browse files

Create CallbackScheduler for Vibrator HAL callbacks

This scheduler keeps a single thread looping and executing callbacks
after a delay. This mechanism is used to simulate the callbacks that
some Vibrator HALs do not support.

Bug: 153418251
Test: atest libvibratorservice_test
Change-Id: If19481dfe2eca662d33738a11257bd4509fe81ca
parent 928b139f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ cc_library_shared {
    name: "libvibratorservice",

    srcs: [
        "VibratorCallbackScheduler.cpp",
        "VibratorHalWrapper.cpp",
    ],

+97 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <chrono>
#include <thread>

#include <vibratorservice/VibratorCallbackScheduler.h>

namespace android {

namespace vibrator {

// -------------------------------------------------------------------------------------------------

bool DelayedCallback::isExpired() const {
    return mExpiration <= std::chrono::steady_clock::now();
}

DelayedCallback::Timestamp DelayedCallback::getExpiration() const {
    return mExpiration;
}

void DelayedCallback::run() const {
    mCallback();
}

bool DelayedCallback::operator<(const DelayedCallback& other) const {
    return mExpiration < other.mExpiration;
}

bool DelayedCallback::operator>(const DelayedCallback& other) const {
    return mExpiration > other.mExpiration;
}

// -------------------------------------------------------------------------------------------------

CallbackScheduler::~CallbackScheduler() {
    {
        std::lock_guard<std::mutex> lock(mMutex);
        mFinished = true;
    }
    mCondition.notify_all();
    if (mCallbackThread && mCallbackThread->joinable()) {
        mCallbackThread->join();
    }
}

void CallbackScheduler::schedule(std::function<void()> callback, std::chrono::milliseconds delay) {
    {
        std::lock_guard<std::mutex> lock(mMutex);
        if (mCallbackThread == nullptr) {
            mCallbackThread = std::make_unique<std::thread>(&CallbackScheduler::loop, this);
        }
        mQueue.emplace(DelayedCallback(callback, delay));
    }
    mCondition.notify_all();
}

void CallbackScheduler::loop() {
    while (true) {
        std::lock_guard<std::mutex> lock(mMutex);
        if (mFinished) {
            // Destructor was called, so let the callback thread die.
            break;
        }
        while (!mQueue.empty() && mQueue.top().isExpired()) {
            mQueue.top().run();
            mQueue.pop();
        }
        if (mQueue.empty()) {
            // Wait until a new callback is scheduled.
            mCondition.wait(mMutex);
        } else {
            // Wait until next callback expires, or a new one is scheduled.
            mCondition.wait_until(mMutex, mQueue.top().getExpiration());
        }
    }
}

// -------------------------------------------------------------------------------------------------

}; // namespace vibrator

}; // namespace android
+122 −70
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@

#include <utils/Log.h>

#include <vibratorservice/VibratorCallbackScheduler.h>
#include <vibratorservice/VibratorHalWrapper.h>

using android::hardware::vibrator::CompositeEffect;
@@ -46,10 +47,12 @@ namespace vibrator {
template <class T>
HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) {
    if (cache.has_value()) {
        return HalResult<T>::ok(cache.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;
@@ -62,27 +65,6 @@ bool isStaticCastValid(Effect effect) {
    return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end());
}

template <class I, class T>
using perform_fn = hardware::Return<void> (I::*)(T, V1_0::EffectStrength,
                                                 V1_0::IVibrator::perform_cb);

template <class I, class T>
HalResult<milliseconds> perform(perform_fn<I, T> performFn, sp<I> handle, T effect,
                                EffectStrength strength) {
    V1_0::Status status;
    int32_t lengthMs;
    V1_0::IVibrator::perform_cb effectCallback = [&status, &lengthMs](V1_0::Status retStatus,
                                                                      uint32_t retLengthMs) {
        status = retStatus;
        lengthMs = retLengthMs;
    };

    V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
    auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);

    return HalResult<milliseconds>::fromReturn(result, status, milliseconds(lengthMs));
}

// -------------------------------------------------------------------------------------------------

template <typename T>
@@ -179,7 +161,7 @@ HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {

class HalCallbackWrapper : public Aidl::BnVibratorCallback {
public:
    HalCallbackWrapper(const std::function<void()>& completionCallback)
    HalCallbackWrapper(std::function<void()> completionCallback)
          : mCompletionCallback(completionCallback) {}

    binder::Status onComplete() override {
@@ -200,8 +182,17 @@ HalResult<void> AidlHalWrapper::ping() {

HalResult<void> AidlHalWrapper::on(milliseconds timeout,
                                   const std::function<void()>& completionCallback) {
    auto cb = new HalCallbackWrapper(completionCallback);
    return HalResult<void>::fromStatus(mHandle->on(timeout.count(), cb));
    HalResult<Capabilities> capabilities = getCapabilities();
    bool supportsCallback = capabilities.isOk() &&
            static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK);
    auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;

    auto ret = HalResult<void>::fromStatus(mHandle->on(timeout.count(), cb));
    if (!supportsCallback && ret.isOk()) {
        mCallbackScheduler->schedule(completionCallback, timeout);
    }

    return ret;
}

HalResult<void> AidlHalWrapper::off() {
@@ -227,39 +218,56 @@ HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) {

HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
    std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
    static auto loadFn = [this]() {
        int32_t capabilities = 0;
        auto result = mHandle->getCapabilities(&capabilities);
        return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
    };
    return loadCached<Capabilities>(loadFn, mCapabilities);
    return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this),
                                    mCapabilities);
}

HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() {
    std::lock_guard<std::mutex> lock(mSupportedEffectsMutex);
    static auto loadFn = [this]() {
        std::vector<Effect> supportedEffects;
        auto result = mHandle->getSupportedEffects(&supportedEffects);
        return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
    };
    return loadCached<std::vector<Effect>>(loadFn, mSupportedEffects);
    return loadCached<std::vector<Effect>>(std::bind(&AidlHalWrapper::getSupportedEffectsInternal,
                                                     this),
                                           mSupportedEffects);
}

HalResult<milliseconds> AidlHalWrapper::performEffect(
        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
    HalResult<Capabilities> capabilities = getCapabilities();
    bool supportsCallback = capabilities.isOk() &&
            static_cast<int32_t>(capabilities.value() & Capabilities::PERFORM_CALLBACK);
    auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;

    int32_t lengthMs;
    auto cb = new HalCallbackWrapper(completionCallback);
    auto result = mHandle->perform(effect, strength, cb, &lengthMs);
    return HalResult<milliseconds>::fromStatus(result, milliseconds(lengthMs));
    milliseconds length = milliseconds(lengthMs);

    auto ret = HalResult<milliseconds>::fromStatus(result, length);
    if (!supportsCallback && ret.isOk()) {
        mCallbackScheduler->schedule(completionCallback, length);
    }

    return ret;
}

HalResult<void> AidlHalWrapper::performComposedEffect(
        const std::vector<CompositeEffect>& primitiveEffects,
        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(mHandle->compose(primitiveEffects, cb));
}

HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
    int32_t capabilities = 0;
    auto result = mHandle->getCapabilities(&capabilities);
    return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
}

HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() {
    std::vector<Effect> supportedEffects;
    auto result = mHandle->getSupportedEffects(&supportedEffects);
    return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
}

// -------------------------------------------------------------------------------------------------

HalResult<void> HidlHalWrapperV1_0::ping() {
@@ -267,10 +275,14 @@ HalResult<void> HidlHalWrapperV1_0::ping() {
    return HalResult<void>::fromReturn(result);
}

HalResult<void> HidlHalWrapperV1_0::on(milliseconds timeout, const std::function<void()>&) {
HalResult<void> HidlHalWrapperV1_0::on(milliseconds timeout,
                                       const std::function<void()>& completionCallback) {
    auto result = mHandleV1_0->on(timeout.count());
    auto status = result.withDefault(V1_0::Status::UNKNOWN_ERROR);
    return HalResult<void>::fromStatus(status);
    auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
    if (ret.isOk()) {
        mCallbackScheduler->schedule(completionCallback, timeout);
    }
    return ret;
}

HalResult<void> HidlHalWrapperV1_0::off() {
@@ -309,11 +321,10 @@ HalResult<std::vector<Effect>> HidlHalWrapperV1_0::getSupportedEffects() {
    return HalResult<std::vector<Effect>>::unsupported();
}

HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(Effect effect, EffectStrength strength,
                                                          const std::function<void()>&) {
HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
    if (isStaticCastValid<V1_0::Effect>(effect)) {
        V1_0::Effect e = static_cast<V1_0::Effect>(effect);
        return perform(&V1_0::IVibrator::perform, mHandleV1_0, e, strength);
        return performInternalV1_0(effect, strength, completionCallback);
    }

    ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
@@ -334,17 +345,44 @@ HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilitiesInternal() {
    return HalResult<Capabilities>::fromReturn(result, capabilities);
}

template <class I, class T>
HalResult<milliseconds> HidlHalWrapperV1_0::performInternal(
        perform_fn<I, T> performFn, sp<I> handle, T effect, EffectStrength strength,
        const std::function<void()>& completionCallback) {
    V1_0::Status status;
    int32_t lengthMs;
    auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
        status = retStatus;
        lengthMs = retLengthMs;
    };

    V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
    auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
    milliseconds length = milliseconds(lengthMs);

    auto ret = HalResult<milliseconds>::fromReturn(result, status, length);
    if (ret.isOk()) {
        mCallbackScheduler->schedule(completionCallback, length);
    }

    return ret;
}

HalResult<milliseconds> HidlHalWrapperV1_0::performInternalV1_0(
        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
    V1_0::Effect e = static_cast<V1_0::Effect>(effect);
    return performInternal(&V1_0::IVibrator::perform, mHandleV1_0, e, strength, completionCallback);
}

// -------------------------------------------------------------------------------------------------

HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(Effect effect, EffectStrength strength,
                                                          const std::function<void()>&) {
HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
    if (isStaticCastValid<V1_0::Effect>(effect)) {
        V1_0::Effect e = static_cast<V1_0::Effect>(effect);
        return perform(&V1_0::IVibrator::perform, mHandleV1_0, e, strength);
        return performInternalV1_0(effect, strength, completionCallback);
    }
    if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
        V1_1::Effect_1_1 e = static_cast<V1_1::Effect_1_1>(effect);
        return perform(&V1_1::IVibrator::perform_1_1, mHandleV1_1, e, strength);
        return performInternalV1_1(effect, strength, completionCallback);
    }

    ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
@@ -352,21 +390,25 @@ HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(Effect effect, EffectS
    return HalResult<milliseconds>::unsupported();
}

HalResult<milliseconds> HidlHalWrapperV1_1::performInternalV1_1(
        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
    V1_1::Effect_1_1 e = static_cast<V1_1::Effect_1_1>(effect);
    return performInternal(&V1_1::IVibrator::perform_1_1, mHandleV1_1, e, strength,
                           completionCallback);
}

// -------------------------------------------------------------------------------------------------

HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(Effect effect, EffectStrength strength,
                                                          const std::function<void()>&) {
HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(
        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
    if (isStaticCastValid<V1_0::Effect>(effect)) {
        V1_0::Effect e = static_cast<V1_0::Effect>(effect);
        return perform(&V1_0::IVibrator::perform, mHandleV1_0, e, strength);
        return performInternalV1_0(effect, strength, completionCallback);
    }
    if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
        V1_1::Effect_1_1 e = static_cast<V1_1::Effect_1_1>(effect);
        return perform(&V1_1::IVibrator::perform_1_1, mHandleV1_1, e, strength);
        return performInternalV1_1(effect, strength, completionCallback);
    }
    if (isStaticCastValid<V1_2::Effect>(effect)) {
        V1_2::Effect e = static_cast<V1_2::Effect>(effect);
        return perform(&V1_2::IVibrator::perform_1_2, mHandleV1_2, e, strength);
        return performInternalV1_2(effect, strength, completionCallback);
    }

    ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
@@ -374,6 +416,13 @@ HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(Effect effect, EffectS
    return HalResult<milliseconds>::unsupported();
}

HalResult<milliseconds> HidlHalWrapperV1_2::performInternalV1_2(
        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
    V1_2::Effect e = static_cast<V1_2::Effect>(effect);
    return performInternal(&V1_2::IVibrator::perform_1_2, mHandleV1_2, e, strength,
                           completionCallback);
}

// -------------------------------------------------------------------------------------------------

HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
@@ -381,23 +430,19 @@ HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
    return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
}

HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(Effect effect, EffectStrength strength,
                                                          const std::function<void()>&) {
HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
    if (isStaticCastValid<V1_0::Effect>(effect)) {
        V1_0::Effect e = static_cast<V1_0::Effect>(effect);
        return perform(&V1_0::IVibrator::perform, mHandleV1_0, e, strength);
        return performInternalV1_0(effect, strength, completionCallback);
    }
    if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
        V1_1::Effect_1_1 e = static_cast<V1_1::Effect_1_1>(effect);
        return perform(&V1_1::IVibrator::perform_1_1, mHandleV1_1, e, strength);
        return performInternalV1_1(effect, strength, completionCallback);
    }
    if (isStaticCastValid<V1_2::Effect>(effect)) {
        V1_2::Effect e = static_cast<V1_2::Effect>(effect);
        return perform(&V1_2::IVibrator::perform_1_2, mHandleV1_2, e, strength);
        return performInternalV1_2(effect, strength, completionCallback);
    }
    if (isStaticCastValid<V1_3::Effect>(effect)) {
        V1_3::Effect e = static_cast<V1_3::Effect>(effect);
        return perform(&V1_3::IVibrator::perform_1_3, mHandleV1_3, e, strength);
        return performInternalV1_3(effect, strength, completionCallback);
    }

    ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
@@ -418,6 +463,13 @@ HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
    return HalResult<Capabilities>::fromReturn(result, capabilities);
}

HalResult<milliseconds> HidlHalWrapperV1_3::performInternalV1_3(
        Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
    V1_3::Effect e = static_cast<V1_3::Effect>(effect);
    return performInternal(&V1_3::IVibrator::perform_1_3, mHandleV1_3, e, strength,
                           completionCallback);
}

// -------------------------------------------------------------------------------------------------

}; // namespace vibrator
+82 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
#define ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H

#include <android-base/thread_annotations.h>
#include <chrono>
#include <condition_variable>
#include <queue>
#include <thread>

namespace android {

namespace vibrator {

// Wrapper for a callback to be executed after a delay.
class DelayedCallback {
public:
    using Timestamp = std::chrono::time_point<std::chrono::steady_clock>;

    DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay)
          : mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {}
    ~DelayedCallback() = default;

    void run() const;
    bool isExpired() const;
    Timestamp getExpiration() const;

    // Compare by expiration time, where A < B when A expires first.
    bool operator<(const DelayedCallback& other) const;
    bool operator>(const DelayedCallback& other) const;

private:
    std::function<void()> mCallback;
    Timestamp mExpiration;
};

// Schedules callbacks to be executed after a delay.
class CallbackScheduler {
public:
    CallbackScheduler() : mCallbackThread(nullptr), mFinished(false) {}
    virtual ~CallbackScheduler();

    virtual void schedule(std::function<void()> callback, std::chrono::milliseconds delay);

private:
    std::condition_variable_any mCondition;
    std::mutex mMutex;

    // Lazily instantiated only at the first time this scheduler is used.
    std::unique_ptr<std::thread> mCallbackThread;

    // Used to quit the callback thread when this instance is being destroyed.
    bool mFinished GUARDED_BY(mMutex);

    // Priority queue with reverse comparator, so tasks that expire first will be on top.
    std::priority_queue<DelayedCallback, std::vector<DelayedCallback>,
                        std::greater<DelayedCallback>>
            mQueue GUARDED_BY(mMutex);

    void loop();
};

}; // namespace vibrator

}; // namespace android

#endif // ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
+59 −11

File changed.

Preview size limit exceeded, changes collapsed.

Loading