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

Commit 6dc14b8e authored by Yu Shan's avatar Yu Shan Committed by Android (Google) Code Review
Browse files

Merge "Add permission check and heartbeat event to VHAL."

parents e0a93293 d110eda7
Loading
Loading
Loading
Loading
+17 −0
Original line number Original line Diff line number Diff line
@@ -126,6 +126,8 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve
    // TODO(b/214605968): define TIMEOUT_IN_NANO in IVehicle and allow getValues/setValues/subscribe
    // TODO(b/214605968): define TIMEOUT_IN_NANO in IVehicle and allow getValues/setValues/subscribe
    // to specify custom timeouts.
    // to specify custom timeouts.
    static constexpr int64_t TIMEOUT_IN_NANO = 30'000'000'000;
    static constexpr int64_t TIMEOUT_IN_NANO = 30'000'000'000;
    // heart beat event interval: 3s
    static constexpr int64_t HEART_BEAT_INTERVAL_IN_NANO = 3'000'000'000;
    const std::shared_ptr<IVehicleHardware> mVehicleHardware;
    const std::shared_ptr<IVehicleHardware> mVehicleHardware;


    // mConfigsByPropId and mConfigFile are only modified during initialization, so no need to
    // mConfigsByPropId and mConfigFile are only modified during initialization, so no need to
@@ -146,6 +148,8 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve
            GUARDED_BY(mLock);
            GUARDED_BY(mLock);
    // SubscriptionClients is thread-safe.
    // SubscriptionClients is thread-safe.
    std::shared_ptr<SubscriptionClients> mSubscriptionClients;
    std::shared_ptr<SubscriptionClients> mSubscriptionClients;
    // RecurrentTimer is thread-safe.
    RecurrentTimer mRecurrentTimer;


    ::android::base::Result<void> checkProperty(
    ::android::base::Result<void> checkProperty(
            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue);
            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue);
@@ -162,6 +166,16 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve
            const std::vector<::aidl::android::hardware::automotive::vehicle::SubscribeOptions>&
            const std::vector<::aidl::android::hardware::automotive::vehicle::SubscribeOptions>&
                    options);
                    options);


    ::android::base::Result<void> checkReadPermission(
            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;

    ::android::base::Result<void> checkWritePermission(
            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;

    ::android::base::Result<
            const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig*>
    getConfig(int32_t propId) const;

    template <class T>
    template <class T>
    static std::shared_ptr<T> getOrCreateClient(
    static std::shared_ptr<T> getOrCreateClient(
            std::unordered_map<CallbackType, std::shared_ptr<T>>* clients,
            std::unordered_map<CallbackType, std::shared_ptr<T>>* clients,
@@ -178,6 +192,9 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve
            const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
            const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
                    updatedValues);
                    updatedValues);


    static void checkHealth(std::weak_ptr<IVehicleHardware> hardware,
                            std::weak_ptr<SubscriptionManager> subscriptionManager);

    // Test-only
    // Test-only
    // Set the default timeout for pending requests.
    // Set the default timeout for pending requests.
    void setTimeout(int64_t timeoutInNano);
    void setTimeout(int64_t timeoutInNano);
+151 −25
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@
#include <android-base/result.h>
#include <android-base/result.h>
#include <android-base/stringprintf.h>
#include <android-base/stringprintf.h>
#include <utils/Log.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>


#include <inttypes.h>
#include <inttypes.h>
#include <set>
#include <set>
@@ -51,7 +52,10 @@ using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::android::automotive::car_binder_lib::LargeParcelableBase;
using ::android::automotive::car_binder_lib::LargeParcelableBase;
using ::android::base::Error;
using ::android::base::Error;
@@ -128,6 +132,13 @@ DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware)
                    [subscriptionManagerCopy](std::vector<VehiclePropValue> updatedValues) {
                    [subscriptionManagerCopy](std::vector<VehiclePropValue> updatedValues) {
                        onPropertyChangeEvent(subscriptionManagerCopy, updatedValues);
                        onPropertyChangeEvent(subscriptionManagerCopy, updatedValues);
                    }));
                    }));

    // Register heartbeat event.
    mRecurrentTimer.registerTimerCallback(
            HEART_BEAT_INTERVAL_IN_NANO,
            std::make_shared<std::function<void()>>([hardwareCopy, subscriptionManagerCopy]() {
                checkHealth(hardwareCopy, subscriptionManagerCopy);
            }));
}
}


void DefaultVehicleHal::onPropertyChangeEvent(
void DefaultVehicleHal::onPropertyChangeEvent(
@@ -222,27 +233,35 @@ ScopedAStatus DefaultVehicleHal::getAllPropConfigs(VehiclePropConfigs* output) {
    return ScopedAStatus::ok();
    return ScopedAStatus::ok();
}
}


Result<void> DefaultVehicleHal::checkProperty(const VehiclePropValue& propValue) {
Result<const VehiclePropConfig*> DefaultVehicleHal::getConfig(int32_t propId) const {
    int32_t propId = propValue.prop;
    auto it = mConfigsByPropId.find(propId);
    auto it = mConfigsByPropId.find(propId);
    if (it == mConfigsByPropId.end()) {
    if (it == mConfigsByPropId.end()) {
        return Error() << "no config for property, ID: " << propId;
        return Error() << "no config for property, ID: " << propId;
    }
    }
    const VehiclePropConfig& config = it->second;
    return &(it->second);
    const VehicleAreaConfig* areaConfig = getAreaConfig(propValue, config);
}

Result<void> DefaultVehicleHal::checkProperty(const VehiclePropValue& propValue) {
    int32_t propId = propValue.prop;
    auto result = getConfig(propId);
    if (!result.ok()) {
        return result.error();
    }
    const VehiclePropConfig* config = result.value();
    const VehicleAreaConfig* areaConfig = getAreaConfig(propValue, *config);
    if (!isGlobalProp(propId) && areaConfig == nullptr) {
    if (!isGlobalProp(propId) && areaConfig == nullptr) {
        // Ignore areaId for global property. For non global property, check whether areaId is
        // Ignore areaId for global property. For non global property, check whether areaId is
        // allowed. areaId must appear in areaConfig.
        // allowed. areaId must appear in areaConfig.
        return Error() << "invalid area ID: " << propValue.areaId << " for prop ID: " << propId
        return Error() << "invalid area ID: " << propValue.areaId << " for prop ID: " << propId
                       << ", not listed in config";
                       << ", not listed in config";
    }
    }
    if (auto result = checkPropValue(propValue, &config); !result.ok()) {
    if (auto result = checkPropValue(propValue, config); !result.ok()) {
        return Error() << "invalid property value: " << propValue.toString()
        return Error() << "invalid property value: " << propValue.toString()
                       << ", error: " << result.error().message();
                       << ", error: " << getErrorMsg(result);
    }
    }
    if (auto result = checkValueRange(propValue, areaConfig); !result.ok()) {
    if (auto result = checkValueRange(propValue, areaConfig); !result.ok()) {
        return Error() << "property value out of range: " << propValue.toString()
        return Error() << "property value out of range: " << propValue.toString()
                       << ", error: " << result.error().message();
                       << ", error: " << getErrorMsg(result);
    }
    }
    return {};
    return {};
}
}
@@ -263,9 +282,30 @@ ScopedAStatus DefaultVehicleHal::getValues(const CallbackType& callback,
        ALOGE("getValues: duplicate request ID");
        ALOGE("getValues: duplicate request ID");
        return toScopedAStatus(maybeRequestIds, StatusCode::INVALID_ARG);
        return toScopedAStatus(maybeRequestIds, StatusCode::INVALID_ARG);
    }
    }

    // A list of failed result we already know before sending to hardware.
    std::vector<GetValueResult> failedResults;
    // The list of requests that we would send to hardware.
    std::vector<GetValueRequest> hardwareRequests;

    for (const auto& request : getValueRequests) {
        if (auto result = checkReadPermission(request.prop); !result.ok()) {
            ALOGW("property does not support reading: %s", getErrorMsg(result).c_str());
            failedResults.push_back(GetValueResult{
                    .requestId = request.requestId,
                    .status = getErrorCode(result),
                    .prop = {},
            });
        } else {
            hardwareRequests.push_back(request);
        }
    }

    // The set of request Ids that we would send to hardware.
    // The set of request Ids that we would send to hardware.
    std::unordered_set<int64_t> hardwareRequestIds(maybeRequestIds.value().begin(),
    std::unordered_set<int64_t> hardwareRequestIds;
                                                   maybeRequestIds.value().end());
    for (const auto& request : hardwareRequests) {
        hardwareRequestIds.insert(request.requestId);
    }


    std::shared_ptr<GetValuesClient> client;
    std::shared_ptr<GetValuesClient> client;
    {
    {
@@ -275,12 +315,21 @@ ScopedAStatus DefaultVehicleHal::getValues(const CallbackType& callback,
    // Register the pending hardware requests and also check for duplicate request Ids.
    // Register the pending hardware requests and also check for duplicate request Ids.
    if (auto addRequestResult = client->addRequests(hardwareRequestIds); !addRequestResult.ok()) {
    if (auto addRequestResult = client->addRequests(hardwareRequestIds); !addRequestResult.ok()) {
        ALOGE("getValues[%s]: failed to add pending requests, error: %s",
        ALOGE("getValues[%s]: failed to add pending requests, error: %s",
              toString(hardwareRequestIds).c_str(), addRequestResult.error().message().c_str());
              toString(hardwareRequestIds).c_str(), getErrorMsg(addRequestResult).c_str());
        return toScopedAStatus(addRequestResult);
        return toScopedAStatus(addRequestResult);
    }
    }


    if (!failedResults.empty()) {
        // First send the failed results we already know back to the client.
        client->sendResults(failedResults);
    }

    if (hardwareRequests.empty()) {
        return ScopedAStatus::ok();
    }

    if (StatusCode status =
    if (StatusCode status =
                mVehicleHardware->getValues(client->getResultCallback(), getValueRequests);
                mVehicleHardware->getValues(client->getResultCallback(), hardwareRequests);
        status != StatusCode::OK) {
        status != StatusCode::OK) {
        // If the hardware returns error, finish all the pending requests for this request because
        // If the hardware returns error, finish all the pending requests for this request because
        // we never expect hardware to call callback for these requests.
        // we never expect hardware to call callback for these requests.
@@ -317,9 +366,17 @@ ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback,


    for (auto& request : setValueRequests) {
    for (auto& request : setValueRequests) {
        int64_t requestId = request.requestId;
        int64_t requestId = request.requestId;
        if (auto result = checkWritePermission(request.value); !result.ok()) {
            ALOGW("property does not support writing: %s", getErrorMsg(result).c_str());
            failedResults.push_back(SetValueResult{
                    .requestId = requestId,
                    .status = getErrorCode(result),
            });
            continue;
        }
        if (auto result = checkProperty(request.value); !result.ok()) {
        if (auto result = checkProperty(request.value); !result.ok()) {
            ALOGW("setValues[%" PRId64 "]: property not valid: %s", requestId,
            ALOGW("setValues[%" PRId64 "]: property is not valid: %s", requestId,
                  result.error().message().c_str());
                  getErrorMsg(result).c_str());
            failedResults.push_back(SetValueResult{
            failedResults.push_back(SetValueResult{
                    .requestId = requestId,
                    .requestId = requestId,
                    .status = StatusCode::INVALID_ARG,
                    .status = StatusCode::INVALID_ARG,
@@ -345,7 +402,7 @@ ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback,
    // Register the pending hardware requests and also check for duplicate request Ids.
    // Register the pending hardware requests and also check for duplicate request Ids.
    if (auto addRequestResult = client->addRequests(hardwareRequestIds); !addRequestResult.ok()) {
    if (auto addRequestResult = client->addRequests(hardwareRequestIds); !addRequestResult.ok()) {
        ALOGE("setValues[%s], failed to add pending requests, error: %s",
        ALOGE("setValues[%s], failed to add pending requests, error: %s",
              toString(hardwareRequestIds).c_str(), addRequestResult.error().message().c_str());
              toString(hardwareRequestIds).c_str(), getErrorMsg(addRequestResult).c_str());
        return toScopedAStatus(addRequestResult, StatusCode::INVALID_ARG);
        return toScopedAStatus(addRequestResult, StatusCode::INVALID_ARG);
    }
    }


@@ -354,6 +411,10 @@ ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback,
        client->sendResults(failedResults);
        client->sendResults(failedResults);
    }
    }


    if (hardwareRequests.empty()) {
        return ScopedAStatus::ok();
    }

    if (StatusCode status =
    if (StatusCode status =
                mVehicleHardware->setValues(client->getResultCallback(), hardwareRequests);
                mVehicleHardware->setValues(client->getResultCallback(), hardwareRequests);
        status != StatusCode::OK) {
        status != StatusCode::OK) {
@@ -413,13 +474,21 @@ Result<void> DefaultVehicleHal::checkSubscribeOptions(
    for (const auto& option : options) {
    for (const auto& option : options) {
        int32_t propId = option.propId;
        int32_t propId = option.propId;
        if (mConfigsByPropId.find(propId) == mConfigsByPropId.end()) {
        if (mConfigsByPropId.find(propId) == mConfigsByPropId.end()) {
            return Error() << StringPrintf("no config for property, ID: %" PRId32, propId);
            return Error(toInt(StatusCode::INVALID_ARG))
                   << StringPrintf("no config for property, ID: %" PRId32, propId);
        }
        }
        const VehiclePropConfig& config = mConfigsByPropId[propId];
        const VehiclePropConfig& config = mConfigsByPropId[propId];


        if (config.changeMode != VehiclePropertyChangeMode::ON_CHANGE &&
        if (config.changeMode != VehiclePropertyChangeMode::ON_CHANGE &&
            config.changeMode != VehiclePropertyChangeMode::CONTINUOUS) {
            config.changeMode != VehiclePropertyChangeMode::CONTINUOUS) {
            return Error() << "only support subscribing to ON_CHANGE or CONTINUOUS property";
            return Error(toInt(StatusCode::INVALID_ARG))
                   << "only support subscribing to ON_CHANGE or CONTINUOUS property";
        }

        if (config.access != VehiclePropertyAccess::READ &&
            config.access != VehiclePropertyAccess::READ_WRITE) {
            return Error(toInt(StatusCode::ACCESS_DENIED))
                   << StringPrintf("Property %" PRId32 " has no read access", propId);
        }
        }


        if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) {
        if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) {
@@ -427,12 +496,13 @@ Result<void> DefaultVehicleHal::checkSubscribeOptions(
            float minSampleRate = config.minSampleRate;
            float minSampleRate = config.minSampleRate;
            float maxSampleRate = config.maxSampleRate;
            float maxSampleRate = config.maxSampleRate;
            if (sampleRate < minSampleRate || sampleRate > maxSampleRate) {
            if (sampleRate < minSampleRate || sampleRate > maxSampleRate) {
                return Error() << StringPrintf(
                return Error(toInt(StatusCode::INVALID_ARG))
                               "sample rate: %f out of range, must be within %f and %f", sampleRate,
                       << StringPrintf("sample rate: %f out of range, must be within %f and %f",
                               minSampleRate, maxSampleRate);
                                       sampleRate, minSampleRate, maxSampleRate);
            }
            }
            if (!SubscriptionManager::checkSampleRate(sampleRate)) {
            if (!SubscriptionManager::checkSampleRate(sampleRate)) {
                return Error() << "invalid sample rate: " << sampleRate;
                return Error(toInt(StatusCode::INVALID_ARG))
                       << "invalid sample rate: " << sampleRate;
            }
            }
        }
        }


@@ -443,7 +513,8 @@ Result<void> DefaultVehicleHal::checkSubscribeOptions(
        // Non-global property.
        // Non-global property.
        for (int32_t areaId : option.areaIds) {
        for (int32_t areaId : option.areaIds) {
            if (auto areaConfig = getAreaConfig(propId, areaId, config); areaConfig == nullptr) {
            if (auto areaConfig = getAreaConfig(propId, areaId, config); areaConfig == nullptr) {
                return Error() << StringPrintf("invalid area ID: %" PRId32 " for prop ID: %" PRId32
                return Error(toInt(StatusCode::INVALID_ARG))
                       << StringPrintf("invalid area ID: %" PRId32 " for prop ID: %" PRId32
                                       ", not listed in config",
                                       ", not listed in config",
                                       areaId, propId);
                                       areaId, propId);
            }
            }
@@ -457,8 +528,8 @@ ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType& callback,
                                           [[maybe_unused]] int32_t maxSharedMemoryFileCount) {
                                           [[maybe_unused]] int32_t maxSharedMemoryFileCount) {
    // TODO(b/205189110): Use shared memory file count.
    // TODO(b/205189110): Use shared memory file count.
    if (auto result = checkSubscribeOptions(options); !result.ok()) {
    if (auto result = checkSubscribeOptions(options); !result.ok()) {
        ALOGE("subscribe: invalid subscribe options: %s", result.error().message().c_str());
        ALOGE("subscribe: invalid subscribe options: %s", getErrorMsg(result).c_str());
        return toScopedAStatus(result, StatusCode::INVALID_ARG);
        return toScopedAStatus(result);
    }
    }


    std::vector<SubscribeOptions> onChangeSubscriptions;
    std::vector<SubscribeOptions> onChangeSubscriptions;
@@ -513,6 +584,61 @@ IVehicleHardware* DefaultVehicleHal::getHardware() {
    return mVehicleHardware.get();
    return mVehicleHardware.get();
}
}


Result<void> DefaultVehicleHal::checkWritePermission(const VehiclePropValue& value) const {
    int32_t propId = value.prop;
    auto result = getConfig(propId);
    if (!result.ok()) {
        return Error(toInt(StatusCode::INVALID_ARG)) << getErrorMsg(result);
    }
    const VehiclePropConfig* config = result.value();

    if (config->access != VehiclePropertyAccess::WRITE &&
        config->access != VehiclePropertyAccess::READ_WRITE) {
        return Error(toInt(StatusCode::ACCESS_DENIED))
               << StringPrintf("Property %" PRId32 " has no write access", propId);
    }
    return {};
}

Result<void> DefaultVehicleHal::checkReadPermission(const VehiclePropValue& value) const {
    int32_t propId = value.prop;
    auto result = getConfig(propId);
    if (!result.ok()) {
        return Error(toInt(StatusCode::INVALID_ARG)) << getErrorMsg(result);
    }
    const VehiclePropConfig* config = result.value();

    if (config->access != VehiclePropertyAccess::READ &&
        config->access != VehiclePropertyAccess::READ_WRITE) {
        return Error(toInt(StatusCode::ACCESS_DENIED))
               << StringPrintf("Property %" PRId32 " has no read access", propId);
    }
    return {};
}

void DefaultVehicleHal::checkHealth(std::weak_ptr<IVehicleHardware> hardware,
                                    std::weak_ptr<SubscriptionManager> subscriptionManager) {
    auto hardwarePtr = hardware.lock();
    if (hardwarePtr == nullptr) {
        ALOGW("the VehicleHardware is destroyed, DefaultVehicleHal is ending");
        return;
    }

    StatusCode status = hardwarePtr->checkHealth();
    if (status != StatusCode::OK) {
        ALOGE("VHAL check health returns non-okay status");
        return;
    }
    std::vector<VehiclePropValue> values = {{
            .prop = toInt(VehicleProperty::VHAL_HEARTBEAT),
            .areaId = 0,
            .status = VehiclePropertyStatus::AVAILABLE,
            .value.int64Values = {uptimeMillis()},
    }};
    onPropertyChangeEvent(subscriptionManager, values);
    return;
}

}  // namespace vehicle
}  // namespace vehicle
}  // namespace automotive
}  // namespace automotive
}  // namespace hardware
}  // namespace hardware
+111 −1
Original line number Original line Diff line number Diff line
@@ -28,6 +28,7 @@
#include <gmock/gmock.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>


#include <chrono>
#include <chrono>
#include <list>
#include <list>
@@ -61,6 +62,8 @@ using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
@@ -87,6 +90,10 @@ constexpr int32_t GLOBAL_CONTINUOUS_PROP = 10003 + 0x10000000 + 0x01000000 + 0x0
constexpr int32_t AREA_ON_CHANGE_PROP = 10004 + 0x10000000 + 0x03000000 + 0x00400000;
constexpr int32_t AREA_ON_CHANGE_PROP = 10004 + 0x10000000 + 0x03000000 + 0x00400000;
// VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:INT32
// VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:INT32
constexpr int32_t AREA_CONTINUOUS_PROP = 10005 + 0x10000000 + 0x03000000 + 0x00400000;
constexpr int32_t AREA_CONTINUOUS_PROP = 10005 + 0x10000000 + 0x03000000 + 0x00400000;
// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
constexpr int32_t READ_ONLY_PROP = 10006 + 0x10000000 + 0x01000000 + 0x00400000;
// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
constexpr int32_t WRITE_ONLY_PROP = 10007 + 0x10000000 + 0x01000000 + 0x00400000;


int32_t testInt32VecProp(size_t i) {
int32_t testInt32VecProp(size_t i) {
    // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
    // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
@@ -145,6 +152,15 @@ std::vector<SetValuesInvalidRequestTestCase> getSetValuesInvalidRequestTestCases
                                    .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
                                    .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
                            },
                            },
                    .expectedStatus = StatusCode::INVALID_ARG,
                    .expectedStatus = StatusCode::INVALID_ARG,
            },
            {
                    .name = "no_write_permission",
                    .request =
                            {
                                    .prop = READ_ONLY_PROP,
                                    .value.int32Values = {0},
                            },
                    .expectedStatus = StatusCode::ACCESS_DENIED,
            }};
            }};
}
}


@@ -205,6 +221,7 @@ class DefaultVehicleHalTest : public ::testing::Test {
        for (size_t i = 0; i < 10000; i++) {
        for (size_t i = 0; i < 10000; i++) {
            testConfigs.push_back(VehiclePropConfig{
            testConfigs.push_back(VehiclePropConfig{
                    .prop = testInt32VecProp(i),
                    .prop = testInt32VecProp(i),
                    .access = VehiclePropertyAccess::READ_WRITE,
                    .areaConfigs =
                    .areaConfigs =
                            {
                            {
                                    {
                                    {
@@ -218,6 +235,7 @@ class DefaultVehicleHalTest : public ::testing::Test {
        // A property with area config.
        // A property with area config.
        testConfigs.push_back(
        testConfigs.push_back(
                VehiclePropConfig{.prop = INT32_WINDOW_PROP,
                VehiclePropConfig{.prop = INT32_WINDOW_PROP,
                                  .access = VehiclePropertyAccess::READ_WRITE,
                                  .areaConfigs = {{
                                  .areaConfigs = {{
                                          .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
                                          .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
                                          .minInt32Value = 0,
                                          .minInt32Value = 0,
@@ -226,11 +244,13 @@ class DefaultVehicleHalTest : public ::testing::Test {
        // A global on-change property.
        // A global on-change property.
        testConfigs.push_back(VehiclePropConfig{
        testConfigs.push_back(VehiclePropConfig{
                .prop = GLOBAL_ON_CHANGE_PROP,
                .prop = GLOBAL_ON_CHANGE_PROP,
                .access = VehiclePropertyAccess::READ_WRITE,
                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
        });
        });
        // A global continuous property.
        // A global continuous property.
        testConfigs.push_back(VehiclePropConfig{
        testConfigs.push_back(VehiclePropConfig{
                .prop = GLOBAL_CONTINUOUS_PROP,
                .prop = GLOBAL_CONTINUOUS_PROP,
                .access = VehiclePropertyAccess::READ_WRITE,
                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
                .minSampleRate = 0.0,
                .minSampleRate = 0.0,
                .maxSampleRate = 100.0,
                .maxSampleRate = 100.0,
@@ -238,6 +258,7 @@ class DefaultVehicleHalTest : public ::testing::Test {
        // A per-area on-change property.
        // A per-area on-change property.
        testConfigs.push_back(VehiclePropConfig{
        testConfigs.push_back(VehiclePropConfig{
                .prop = AREA_ON_CHANGE_PROP,
                .prop = AREA_ON_CHANGE_PROP,
                .access = VehiclePropertyAccess::READ_WRITE,
                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                .areaConfigs =
                .areaConfigs =
                        {
                        {
@@ -257,6 +278,7 @@ class DefaultVehicleHalTest : public ::testing::Test {
        // A per-area continuous property.
        // A per-area continuous property.
        testConfigs.push_back(VehiclePropConfig{
        testConfigs.push_back(VehiclePropConfig{
                .prop = AREA_CONTINUOUS_PROP,
                .prop = AREA_CONTINUOUS_PROP,
                .access = VehiclePropertyAccess::READ_WRITE,
                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
                .minSampleRate = 0.0,
                .minSampleRate = 0.0,
                .maxSampleRate = 1000.0,
                .maxSampleRate = 1000.0,
@@ -275,6 +297,28 @@ class DefaultVehicleHalTest : public ::testing::Test {
                                },
                                },
                        },
                        },
        });
        });
        // A read-only property.
        testConfigs.push_back(VehiclePropConfig{
                .prop = READ_ONLY_PROP,
                .access = VehiclePropertyAccess::READ,
                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
                .minSampleRate = 0.0,
                .maxSampleRate = 1000.0,
        });
        // A write-only property.
        testConfigs.push_back(VehiclePropConfig{
                .prop = WRITE_ONLY_PROP,
                .access = VehiclePropertyAccess::WRITE,
                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
                .minSampleRate = 0.0,
                .maxSampleRate = 1000.0,
        });
        // Register the heartbeat event property.
        testConfigs.push_back(VehiclePropConfig{
                .prop = toInt(VehicleProperty::VHAL_HEARTBEAT),
                .access = VehiclePropertyAccess::READ,
                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
        });
        hardware->setPropertyConfigs(testConfigs);
        hardware->setPropertyConfigs(testConfigs);
        mHardwarePtr = hardware.get();
        mHardwarePtr = hardware.get();
        mVhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
        mVhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
@@ -509,6 +553,39 @@ TEST_F(DefaultVehicleHalTest, testGetValuesInvalidLargeParcelableInput) {
    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG));
    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG));
}
}


TEST_F(DefaultVehicleHalTest, testGetValuesNoReadPermission) {
    GetValueRequests requests = {
            .sharedMemoryFd = {},
            .payloads =
                    {
                            {
                                    .requestId = 0,
                                    .prop =
                                            {
                                                    .prop = WRITE_ONLY_PROP,
                                            },
                            },
                    },
    };

    auto status = getClient()->getValues(getCallbackClient(), requests);

    ASSERT_TRUE(status.isOk()) << "getValue with no read permission should return okay with error "
                                  "returned from callback"
                               << ", error: " << status.getMessage();
    EXPECT_TRUE(getHardware()->nextGetValueRequests().empty()) << "expect no request to hardware";

    auto maybeResult = getCallback()->nextGetValueResults();
    ASSERT_TRUE(maybeResult.has_value()) << "no results in callback";
    EXPECT_EQ(maybeResult.value().payloads, std::vector<GetValueResult>({
                                                    {
                                                            .requestId = 0,
                                                            .status = StatusCode::ACCESS_DENIED,
                                                    },
                                            }))
            << "expect to get ACCESS_DENIED status if no read permission";
}

TEST_F(DefaultVehicleHalTest, testGetValuesFinishBeforeTimeout) {
TEST_F(DefaultVehicleHalTest, testGetValuesFinishBeforeTimeout) {
    // timeout: 0.1s
    // timeout: 0.1s
    int64_t timeout = 100000000;
    int64_t timeout = 100000000;
@@ -1318,7 +1395,7 @@ INSTANTIATE_TEST_SUITE_P(
            return info.param.name;
            return info.param.name;
        });
        });


TEST_P(SubscribeInvalidOptionsTest, testSubscribeInvalidRequest) {
TEST_P(SubscribeInvalidOptionsTest, testSubscribeInvalidOptions) {
    std::vector<SubscribeOptions> options = {GetParam().option};
    std::vector<SubscribeOptions> options = {GetParam().option};


    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
@@ -1327,6 +1404,17 @@ TEST_P(SubscribeInvalidOptionsTest, testSubscribeInvalidRequest) {
    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG));
    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG));
}
}


TEST_F(DefaultVehicleHalTest, testSubscribeNoReadPermission) {
    std::vector<SubscribeOptions> options = {{
            .propId = WRITE_ONLY_PROP,
    }};

    auto status = getClient()->subscribe(getCallbackClient(), options, 0);

    ASSERT_FALSE(status.isOk()) << "subscribe to a write-only property must fail";
    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::ACCESS_DENIED));
}

TEST_F(DefaultVehicleHalTest, testUnsubscribeFailure) {
TEST_F(DefaultVehicleHalTest, testUnsubscribeFailure) {
    auto status = getClient()->unsubscribe(getCallbackClient(),
    auto status = getClient()->unsubscribe(getCallbackClient(),
                                           std::vector<int32_t>({GLOBAL_ON_CHANGE_PROP}));
                                           std::vector<int32_t>({GLOBAL_ON_CHANGE_PROP}));
@@ -1335,6 +1423,28 @@ TEST_F(DefaultVehicleHalTest, testUnsubscribeFailure) {
    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG));
    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG));
}
}


TEST_F(DefaultVehicleHalTest, testHeartbeatEvent) {
    std::vector<SubscribeOptions> options = {{
            .propId = toInt(VehicleProperty::VHAL_HEARTBEAT),
    }};
    int64_t currentTime = uptimeMillis();
    auto status = getClient()->subscribe(getCallbackClient(), options, 0);

    ASSERT_TRUE(status.isOk()) << "unable to subscribe to heartbeat event: " << status.getMessage();

    // We send out a heartbeat event every 3s, so sleep for 3s.
    std::this_thread::sleep_for(std::chrono::seconds(3));

    auto maybeResults = getCallback()->nextOnPropertyEventResults();
    ASSERT_TRUE(maybeResults.has_value()) << "no results in callback";
    ASSERT_EQ(maybeResults.value().payloads.size(), static_cast<size_t>(1));
    VehiclePropValue gotValue = maybeResults.value().payloads[0];
    ASSERT_EQ(gotValue.prop, toInt(VehicleProperty::VHAL_HEARTBEAT));
    ASSERT_EQ(gotValue.value.int64Values.size(), static_cast<size_t>(1));
    ASSERT_GE(gotValue.value.int64Values[0], currentTime)
            << "expect to get the latest timestamp with the heartbeat event";
}

}  // namespace vehicle
}  // namespace vehicle
}  // namespace automotive
}  // namespace automotive
}  // namespace hardware
}  // namespace hardware
+1 −1
Original line number Original line Diff line number Diff line
@@ -185,7 +185,7 @@ TEST_F(SubscriptionManagerTest, testOverrideSubscriptionContinuous) {


    // Theoretically trigger 10 times, but check for at least 9 times to be stable.
    // Theoretically trigger 10 times, but check for at least 9 times to be stable.
    EXPECT_GE(getEvents().size(), static_cast<size_t>(9));
    EXPECT_GE(getEvents().size(), static_cast<size_t>(9));
    EXPECT_LE(getEvents().size(), static_cast<size_t>(11));
    EXPECT_LE(getEvents().size(), static_cast<size_t>(15));
}
}


TEST_F(SubscriptionManagerTest, testSubscribeMultipleAreasContinuous) {
TEST_F(SubscriptionManagerTest, testSubscribeMultipleAreasContinuous) {