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

Commit 8ab96e38 authored by Pavel Maltsev's avatar Pavel Maltsev
Browse files

Add continuous property infrastructure to VHAL

Implemented RecurrentTimer class that can be used in VHAL
implementations to trigger an event every X amount of time.
This make sense for continous properties.
Also added linkToDeath for VHAL listeners.

Test: unit tests added and ran

Change-Id: I4a2e0607ff6b15b3e7babbe3fbb9fff8d4e28838
parent da0d1a1d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -118,6 +118,7 @@ LOCAL_WHOLE_STATIC_LIBRARIES := \

LOCAL_SRC_FILES:= \
    tests/AccessControlConfigParser_test.cpp \
    tests/RecurrentTimer_test.cpp \
    tests/SubscriptionManager_test.cpp \
    tests/VehicleHalManager_test.cpp \
    tests/VehicleObjectPool_test.cpp \
+149 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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_hardware_automotive_vehicle_V2_0_RecurrentTimer_H_
#define android_hardware_automotive_vehicle_V2_0_RecurrentTimer_H_

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <list>
#include <mutex>
#include <set>
#include <thread>
#include <unordered_map>

/**
 * This class allows to specify multiple time intervals to receive
 * notifications. A single thread is used internally.
 */
class RecurrentTimer {
private:
    using Nanos = std::chrono::nanoseconds;
    using Clock = std::chrono::steady_clock;
    using TimePoint = std::chrono::time_point<Clock, Nanos>;
public:
    using Action = std::function<void(const std::vector<int32_t>& cookies)>;

    RecurrentTimer(const Action& action) : mAction(action) {
        mTimerThread = std::thread(&RecurrentTimer::loop, this, action);
    }

    virtual ~RecurrentTimer() {
        stop();
    }

    /**
     * Registers recurrent event for a given interval. Registred events are distinguished by
     * cookies thus calling this method multiple times with the same cookie will override the
     * interval provided before.
     */
    void registerRecurrentEvent(std::chrono::nanoseconds interval, int32_t cookie) {
        TimePoint now = Clock::now();
        // Align event time point among all intervals. Thus if we have two intervals 1ms and 2ms,
        // during every second wake-up both intervals will be triggered.
        TimePoint absoluteTime = now - Nanos(now.time_since_epoch().count() % interval.count());

        {
            std::lock_guard<std::mutex> g(mLock);
            mCookieToEventsMap[cookie] = { interval, cookie, absoluteTime };
        }
        mCond.notify_one();
    }

    void unregisterRecurrentEvent(int32_t cookie) {
        {
            std::lock_guard<std::mutex> g(mLock);
            mCookieToEventsMap.erase(cookie);
        }
        mCond.notify_one();
    }


private:

    struct RecurrentEvent {
        Nanos interval;
        int32_t cookie;
        TimePoint absoluteTime;  // Absolute time of the next event.

        void updateNextEventTime(TimePoint now) {
            // We want to move time to next event by adding some number of intervals (usually 1)
            // to previous absoluteTime.
            int intervalMultiplier = (now - absoluteTime) / interval;
            if (intervalMultiplier <= 0) intervalMultiplier = 1;
            absoluteTime += intervalMultiplier * interval;
        }
    };

    void loop(const Action& action) {
        static constexpr auto kInvalidTime = TimePoint(Nanos::max());

        std::vector<int32_t> cookies;

        while (!mStopRequested) {
            auto now = Clock::now();
            auto nextEventTime = kInvalidTime;
            cookies.clear();

            {
                std::unique_lock<std::mutex> g(mLock);

                for (auto&& it : mCookieToEventsMap) {
                    RecurrentEvent& event = it.second;
                    if (event.absoluteTime <= now) {
                        event.updateNextEventTime(now);
                        cookies.push_back(event.cookie);
                    }

                    if (nextEventTime > event.absoluteTime) {
                        nextEventTime = event.absoluteTime;
                    }
                }
            }

            if (cookies.size() != 0) {
                action(cookies);
            }

            std::unique_lock<std::mutex> g(mLock);
            mCond.wait_until(g, nextEventTime);  // nextEventTime can be nanoseconds::max()
        }
    }

    void stop() {
        mStopRequested = true;
        {
            std::lock_guard<std::mutex> g(mLock);
            mCookieToEventsMap.clear();
        }
        mCond.notify_one();
        if (mTimerThread.joinable()) {
            mTimerThread.join();
        }
    }
private:
    mutable std::mutex mLock;
    std::thread mTimerThread;
    std::condition_variable mCond;
    std::atomic_bool mStopRequested { false };
    Action mAction;
    std::unordered_map<int32_t, RecurrentEvent> mCookieToEventsMap;
};


#endif  // android_hardware_automotive_vehicle_V2_0_RecurrentTimer_H
+59 −25
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <list>

#include <android/log.h>
#include <hidl/HidlSupport.h>
#include <hwbinder/IPCThreadState.h>

#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
@@ -50,10 +51,8 @@ public:
    }

    void addOrUpdateSubscription(const SubscribeOptions &opts);

    bool isSubscribed(int32_t propId,
                      int32_t areaId,
                      SubscribeFlags flags);
    bool isSubscribed(int32_t propId, int32_t areaId, SubscribeFlags flags);
    std::vector<int32_t> getSubscribedProperties() const;

private:
    const sp<IVehicleCallback> mCallback;
@@ -85,15 +84,29 @@ struct HalClientValues {

class SubscriptionManager {
public:
    virtual ~SubscriptionManager() {}
    using OnPropertyUnsubscribed = std::function<void(int32_t)>;

    /**
     * Constructs SubscriptionManager
     *
     * @param onPropertyUnsubscribed - this callback function will be called when there are no
     *                                    more client subscribed to particular property.
     */
    SubscriptionManager(const OnPropertyUnsubscribed& onPropertyUnsubscribed)
            : mOnPropertyUnsubscribed(onPropertyUnsubscribed),
                mCallbackDeathRecipient(new DeathRecipient(
                    std::bind(&SubscriptionManager::onCallbackDead, this, std::placeholders::_1)))
    {}

    ~SubscriptionManager() = default;

    /**
     * Updates subscription. Returns the vector of properties subscription that
     * needs to be updated in VehicleHAL.
     */
    std::list<SubscribeOptions> addOrUpdateSubscription(
            const sp<IVehicleCallback>& callback,
            const hidl_vec<SubscribeOptions>& optionList);
    StatusCode addOrUpdateSubscription(const sp<IVehicleCallback>& callback,
                                       const hidl_vec<SubscribeOptions>& optionList,
                                       std::list<SubscribeOptions>* outUpdatedOptions);

    /**
     * Returns a list of IVehicleCallback -> list of VehiclePropValue ready for
@@ -103,30 +116,48 @@ public:
            const std::vector<recyclable_ptr<VehiclePropValue>>& propValues,
            SubscribeFlags flags) const;

    std::list<sp<HalClient>> getSubscribedClients(
        int32_t propId, int32_t area, SubscribeFlags flags) const;

    std::list<sp<HalClient>> getSubscribedClients(int32_t propId,
                                                  int32_t area,
                                                  SubscribeFlags flags) const;
    /**
     * Returns true the client was unsubscribed successfully and there are
     * no more clients subscribed to given propId.
     * If there are no clients subscribed to given properties than callback function provided
     * in the constructor will be called.
     */
    bool unsubscribe(const sp<IVehicleCallback>& callback,
                     int32_t propId);
    void unsubscribe(const sp<IVehicleCallback>& callback, int32_t propId);
private:
    std::list<sp< HalClient>> getSubscribedClientsLocked(
            int32_t propId, int32_t area, SubscribeFlags flags) const;
    std::list<sp<HalClient>> getSubscribedClientsLocked(int32_t propId,
                                                        int32_t area,
                                                        SubscribeFlags flags) const;

    bool updateHalEventSubscriptionLocked(const SubscribeOptions &opts, SubscribeOptions* out);

    bool updateHalEventSubscriptionLocked(const SubscribeOptions &opts,
                                          SubscribeOptions *out);
    void addClientToPropMapLocked(int32_t propId, const sp<HalClient>& client);

    void addClientToPropMapLocked(int32_t propId,
                                  const sp<HalClient> &client);
    sp<HalClientVector> getClientsForPropertyLocked(int32_t propId) const;

    sp<HalClientVector> getClientsForPropertyLocked(
            int32_t propId) const;
    sp<HalClient> getOrCreateHalClientLocked(const sp<IVehicleCallback> &callback);

    sp<HalClient> getOrCreateHalClientLocked(
            const sp<IVehicleCallback> &callback);
    void onCallbackDead(uint64_t cookie);

private:
    using OnClientDead = std::function<void(uint64_t)>;

    class DeathRecipient : public hidl_death_recipient {
    public:
        DeathRecipient(const OnClientDead& onClientDead)
            : mOnClientDead(onClientDead) {}
        ~DeathRecipient() = default;

        DeathRecipient(const DeathRecipient& ) = delete;
        DeathRecipient& operator=(const DeathRecipient&) = delete;

        void serviceDied(uint64_t cookie,
                         const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
            mOnClientDead(cookie);
        }
    private:
        OnClientDead mOnClientDead;
    };

private:
    using MuxGuard = std::lock_guard<std::mutex>;
@@ -136,6 +167,9 @@ private:
    std::map<sp<IVehicleCallback>, sp<HalClient>> mClients;
    std::map<int32_t, sp<HalClientVector>> mPropToClients;
    std::map<int32_t, SubscribeOptions> mHalEventSubscribeOptions;

    OnPropertyUnsubscribed mOnPropertyUnsubscribed;
    sp<DeathRecipient> mCallbackDeathRecipient;
};


+5 −1
Original line number Diff line number Diff line
@@ -56,7 +56,9 @@ struct Caller {
class VehicleHalManager : public IVehicle {
public:
    VehicleHalManager(VehicleHal* vehicleHal)
        : mHal(vehicleHal) {
        : mHal(vehicleHal),
          mSubscriptionManager(std::bind(&VehicleHalManager::onAllClientsUnsubscribed,
                                         this, std::placeholders::_1)) {
        init();
    }

@@ -105,6 +107,8 @@ private:
                  int32_t propertyId,
                  VehiclePropertyAccess requiredAccess) const;

    void onAllClientsUnsubscribed(int32_t propertyId);

    static bool isSubscribable(const VehiclePropConfig& config,
                               SubscribeFlags flags);
    static bool isSampleRateFixed(VehiclePropertyChangeMode mode);
+62 −9
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include "SubscriptionManager.h"

#include <cmath>
#include <inttypes.h>

#include <android/log.h>

@@ -58,6 +59,8 @@ bool mergeSubscribeOptions(const SubscribeOptions &oldOpts,
}

void HalClient::addOrUpdateSubscription(const SubscribeOptions &opts)  {
    ALOGI("%s opts.propId: 0x%x", __func__, opts.propId);

    auto it = mSubscriptions.find(opts.propId);
    if (it == mSubscriptions.end()) {
        mSubscriptions.emplace(opts.propId, opts);
@@ -84,17 +87,33 @@ bool HalClient::isSubscribed(int32_t propId,
    return res;
}

std::list<SubscribeOptions> SubscriptionManager::addOrUpdateSubscription(
std::vector<int32_t> HalClient::getSubscribedProperties() const {
    std::vector<int32_t> props;
    for (const auto& subscription : mSubscriptions) {
        ALOGI("%s propId: 0x%x, propId: 0x%x", __func__, subscription.first, subscription.second.propId);
        props.push_back(subscription.first);
    }
    return props;
}

StatusCode SubscriptionManager::addOrUpdateSubscription(
        const sp<IVehicleCallback> &callback,
        const hidl_vec<SubscribeOptions> &optionList) {
    std::list<SubscribeOptions> updatedSubscriptions;
        const hidl_vec<SubscribeOptions> &optionList,
        std::list<SubscribeOptions>* outUpdatedSubscriptions) {
    outUpdatedSubscriptions->clear();

    MuxGuard g(mLock);

    ALOGI("SubscriptionManager::addOrUpdateSubscription, callback: %p", callback.get());

    const sp<HalClient>& client = getOrCreateHalClientLocked(callback);
    if (client.get() == nullptr) {
        return StatusCode::INTERNAL_ERROR;
    }

    for (size_t i = 0; i < optionList.size(); i++) {
        const SubscribeOptions& opts = optionList[i];
        ALOGI("SubscriptionManager::addOrUpdateSubscription, prop: 0x%x", opts.propId);
        client->addOrUpdateSubscription(opts);

        addClientToPropMapLocked(opts.propId, client);
@@ -102,12 +121,12 @@ std::list<SubscribeOptions> SubscriptionManager::addOrUpdateSubscription(
        if (SubscribeFlags::HAL_EVENT & opts.flags) {
            SubscribeOptions updated;
            if (updateHalEventSubscriptionLocked(opts, &updated)) {
                updatedSubscriptions.push_back(updated);
                outUpdatedSubscriptions->push_back(updated);
            }
        }
    }

    return updatedSubscriptions;
    return StatusCode::OK;
}

std::list<HalClientValues> SubscriptionManager::distributeValuesToClients(
@@ -205,6 +224,14 @@ sp<HalClient> SubscriptionManager::getOrCreateHalClientLocked(
        const sp<IVehicleCallback>& callback) {
    auto it = mClients.find(callback);
    if (it == mClients.end()) {
        uint64_t cookie = reinterpret_cast<uint64_t>(callback.get());
        ALOGI("Creating new client and linking to death recipient, cookie: 0x%" PRIx64, cookie);
        auto res = callback->linkToDeath(mCallbackDeathRecipient, cookie);
        if (!res.isOk()) {  // Client is already dead?
            ALOGW("%s failed to link to death, client %p, err: %s",
                  __func__, callback.get(), res.description().c_str());
            return nullptr;
        }
        IPCThreadState* self = IPCThreadState::self();
        pid_t pid = self->getCallingPid();
        uid_t uid = self->getCallingUid();
@@ -216,7 +243,7 @@ sp<HalClient> SubscriptionManager::getOrCreateHalClientLocked(
    }
}

bool SubscriptionManager::unsubscribe(const sp<IVehicleCallback>& callback,
void SubscriptionManager::unsubscribe(const sp<IVehicleCallback>& callback,
                                      int32_t propId) {
    MuxGuard g(mLock);
    auto propertyClients = getClientsForPropertyLocked(propId);
@@ -243,13 +270,39 @@ bool SubscriptionManager::unsubscribe(const sp<IVehicleCallback>& callback,
        }

        if (!isClientSubscribedToOtherProps) {
            auto res = client->getCallback()->unlinkToDeath(mCallbackDeathRecipient);
            if (!res.isOk()) {
                ALOGW("%s failed to unlink to death, client: %p, err: %s",
                      __func__, client->getCallback().get(), res.description().c_str());
            }
            mClients.erase(clientIter);
        }
    }

    return (propertyClients == nullptr || propertyClients->isEmpty())
            ? mHalEventSubscribeOptions.erase(propId) == 1
            : false;
    if (propertyClients == nullptr || propertyClients->isEmpty()) {
        mHalEventSubscribeOptions.erase(propId);
        mOnPropertyUnsubscribed(propId);
    }
}

void SubscriptionManager::onCallbackDead(uint64_t cookie) {
    ALOGI("%s, cookie: 0x%" PRIx64, __func__, cookie);
    IVehicleCallback* callback = reinterpret_cast<IVehicleCallback*>(cookie);

    std::vector<int32_t> props;
    {
        MuxGuard g(mLock);
        const auto& it = mClients.find(callback);
        if (it == mClients.end()) {
            return;  // Nothing to do here, client wasn't subscribed to any properties.
        }
        const auto& halClient = it->second;
        props = halClient->getSubscribedProperties();
    }

    for (int32_t propId : props) {
        unsubscribe(callback, propId);
    }
}


Loading