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

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

Merge "Update subscription logic for VHAL ref impl." into tm-dev

parents a247d9f8 5a06465d
Loading
Loading
Loading
Loading
+42 −4
Original line number Diff line number Diff line
@@ -90,6 +90,14 @@ interface IVehicle {
     * area ID) are not allowed in a single call. This function must return
     * {@link StatusCode#INVALID_ARG} for duplicate properties.
     *
     * The {@link VehiclePropValue#timestamp} field in request is ignored. The
     * {@link VehiclePropValue#timestamp} field in {@link GetValueResult} must
     * be the system uptime since boot when the value changes for
     * ON_CHANGE property or when the value is checked according to polling rate
     * for CONTINUOUS property. Note that for CONTINUOUS property, VHAL client
     * reading the property multiple times between the polling interval will get
     * the same timestamp.
     *
     * @param callback A callback interface, whose 'onGetValues' would be called
     *    after the value is fetched. Caller should use
     *    {@code android-automotive-large-parcelable} library to parse the
@@ -104,7 +112,7 @@ interface IVehicle {
     * Set vehicle property values.
     *
     * The {@link IVehicleCallback#onSetValues} function would be called after
     * the values set request are sent through vehicle bus or are failed to set.
     * the values set request are sent through vehicle bus or failed to set.
     * If the bus protocol supports confirmation, the callback would be called
     * after getting the confirmation.
     *
@@ -152,11 +160,36 @@ interface IVehicle {
     * Clients must be able to subscribe to multiple properties at a time
     * depending on data provided in options argument.
     *
     * For one callback, the is only one subscription for one property.
     * For one callback, there is only one subscription for one property.
     * A new subscription with a different sample rate would override the old
     * subscription. One property could be subscribed multiple times for
     * different callbacks.
     *
     * If error is returned, some of the properties failed to subscribe.
     * Caller is safe to try again, since subscribing to an already subscribed
     * property is okay.
     *
     * The specified sample rate is just a guidance. It is not guaranteed that
     * the sample rate is achievable depending on how the polling refresh rate
     * is. The actual property event rate might be higher/lower than the
     * specified sampleRate, for example, if the polling rate can be 5 times/s
     * or 10 times/s, subscribing to a sample rate of 7 might use the 5 times/s
     * polling rate, thus generating 5 events/s. We only require that on
     * average, the {@code minSampleRate} and {@code maxSampleRate} can be
     * achieved, all the sampleRate within min and max would on average
     * generates events with rate >= {@code minSampleRate} and <=
     * {@code maxSampleRate}.
     *
     * The {@link VehiclePropValue#timestamp} field for each property event must
     * be the system uptime since boot when the value changes for
     * ON_CHANGE property or when the value is checked according to polling rate
     * for CONTINUOUS property. Note that for CONTINUOUS property, VHAL client
     * reading the property multiple times between the polling interval will get
     * the same timestamp.
     * For example, if the polling rate for a property is 10 times/s, no matter
     * what the sampleRate specified in {@code options}, the timestamp for
     * the timestamp is updated 10 times/s.
     *
     * @param callback The subscription callbacks.
     *    {@link IVehicleCallback#onPropertyEvent} would be called when a new
     *    property event arrives.
@@ -189,8 +222,13 @@ interface IVehicle {
    /**
     * Unsubscribes from property events.
     *
     * If 'callback' is not valid or 'propIds' were not subscribed for this
     * 'callback', this method must return {@link StatusCode#INVALID_ARG}.
     * If 'callback' is not valid this method must return
     * {@link StatusCode#INVALID_ARG}. If a specified propId was not subscribed
     * before, this method must ignore that propId.
     *
     * If error is returned, some of the properties failed to unsubscribe.
     * Caller is safe to try again, since unsubscribing an already unsubscribed
     * property is okay.
     *
     * @param callback The callback used in the previous subscription.
     * @param propIds The IDs for the properties to unsubscribe.
+12 −5
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <FakeObd2Frame.h>
#include <FakeUserHal.h>
#include <IVehicleHardware.h>
#include <RecurrentTimer.h>
#include <VehicleHalTypes.h>
#include <VehiclePropertyStore.h>
#include <android-base/parseint.h>
@@ -82,6 +83,10 @@ class FakeVehicleHardware : public IVehicleHardware {
    void registerOnPropertySetErrorEvent(
            std::unique_ptr<const PropertySetErrorCallback> callback) override;

    // Update the sample rate for the [propId, areaId] pair.
    aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
            int32_t propId, int32_t areaId, float sampleRate) override;

  protected:
    // mValuePool is also used in mServerSidePropStore.
    const std::shared_ptr<VehiclePropValuePool> mValuePool;
@@ -99,11 +104,13 @@ class FakeVehicleHardware : public IVehicleHardware {

    const std::unique_ptr<obd2frame::FakeObd2Frame> mFakeObd2Frame;
    const std::unique_ptr<FakeUserHal> mFakeUserHal;
    std::mutex mCallbackLock;
    std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback
            GUARDED_BY(mCallbackLock);
    std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback
            GUARDED_BY(mCallbackLock);
    // RecurrentTimer is thread-safe.
    std::unique_ptr<RecurrentTimer> mRecurrentTimer;
    std::mutex mLock;
    std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback GUARDED_BY(mLock);
    std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback GUARDED_BY(mLock);
    std::unordered_map<PropIdAreaId, std::shared_ptr<RecurrentTimer::Callback>, PropIdAreaIdHash>
            mRecurrentActions GUARDED_BY(mLock);

    void init();
    // Stores the initial value to property store.
+46 −10
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ using ::android::base::EqualsIgnoreCase;
using ::android::base::Error;
using ::android::base::ParseFloat;
using ::android::base::Result;
using ::android::base::ScopedLockAssertion;
using ::android::base::StartsWith;
using ::android::base::StringPrintf;

@@ -131,18 +132,14 @@ void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDecla
}

FakeVehicleHardware::FakeVehicleHardware()
    : mValuePool(new VehiclePropValuePool),
      mServerSidePropStore(new VehiclePropertyStore(mValuePool)),
      mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)),
      mFakeUserHal(new FakeUserHal(mValuePool)) {
    init();
}
    : FakeVehicleHardware(std::make_unique<VehiclePropValuePool>()) {}

FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool)
    : mValuePool(std::move(valuePool)),
      mServerSidePropStore(new VehiclePropertyStore(mValuePool)),
      mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)),
      mFakeUserHal(new FakeUserHal(mValuePool)) {
      mFakeUserHal(new FakeUserHal(mValuePool)),
      mRecurrentTimer(new RecurrentTimer()) {
    init();
}

@@ -837,18 +834,57 @@ StatusCode FakeVehicleHardware::checkHealth() {

void FakeVehicleHardware::registerOnPropertyChangeEvent(
        std::unique_ptr<const PropertyChangeCallback> callback) {
    std::scoped_lock<std::mutex> lockGuard(mCallbackLock);
    std::scoped_lock<std::mutex> lockGuard(mLock);
    mOnPropertyChangeCallback = std::move(callback);
}

void FakeVehicleHardware::registerOnPropertySetErrorEvent(
        std::unique_ptr<const PropertySetErrorCallback> callback) {
    std::scoped_lock<std::mutex> lockGuard(mCallbackLock);
    std::scoped_lock<std::mutex> lockGuard(mLock);
    mOnPropertySetErrorCallback = std::move(callback);
}

StatusCode FakeVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, float sampleRate) {
    // DefaultVehicleHal makes sure that sampleRate must be within minSampleRate and maxSampleRate.
    // For fake implementation, we would write the same value with a new timestamp into propStore
    // at sample rate.
    std::scoped_lock<std::mutex> lockGuard(mLock);

    PropIdAreaId propIdAreaId{
            .propId = propId,
            .areaId = areaId,
    };
    if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) {
        mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]);
    }
    if (sampleRate == 0) {
        return StatusCode::OK;
    }
    int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate);
    auto action = std::make_shared<RecurrentTimer::Callback>([this, propId, areaId] {
        // Refresh the property value. In real implementation, this should poll the latest value
        // from vehicle bus. Here, we are just refreshing the existing value with a new timestamp.
        auto result = getValue(VehiclePropValue{
                .prop = propId,
                .areaId = areaId,
        });
        if (!result.ok()) {
            // Failed to read current value, skip refreshing.
            return;
        }
        result.value()->timestamp = elapsedRealtimeNano();
        // Must remove the value before writing, otherwise, we would generate no update event since
        // the value is the same.
        mServerSidePropStore->removeValue(*result.value());
        mServerSidePropStore->writeValue(std::move(result.value()));
    });
    mRecurrentTimer->registerTimerCallback(interval, action);
    mRecurrentActions[propIdAreaId] = action;
    return StatusCode::OK;
}

void FakeVehicleHardware::onValueChangeCallback(const VehiclePropValue& value) {
    std::scoped_lock<std::mutex> lockGuard(mCallbackLock);
    std::scoped_lock<std::mutex> lockGuard(mLock);

    if (mOnPropertyChangeCallback == nullptr) {
        return;
+80 −7
Original line number Diff line number Diff line
@@ -25,12 +25,15 @@
#include <android-base/expected.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>

#include <inttypes.h>
#include <chrono>
#include <condition_variable>
#include <vector>

namespace android {
@@ -53,6 +56,7 @@ using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::android::base::expected;
using ::android::base::ScopedLockAssertion;
using ::android::base::StringPrintf;
using ::android::base::unexpected;
using ::testing::ContainerEq;
@@ -60,6 +64,8 @@ using ::testing::ContainsRegex;
using ::testing::Eq;
using ::testing::WhenSortedBy;

using std::chrono::milliseconds;

constexpr int INVALID_PROP_ID = 0;
constexpr char CAR_MAKE[] = "Default Car";

@@ -158,30 +164,65 @@ class FakeVehicleHardwareTest : public ::testing::Test {
    }

    void onSetValues(std::vector<SetValueResult> results) {
        std::scoped_lock<std::mutex> lockGuard(mLock);
        for (auto& result : results) {
            mSetValueResults.push_back(result);
        }
    }

    const std::vector<SetValueResult>& getSetValueResults() { return mSetValueResults; }
    const std::vector<SetValueResult>& getSetValueResults() {
        std::scoped_lock<std::mutex> lockGuard(mLock);
        return mSetValueResults;
    }

    void onGetValues(std::vector<GetValueResult> results) {
        std::scoped_lock<std::mutex> lockGuard(mLock);
        for (auto& result : results) {
            mGetValueResults.push_back(result);
        }
    }

    const std::vector<GetValueResult>& getGetValueResults() { return mGetValueResults; }
    const std::vector<GetValueResult>& getGetValueResults() {
        std::scoped_lock<std::mutex> lockGuard(mLock);
        return mGetValueResults;
    }

    void onPropertyChangeEvent(std::vector<VehiclePropValue> values) {
        std::scoped_lock<std::mutex> lockGuard(mLock);
        for (auto& value : values) {
            mChangedProperties.push_back(value);
            PropIdAreaId propIdAreaId{
                    .propId = value.prop,
                    .areaId = value.areaId,
            };
            mEventCount[propIdAreaId]++;
        }
        mCv.notify_one();
    }

    const std::vector<VehiclePropValue>& getChangedProperties() {
        std::scoped_lock<std::mutex> lockGuard(mLock);
        return mChangedProperties;
    }

    const std::vector<VehiclePropValue>& getChangedProperties() { return mChangedProperties; }
    bool waitForChangedProperties(int32_t propId, int32_t areaId, size_t count,
                                  milliseconds timeout) {
        PropIdAreaId propIdAreaId{
                .propId = propId,
                .areaId = areaId,
        };
        std::unique_lock<std::mutex> lk(mLock);
        return mCv.wait_for(lk, timeout, [this, propIdAreaId, count] {
            ScopedLockAssertion lockAssertion(mLock);
            return mEventCount[propIdAreaId] >= count;
        });
    }

    void clearChangedProperties() { mChangedProperties.clear(); }
    void clearChangedProperties() {
        std::scoped_lock<std::mutex> lockGuard(mLock);
        mEventCount.clear();
        mChangedProperties.clear();
    }

    static void addSetValueRequest(std::vector<SetValueRequest>& requests,
                                   std::vector<SetValueResult>& expectedResults, int64_t requestId,
@@ -246,11 +287,14 @@ class FakeVehicleHardwareTest : public ::testing::Test {

  private:
    FakeVehicleHardware mHardware;
    std::vector<SetValueResult> mSetValueResults;
    std::vector<GetValueResult> mGetValueResults;
    std::vector<VehiclePropValue> mChangedProperties;
    std::shared_ptr<IVehicleHardware::SetValuesCallback> mSetValuesCallback;
    std::shared_ptr<IVehicleHardware::GetValuesCallback> mGetValuesCallback;
    std::condition_variable mCv;
    std::mutex mLock;
    std::unordered_map<PropIdAreaId, size_t, PropIdAreaIdHash> mEventCount GUARDED_BY(mLock);
    std::vector<SetValueResult> mSetValueResults GUARDED_BY(mLock);
    std::vector<GetValueResult> mGetValueResults GUARDED_BY(mLock);
    std::vector<VehiclePropValue> mChangedProperties GUARDED_BY(mLock);
};

TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs) {
@@ -1510,6 +1554,35 @@ TEST_F(FakeVehicleHardwareTest, testGetEchoReverseBytes) {
    ASSERT_EQ(result.value().value.byteValues, std::vector<uint8_t>({0x04, 0x03, 0x02, 0x01}));
}

TEST_F(FakeVehicleHardwareTest, testUpdateSampleRate) {
    int32_t propSpeed = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
    int32_t propSteering = toInt(VehicleProperty::PERF_STEERING_ANGLE);
    int32_t areaId = 0;
    getHardware()->updateSampleRate(propSpeed, areaId, 5);

    ASSERT_TRUE(waitForChangedProperties(propSpeed, areaId, /*count=*/5, milliseconds(1500)))
            << "not enough events generated for speed";

    getHardware()->updateSampleRate(propSteering, areaId, 10);

    ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/10, milliseconds(1500)))
            << "not enough events generated for steering";

    int64_t timestamp = elapsedRealtimeNano();
    // Disable refreshing for propSpeed.
    getHardware()->updateSampleRate(propSpeed, areaId, 0);
    clearChangedProperties();

    ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/5, milliseconds(1500)))
            << "should still receive steering events after disable polling for speed";
    auto updatedValues = getChangedProperties();
    for (auto& value : updatedValues) {
        ASSERT_GE(value.timestamp, timestamp);
        ASSERT_EQ(value.prop, propSteering);
        ASSERT_EQ(value.areaId, areaId);
    }
}

}  // namespace fake
}  // namespace vehicle
}  // namespace automotive
+29 −0
Original line number Diff line number Diff line
@@ -80,6 +80,35 @@ 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;

Loading