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

Commit 66df5f37 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Create CallbackScheduler for Vibrator HAL callbacks"

parents 34bdf8e7 10d9dc7a
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