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

Commit 052608fe authored by Yu Shan's avatar Yu Shan
Browse files

Add subscribe/unsubscribe to IVehicleHardware.

These two functions replaces updateSampleRate. Previously
updateSampleRate will be called when a continuous property is
subscribed/unsubscribed. However, IVehicleHardware layer does not
know when an on-change property is subscribed/unsubscribed. This CL
introduces two new functions to notify IVehicleHardware when any
properties are subscribed/unsubscribed.

Test: atest DefaultVehicleHalTest
Bug: 306262618
Change-Id: I8d32d1eb919036015b5082f74e259bcd4b1bd29e
parent a7867cec
Loading
Loading
Loading
Loading
+79 −29
Original line number Diff line number Diff line
@@ -82,35 +82,6 @@ class IVehicleHardware {
            const std::vector<aidl::android::hardware::automotive::vehicle::GetValueRequest>&
                    requests) const = 0;

    // Update the sampling rate for the specified property and the specified areaId (0 for global
    // property) if server supports it. The property must be a continuous property.
    // {@code sampleRate} means that for this specific property, the server must generate at least
    // this many OnPropertyChange events per seconds.
    // A sampleRate of 0 means the property is no longer subscribed and server does not need to
    // generate any onPropertyEvent for this property.
    // This would be called if sample rate is updated for a subscriber, a new subscriber is added
    // or an existing subscriber is removed. For example:
    // 1. We have no subscriber for speed.
    // 2. A new subscriber is subscribing speed for 10 times/s, updsateSampleRate would be called
    //    with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s.
    // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10
    //    times/sec, updateSampleRate would not be called.
    // 4. The initial subscriber is removed, updateSampleRate would be called with sampleRate as
    //    5, because now it only needs to report event 5times/sec. The impl can now poll vehicle
    //    speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as
    //    the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional
    //    events.
    // 5. The second subscriber is removed, updateSampleRate would be called with sampleRate as 0.
    //    The impl can optionally disable the polling for vehicle speed.
    //
    // If the impl is always polling at {@code maxSampleRate} as specified in config, then this
    // function can be a no-op.
    virtual aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
            [[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId,
            [[maybe_unused]] float sampleRate) {
        return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
    }

    // Dump debug information in the server.
    virtual DumpResult dump(const std::vector<std::string>& options) = 0;

@@ -145,6 +116,85 @@ class IVehicleHardware {
        // By default batching is disabled.
        return std::chrono::nanoseconds(0);
    }

    // A [propId, areaId] is newly subscribed or the update rate is changed.
    //
    // The 'options' contains the property ID, area ID and sample rate in Hz.
    //
    // For continuous property, the sample rate is never 0 and indicates the new sample rate (or
    // the initial sample rate if this property was not subscribed before).
    //
    // For on-change property, the sample rate is always 0 and must be ignored.
    //
    // A subscription from VHAL client might not necessarily trigger this function.
    // DefaultVehicleHal will aggregate all the subscriptions from all the clients and notify
    // IVehicleHardware if new subscriptions are required or sample rate is updated.
    //
    // For example:
    // 1. VHAL initially have no subscriber for speed.
    // 2. A new subscriber is subscribing speed for 10 times/s, 'subscribe' is called
    //    with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s.
    // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10
    //    times/sec, 'subscribe' is not called.
    // 4. The initial subscriber is removed, 'subscribe' is called with sampleRate as
    //    5, because now it only needs to report event 5times/sec. The impl can now poll vehicle
    //    speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as
    //    the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional
    //    events.
    // 5. The second subscriber is removed, 'unsubscribe' is called.
    //    The impl can optionally disable the polling for vehicle speed.
    //
    // It is recommended to only deliver the subscribed property events to DefaultVehicleHal to
    // improve performance. However, even if unsubscribed property events are delivered, they
    // will be filtered out by DefaultVehicleHal.
    //
    // For continuous property, if the impl is always polling at {@code maxSampleRate} as specified
    // in config, then this function can be a no-op.
    //
    // For on-change property, if the impl is always subscribing to all on-change properties, then
    // this function can be no-op.
    virtual aidl::android::hardware::automotive::vehicle::StatusCode subscribe(
            [[maybe_unused]] aidl::android::hardware::automotive::vehicle::SubscribeOptions
                    options) {
        return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
    }

    // A [propId, areaId] is unsubscribed. This applies for both continuous or on-change property.
    virtual aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(
            [[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId) {
        return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
    }

    // This function is deprecated, subscribe/unsubscribe should be used instead.
    //
    // Update the sampling rate for the specified property and the specified areaId (0 for global
    // property) if server supports it. The property must be a continuous property.
    // {@code sampleRate} means that for this specific property, the server must generate at least
    // this many OnPropertyChange events per seconds.
    // A sampleRate of 0 means the property is no longer subscribed and server does not need to
    // generate any onPropertyEvent for this property.
    // This would be called if sample rate is updated for a subscriber, a new subscriber is added
    // or an existing subscriber is removed. For example:
    // 1. We have no subscriber for speed.
    // 2. A new subscriber is subscribing speed for 10 times/s, updateSampleRate would be called
    //    with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s.
    // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10
    //    times/sec, updateSampleRate would not be called.
    // 4. The initial subscriber is removed, updateSampleRate would be called with sampleRate as
    //    5, because now it only needs to report event 5times/sec. The impl can now poll vehicle
    //    speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as
    //    the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional
    //    events.
    // 5. The second subscriber is removed, updateSampleRate would be called with sampleRate as 0.
    //    The impl can optionally disable the polling for vehicle speed.
    //
    // If the impl is always polling at {@code maxSampleRate} as specified in config, then this
    // function can be a no-op.
    virtual aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
            [[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId,
            [[maybe_unused]] float sampleRate) {
        return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
    }
};

}  // namespace vehicle
+5 −0
Original line number Diff line number Diff line
@@ -329,6 +329,11 @@ struct PropIdAreaIdHash {
    }
};

inline std::string propIdToString(int32_t propId) {
    return toString(
            static_cast<aidl::android::hardware::automotive::vehicle::VehicleProperty>(propId));
}

}  // namespace vehicle
}  // namespace automotive
}  // namespace hardware
+12 −3
Original line number Diff line number Diff line
@@ -119,7 +119,7 @@ class SubscriptionManager final {
    mutable std::mutex mLock;
    std::unordered_map<PropIdAreaId, std::unordered_map<ClientIdType, CallbackType>,
                       PropIdAreaIdHash>
            mClientsByPropIdArea GUARDED_BY(mLock);
            mClientsByPropIdAreaId GUARDED_BY(mLock);
    std::unordered_map<ClientIdType, std::unordered_set<PropIdAreaId, PropIdAreaIdHash>>
            mSubscribedPropsByClient GUARDED_BY(mLock);
    std::unordered_map<PropIdAreaId, ContSubConfigs, PropIdAreaIdHash> mContSubConfigsByPropIdArea
@@ -128,13 +128,22 @@ class SubscriptionManager final {
    VhalResult<void> addContinuousSubscriberLocked(const ClientIdType& clientId,
                                                   const PropIdAreaId& propIdAreaId,
                                                   float sampleRateHz) REQUIRES(mLock);
    VhalResult<void> addOnChangeSubscriberLocked(const PropIdAreaId& propIdAreaId) REQUIRES(mLock);
    // Removes the subscription client for the continuous [propId, areaId].
    VhalResult<void> removeContinuousSubscriberLocked(const ClientIdType& clientId,
                                                      const PropIdAreaId& propIdAreaId)
            REQUIRES(mLock);
    // Removes one subscription client for the on-change [propId, areaId].
    VhalResult<void> removeOnChangeSubscriberLocked(const PropIdAreaId& propIdAreaId)
            REQUIRES(mLock);

    VhalResult<void> updateContSubConfigs(const PropIdAreaId& PropIdAreaId,
    VhalResult<void> updateContSubConfigsLocked(const PropIdAreaId& PropIdAreaId,
                                                const ContSubConfigs& newConfig) REQUIRES(mLock);

    VhalResult<void> unsubscribePropIdAreaIdLocked(SubscriptionManager::ClientIdType clientId,
                                                   const PropIdAreaId& propIdAreaId)
            REQUIRES(mLock);

    // Checks whether the manager is empty. For testing purpose.
    bool isEmpty();

+136 −60
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#include "SubscriptionManager.h"

#include <VehicleUtils.h>
#include <android-base/stringprintf.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
@@ -29,10 +30,6 @@ namespace vehicle {

namespace {

constexpr float ONE_SECOND_IN_NANO = 1'000'000'000.;

}  // namespace

using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
using ::aidl::android::hardware::automotive::vehicle::StatusCode;
using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
@@ -43,13 +40,26 @@ using ::android::base::Result;
using ::android::base::StringPrintf;
using ::ndk::ScopedAStatus;

constexpr float ONE_SECOND_IN_NANOS = 1'000'000'000.;

SubscribeOptions newSubscribeOptions(int32_t propId, int32_t areaId, float sampleRateHz) {
    SubscribeOptions subscribedOptions;
    subscribedOptions.propId = propId;
    subscribedOptions.areaIds = {areaId};
    subscribedOptions.sampleRate = sampleRateHz;

    return subscribedOptions;
}

}  // namespace

SubscriptionManager::SubscriptionManager(IVehicleHardware* vehicleHardware)
    : mVehicleHardware(vehicleHardware) {}

SubscriptionManager::~SubscriptionManager() {
    std::scoped_lock<std::mutex> lockGuard(mLock);

    mClientsByPropIdArea.clear();
    mClientsByPropIdAreaId.clear();
    mSubscribedPropsByClient.clear();
}

@@ -62,10 +72,10 @@ Result<int64_t> SubscriptionManager::getIntervalNanos(float sampleRateHz) {
    if (sampleRateHz <= 0) {
        return Error() << "invalid sample rate, must be a positive number";
    }
    if (sampleRateHz <= (ONE_SECOND_IN_NANO / static_cast<float>(INT64_MAX))) {
    if (sampleRateHz <= (ONE_SECOND_IN_NANOS / static_cast<float>(INT64_MAX))) {
        return Error() << "invalid sample rate: " << sampleRateHz << ", too small";
    }
    intervalNanos = static_cast<int64_t>(ONE_SECOND_IN_NANO / sampleRateHz);
    intervalNanos = static_cast<int64_t>(ONE_SECOND_IN_NANOS / sampleRateHz);
    return intervalNanos;
}

@@ -95,12 +105,31 @@ float ContSubConfigs::getMaxSampleRateHz() const {
    return mMaxSampleRateHz;
}

VhalResult<void> SubscriptionManager::addOnChangeSubscriberLocked(
        const PropIdAreaId& propIdAreaId) {
    if (mClientsByPropIdAreaId.find(propIdAreaId) != mClientsByPropIdAreaId.end()) {
        // This propId, areaId is already subscribed, ignore the request.
        return {};
    }

    int32_t propId = propIdAreaId.propId;
    int32_t areaId = propIdAreaId.areaId;
    if (auto status = mVehicleHardware->subscribe(
                newSubscribeOptions(propId, areaId, /*updateRateHz=*/0));
        status != StatusCode::OK) {
        return StatusError(status)
               << StringPrintf("failed subscribe for prop: %s, areaId: %" PRId32,
                               propIdToString(propId).c_str(), areaId);
    }
    return {};
}

VhalResult<void> SubscriptionManager::addContinuousSubscriberLocked(
        const ClientIdType& clientId, const PropIdAreaId& propIdAreaId, float sampleRateHz) {
    // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
    ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
    newConfig.addClient(clientId, sampleRateHz);
    return updateContSubConfigs(propIdAreaId, newConfig);
    return updateContSubConfigsLocked(propIdAreaId, newConfig);
}

VhalResult<void> SubscriptionManager::removeContinuousSubscriberLocked(
@@ -108,10 +137,27 @@ VhalResult<void> SubscriptionManager::removeContinuousSubscriberLocked(
    // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
    ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
    newConfig.removeClient(clientId);
    return updateContSubConfigs(propIdAreaId, newConfig);
    return updateContSubConfigsLocked(propIdAreaId, newConfig);
}

VhalResult<void> SubscriptionManager::updateContSubConfigs(const PropIdAreaId& propIdAreaId,
VhalResult<void> SubscriptionManager::removeOnChangeSubscriberLocked(
        const PropIdAreaId& propIdAreaId) {
    if (mClientsByPropIdAreaId[propIdAreaId].size() > 1) {
        // After unsubscribing this client, there is still client subscribed, so do nothing.
        return {};
    }

    int32_t propId = propIdAreaId.propId;
    int32_t areaId = propIdAreaId.areaId;
    if (auto status = mVehicleHardware->unsubscribe(propId, areaId); status != StatusCode::OK) {
        return StatusError(status)
               << StringPrintf("failed unsubscribe for prop: %s, areaId: %" PRId32,
                               propIdToString(propId).c_str(), areaId);
    }
    return {};
}

VhalResult<void> SubscriptionManager::updateContSubConfigsLocked(const PropIdAreaId& propIdAreaId,
                                                                 const ContSubConfigs& newConfig) {
    if (newConfig.getMaxSampleRateHz() ==
        mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRateHz()) {
@@ -123,10 +169,27 @@ VhalResult<void> SubscriptionManager::updateContSubConfigs(const PropIdAreaId& p
    int32_t areaId = propIdAreaId.areaId;
    if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRateHz);
        status != StatusCode::OK) {
        return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32
                                                   ", area"
        return StatusError(status)
               << StringPrintf("failed to update sample rate for prop: %s, areaId: %" PRId32
                               ", sample rate: %f HZ",
                               propIdToString(propId).c_str(), areaId, newRateHz);
    }
    if (newRateHz != 0) {
        if (auto status =
                    mVehicleHardware->subscribe(newSubscribeOptions(propId, areaId, newRateHz));
            status != StatusCode::OK) {
            return StatusError(status) << StringPrintf(
                           "failed subscribe for prop: %s, areaId"
                           ": %" PRId32 ", sample rate: %f HZ",
                                                   propId, areaId, newRateHz);
                           propIdToString(propId).c_str(), areaId, newRateHz);
        }
    } else {
        if (auto status = mVehicleHardware->unsubscribe(propId, areaId); status != StatusCode::OK) {
            return StatusError(status) << StringPrintf(
                           "failed unsubscribe for prop: %s, areaId"
                           ": %" PRId32,
                           propIdToString(propId).c_str(), areaId);
        }
    }
    mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
    return {};
@@ -163,17 +226,49 @@ VhalResult<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCa
                    .propId = propId,
                    .areaId = areaId,
            };
            VhalResult<void> result;
            if (isContinuousProperty) {
                if (auto result = addContinuousSubscriberLocked(clientId, propIdAreaId,
                                                                option.sampleRate);
                    !result.ok()) {
                    return result;
                result = addContinuousSubscriberLocked(clientId, propIdAreaId, option.sampleRate);
            } else {
                result = addOnChangeSubscriberLocked(propIdAreaId);
            }

            if (!result.ok()) {
                return result;
            }

            mSubscribedPropsByClient[clientId].insert(propIdAreaId);
            mClientsByPropIdArea[propIdAreaId][clientId] = callback;
            mClientsByPropIdAreaId[propIdAreaId][clientId] = callback;
        }
    }
    return {};
}

VhalResult<void> SubscriptionManager::unsubscribePropIdAreaIdLocked(
        SubscriptionManager::ClientIdType clientId, const PropIdAreaId& propIdAreaId) {
    if (mContSubConfigsByPropIdArea.find(propIdAreaId) != mContSubConfigsByPropIdArea.end()) {
        // This is a subscribed continuous property.
        if (auto result = removeContinuousSubscriberLocked(clientId, propIdAreaId); !result.ok()) {
            return result;
        }
    } else {
        if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
            ALOGW("Unsubscribe: The property: %s, areaId: %" PRId32
                  " was not previously subscribed, do nothing",
                  propIdToString(propIdAreaId.propId).c_str(), propIdAreaId.areaId);
            return {};
        }
        // This is an on-change property.
        if (auto result = removeOnChangeSubscriberLocked(propIdAreaId); !result.ok()) {
            return result;
        }
    }

    auto& clients = mClientsByPropIdAreaId[propIdAreaId];
    clients.erase(clientId);
    if (clients.empty()) {
        mClientsByPropIdAreaId.erase(propIdAreaId);
        mContSubConfigsByPropIdArea.erase(propIdAreaId);
    }
    return {};
}
@@ -186,39 +281,27 @@ VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdT
        return StatusError(StatusCode::INVALID_ARG)
               << "No property was subscribed for the callback";
    }
    std::unordered_set<int32_t> subscribedPropIds;
    for (auto const& propIdAreaId : mSubscribedPropsByClient[clientId]) {
        subscribedPropIds.insert(propIdAreaId.propId);
    }

    std::vector<PropIdAreaId> propIdAreaIdsToUnsubscribe;
    std::unordered_set<int32_t> propIdSet;
    for (int32_t propId : propIds) {
        if (subscribedPropIds.find(propId) == subscribedPropIds.end()) {
            return StatusError(StatusCode::INVALID_ARG)
                   << "property ID: " << propId << " is not subscribed";
        propIdSet.insert(propId);
    }
    auto& subscribedPropIdsAreaIds = mSubscribedPropsByClient[clientId];
    for (const auto& propIdAreaId : subscribedPropIdsAreaIds) {
        if (propIdSet.find(propIdAreaId.propId) != propIdSet.end()) {
            propIdAreaIdsToUnsubscribe.push_back(propIdAreaId);
        }

    auto& propIdAreaIds = mSubscribedPropsByClient[clientId];
    auto it = propIdAreaIds.begin();
    while (it != propIdAreaIds.end()) {
        int32_t propId = it->propId;
        if (std::find(propIds.begin(), propIds.end(), propId) != propIds.end()) {
            if (auto result = removeContinuousSubscriberLocked(clientId, *it); !result.ok()) {
                return result;
    }

            auto& clients = mClientsByPropIdArea[*it];
            clients.erase(clientId);
            if (clients.empty()) {
                mClientsByPropIdArea.erase(*it);
                mContSubConfigsByPropIdArea.erase(*it);
            }
            it = propIdAreaIds.erase(it);
        } else {
            it++;
    for (const auto& propIdAreaId : propIdAreaIdsToUnsubscribe) {
        if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
            return result;
        }
        subscribedPropIdsAreaIds.erase(propIdAreaId);
    }
    if (propIdAreaIds.empty()) {

    if (subscribedPropIdsAreaIds.empty()) {
        mSubscribedPropsByClient.erase(clientId);
    }
    return {};
@@ -233,16 +316,9 @@ VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdT

    auto& subscriptions = mSubscribedPropsByClient[clientId];
    for (auto const& propIdAreaId : subscriptions) {
        if (auto result = removeContinuousSubscriberLocked(clientId, propIdAreaId); !result.ok()) {
        if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
            return result;
        }

        auto& clients = mClientsByPropIdArea[propIdAreaId];
        clients.erase(clientId);
        if (clients.empty()) {
            mClientsByPropIdArea.erase(propIdAreaId);
            mContSubConfigsByPropIdArea.erase(propIdAreaId);
        }
    }
    mSubscribedPropsByClient.erase(clientId);
    return {};
@@ -258,11 +334,11 @@ SubscriptionManager::getSubscribedClients(std::vector<VehiclePropValue>&& update
                .propId = value.prop,
                .areaId = value.areaId,
        };
        if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) {
        if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
            continue;
        }

        for (const auto& [_, client] : mClientsByPropIdArea[propIdAreaId]) {
        for (const auto& [_, client] : mClientsByPropIdAreaId[propIdAreaId]) {
            clients[client].push_back(value);
        }
    }
@@ -280,11 +356,11 @@ SubscriptionManager::getSubscribedClientsForErrorEvents(
                .propId = errorEvent.propId,
                .areaId = errorEvent.areaId,
        };
        if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) {
        if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
            continue;
        }

        for (const auto& [_, client] : mClientsByPropIdArea[propIdAreaId]) {
        for (const auto& [_, client] : mClientsByPropIdAreaId[propIdAreaId]) {
            clients[client].push_back({
                    .propId = errorEvent.propId,
                    .areaId = errorEvent.areaId,
@@ -297,7 +373,7 @@ SubscriptionManager::getSubscribedClientsForErrorEvents(

bool SubscriptionManager::isEmpty() {
    std::scoped_lock<std::mutex> lockGuard(mLock);
    return mSubscribedPropsByClient.empty() && mClientsByPropIdArea.empty();
    return mSubscribedPropsByClient.empty() && mClientsByPropIdAreaId.empty();
}

size_t SubscriptionManager::countClients() {
+57 −6

File changed.

Preview size limit exceeded, changes collapsed.

Loading