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

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

Merge changes I11d5d0f1,I5f708479

* changes:
  Extend onDump to support debug options.
  Add input checks for Default VHAL.
parents f05e2cf9 795f4de0
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -197,6 +197,7 @@ cc_test {
    static_libs: [
        "libbase",
        "libcutils",
        "libgmock",
        "libjsoncpp",
        "libprotobuf-cpp-lite",
    ],
@@ -212,6 +213,7 @@ cc_test {
        "android.hardware.automotive.vehicle@2.0-default-impl-lib",
        "android.hardware.automotive.vehicle@2.0-libproto-native",
    ],
    data: [":vhal_test_json"],
    test_suites: ["general-tests"],
}

@@ -233,7 +235,7 @@ cc_binary {
    static_libs: [
        "android.hardware.automotive.vehicle@2.0-manager-lib",
        "android.hardware.automotive.vehicle@2.0-libproto-native",
        "//device/generic/car/emulator/vhal_v2_0:android.hardware.automotive.vehicle@2.0-emulator-impl-lib",
        "android.hardware.automotive.vehicle@2.0-emulator-impl-lib",
    ],
}

+193 −3
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
#define LOG_TAG "DefaultVehicleHal_v2_0"

#include <assert.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
#include <vhal_v2_0/RecurrentTimer.h>
@@ -72,6 +73,173 @@ bool DefaultVehicleHal::dump(const hidl_handle& fd, const hidl_vec<hidl_string>&
    return mVehicleClient->dump(fd, options);
}

StatusCode DefaultVehicleHal::checkPropValue(const VehiclePropValue& value,
                                             const VehiclePropConfig* config) {
    int32_t property = value.prop;
    VehiclePropertyType type = getPropType(property);
    switch (type) {
        case VehiclePropertyType::BOOLEAN:
        case VehiclePropertyType::INT32:
            if (value.value.int32Values.size() != 1) {
                return StatusCode::INVALID_ARG;
            }
            break;
        case VehiclePropertyType::INT32_VEC:
            if (value.value.int32Values.size() < 1) {
                return StatusCode::INVALID_ARG;
            }
            break;
        case VehiclePropertyType::INT64:
            if (value.value.int64Values.size() != 1) {
                return StatusCode::INVALID_ARG;
            }
            break;
        case VehiclePropertyType::INT64_VEC:
            if (value.value.int64Values.size() < 1) {
                return StatusCode::INVALID_ARG;
            }
            break;
        case VehiclePropertyType::FLOAT:
            if (value.value.floatValues.size() != 1) {
                return StatusCode::INVALID_ARG;
            }
            break;
        case VehiclePropertyType::FLOAT_VEC:
            if (value.value.floatValues.size() < 1) {
                return StatusCode::INVALID_ARG;
            }
            break;
        case VehiclePropertyType::BYTES:
            // We allow setting an empty bytes array.
            break;
        case VehiclePropertyType::STRING:
            // We allow setting an empty string.
            break;
        case VehiclePropertyType::MIXED:
            if (getPropGroup(property) == VehiclePropertyGroup::VENDOR) {
                // We only checks vendor mixed properties.
                return checkVendorMixedPropValue(value, config);
            }
            break;
        default:
            ALOGW("Unknown property type: %d", type);
            return StatusCode::INVALID_ARG;
    }
    return StatusCode::OK;
}

StatusCode DefaultVehicleHal::checkVendorMixedPropValue(const VehiclePropValue& value,
                                                        const VehiclePropConfig* config) {
    auto configArray = config->configArray;
    // configArray[0], 1 indicates the property has a String value, we allow the string value to
    // be empty.

    size_t int32Count = 0;
    // configArray[1], 1 indicates the property has a Boolean value.
    if (configArray[1] == 1) {
        int32Count++;
    }
    // configArray[2], 1 indicates the property has an Integer value.
    if (configArray[2] == 1) {
        int32Count++;
    }
    // configArray[3], the number indicates the size of Integer[] in the property.
    int32Count += static_cast<size_t>(configArray[3]);
    if (value.value.int32Values.size() != int32Count) {
        return StatusCode::INVALID_ARG;
    }

    size_t int64Count = 0;
    // configArray[4], 1 indicates the property has a Long value.
    if (configArray[4] == 1) {
        int64Count++;
    }
    // configArray[5], the number indicates the size of Long[] in the property.
    int64Count += static_cast<size_t>(configArray[5]);
    if (value.value.int64Values.size() != int64Count) {
        return StatusCode::INVALID_ARG;
    }

    size_t floatCount = 0;
    // configArray[6], 1 indicates the property has a Float value.
    if (configArray[6] == 1) {
        floatCount++;
    }
    // configArray[7], the number indicates the size of Float[] in the property.
    floatCount += static_cast<size_t>(configArray[7]);
    if (value.value.floatValues.size() != floatCount) {
        return StatusCode::INVALID_ARG;
    }

    // configArray[8], the number indicates the size of byte[] in the property.
    if (configArray[8] != 0 && value.value.bytes.size() != static_cast<size_t>(configArray[8])) {
        return StatusCode::INVALID_ARG;
    }
    return StatusCode::OK;
}

StatusCode DefaultVehicleHal::checkValueRange(const VehiclePropValue& value,
                                              const VehiclePropConfig* config) {
    int32_t property = value.prop;
    VehiclePropertyType type = getPropType(property);
    const VehicleAreaConfig* areaConfig;
    if (isGlobalProp(property)) {
        if (config->areaConfigs.size() == 0) {
            return StatusCode::OK;
        }
        areaConfig = &(config->areaConfigs[0]);
    } else {
        for (auto& c : config->areaConfigs) {
            // areaId might contain multiple areas.
            if (c.areaId & value.areaId) {
                areaConfig = &c;
                break;
            }
        }
    }
    switch (type) {
        case VehiclePropertyType::INT32:
            if (areaConfig->minInt32Value == 0 && areaConfig->maxInt32Value == 0) {
                break;
            }
            // We already checked this in checkPropValue.
            assert(value.value.int32Values.size() > 0);
            if (value.value.int32Values[0] < areaConfig->minInt32Value ||
                value.value.int32Values[0] > areaConfig->maxInt32Value) {
                return StatusCode::INVALID_ARG;
            }
            break;
        case VehiclePropertyType::INT64:
            if (areaConfig->minInt64Value == 0 && areaConfig->maxInt64Value == 0) {
                break;
            }
            // We already checked this in checkPropValue.
            assert(value.value.int64Values.size() > 0);
            if (value.value.int64Values[0] < areaConfig->minInt64Value ||
                value.value.int64Values[0] > areaConfig->maxInt64Value) {
                return StatusCode::INVALID_ARG;
            }
            break;
        case VehiclePropertyType::FLOAT:
            if (areaConfig->minFloatValue == 0 && areaConfig->maxFloatValue == 0) {
                break;
            }
            // We already checked this in checkPropValue.
            assert(value.value.floatValues.size() > 0);
            if (value.value.floatValues[0] < areaConfig->minFloatValue ||
                value.value.floatValues[0] > areaConfig->maxFloatValue) {
                return StatusCode::INVALID_ARG;
            }
            break;
        default:
            // We don't check the rest of property types. Additional logic needs to be added if
            // required for real implementation. E.g., you might want to enforce the range
            // checks on vector as well or you might want to check the range for mixed property.
            break;
    }
    return StatusCode::OK;
}

StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) {
    if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
        // Android side cannot set property status - this value is the
@@ -79,12 +247,26 @@ StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) {
        // its underlying hardware
        return StatusCode::INVALID_ARG;
    }
    auto currentPropValue = mPropStore->readValueOrNull(propValue);

    if (currentPropValue == nullptr) {
    int32_t property = propValue.prop;
    const VehiclePropConfig* config = mPropStore->getConfigOrNull(property);
    if (config == nullptr) {
        ALOGW("no config for prop 0x%x", property);
        return StatusCode::INVALID_ARG;
    }
    if (currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {
    auto status = checkPropValue(propValue, config);
    if (status != StatusCode::OK) {
        ALOGW("invalid property value: %s", toString(propValue).c_str());
        return status;
    }
    status = checkValueRange(propValue, config);
    if (status != StatusCode::OK) {
        ALOGW("property value out of range: %s", toString(propValue).c_str());
        return status;
    }

    auto currentPropValue = mPropStore->readValueOrNull(propValue);
    if (currentPropValue && currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {
        // do not allow Android side to set() a disabled/error property
        return StatusCode::NOT_AVAILABLE;
    }
@@ -151,6 +333,14 @@ StatusCode DefaultVehicleHal::subscribe(int32_t property, float sampleRate) {
    if (!isContinuousProperty(property)) {
        return StatusCode::INVALID_ARG;
    }

    // If the config does not exist, isContinuousProperty should return false.
    const VehiclePropConfig* config = mPropStore->getConfigOrNull(property);
    if (sampleRate < config->minSampleRate || sampleRate > config->maxSampleRate) {
        ALOGW("sampleRate out of range");
        return StatusCode::INVALID_ARG;
    }

    mRecurrentTimer.registerRecurrentEvent(hertzToNanoseconds(sampleRate), property);
    return StatusCode::OK;
}
+10 −0
Original line number Diff line number Diff line
@@ -61,6 +61,16 @@ class DefaultVehicleHal : public VehicleHal {
    virtual void onPropertyValue(const VehiclePropValue& value, bool updateStatus);
    // Returns a lambda that could be used in mRecurrentTimer.
    RecurrentTimer::Action getTimerAction();
    // Check whether a propValue is valid according to its type.
    StatusCode checkPropValue(const VehiclePropValue& propValue, const VehiclePropConfig* config);
    // Check whether the property value is within the range according to area config.
    StatusCode checkValueRange(const VehiclePropValue& propValue, const VehiclePropConfig* config);

  private:
    // Check whether a vendor mixed value property is valid according to its config array.
    // See 'VehiclePropertyType' documentation in 'types.hal' for detail.
    StatusCode checkVendorMixedPropValue(const VehiclePropValue& value,
                                         const VehiclePropConfig* config);
};

}  // namespace impl
+229 −15
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@

#include <android-base/format.h>
#include <android-base/logging.h>
#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <utils/SystemClock.h>

#include "DefaultConfig.h"
@@ -82,7 +84,7 @@ void DefaultVehicleHalServer::sendAllValuesToClient() {
    }
}

GeneratorHub* DefaultVehicleHalServer::getGenerator() {
GeneratorHub* DefaultVehicleHalServer::getGeneratorHub() {
    return &mGeneratorHub;
}

@@ -145,8 +147,8 @@ StatusCode DefaultVehicleHalServer::handleGenerateFakeDataRequest(const VehicleP
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = v.int32Values[1];
            getGenerator()->registerGenerator(cookie,
                                              std::make_unique<LinearFakeValueGenerator>(request));
            getGeneratorHub()->registerGenerator(
                    cookie, std::make_unique<LinearFakeValueGenerator>(request));
            break;
        }
        case FakeDataCommand::StartJson: {
@@ -156,8 +158,12 @@ StatusCode DefaultVehicleHalServer::handleGenerateFakeDataRequest(const VehicleP
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = std::hash<std::string>()(v.stringValue);
            getGenerator()->registerGenerator(cookie,
                                              std::make_unique<JsonFakeValueGenerator>(request));
            auto generator = std::make_unique<JsonFakeValueGenerator>(request);
            if (!generator->hasNext()) {
                LOG(ERROR) << __func__ << ": invalid JSON file, no events";
                return StatusCode::INVALID_ARG;
            }
            getGeneratorHub()->registerGenerator(cookie, std::move(generator));
            break;
        }
        case FakeDataCommand::StopLinear: {
@@ -167,7 +173,7 @@ StatusCode DefaultVehicleHalServer::handleGenerateFakeDataRequest(const VehicleP
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = v.int32Values[1];
            getGenerator()->unregisterGenerator(cookie);
            getGeneratorHub()->unregisterGenerator(cookie);
            break;
        }
        case FakeDataCommand::StopJson: {
@@ -177,7 +183,7 @@ StatusCode DefaultVehicleHalServer::handleGenerateFakeDataRequest(const VehicleP
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = std::hash<std::string>()(v.stringValue);
            getGenerator()->unregisterGenerator(cookie);
            getGeneratorHub()->unregisterGenerator(cookie);
            break;
        }
        case FakeDataCommand::KeyPress: {
@@ -331,10 +337,11 @@ StatusCode DefaultVehicleHalServer::onSetProperty(const VehiclePropValue& value,
}

IVehicleServer::DumpResult DefaultVehicleHalServer::onDump(
        const std::vector<std::string>& /* options */) {
        const std::vector<std::string>& options) {
    DumpResult result;
    if (options.size() == 0) {
        // No options, dump all stored properties.
        result.callerShouldDumpState = true;

        result.buffer += "Server side properties: \n";
        auto values = mServerSidePropStore.readAllValues();
        size_t i = 0;
@@ -344,6 +351,213 @@ IVehicleServer::DumpResult DefaultVehicleHalServer::onDump(
        }
        return result;
    }
    if (options[0] != "--debughal") {
        // We only expect "debughal" command. This might be some commands that the caller knows
        // about, so let caller handle it.
        result.callerShouldDumpState = true;
        return result;
    }

    return debug(options);
}

IVehicleServer::DumpResult DefaultVehicleHalServer::debug(const std::vector<std::string>& options) {
    DumpResult result;
    // This is a debug command for the HAL, caller should not continue to dump state.
    result.callerShouldDumpState = false;

    if (options.size() < 2) {
        result.buffer += "No command specified\n";
        result.buffer += getHelpInfo();
        return result;
    }

    std::string command = options[1];
    if (command == "--help") {
        result.buffer += getHelpInfo();
        return result;
    } else if (command == "--genfakedata") {
        return genFakeData(options);
    }

    result.buffer += "Unknown command: \"" + command + "\"\n";
    result.buffer += getHelpInfo();
    return result;
}

std::string DefaultVehicleHalServer::getHelpInfo() {
    return "Help: \n"
           "Generate Fake Data: \n"
           "\tStart a linear generator: \n"
           "\t--debughal --genfakedata --startlinear [propID(int32)] [middleValue(float)] "
           "[currentValue(float)] [dispersion(float)] [increment(float)] [interval(int64)]\n"
           "\tStop a linear generator: \n"
           "\t--debughal --genfakedata --stoplinear [propID(int32)]\n"
           "\tStart a json generator: \n"
           "\t--debughal --genfakedata --startjson [jsonFilePath(string)] "
           "[repetition(int32)(optional)]\n"
           "\tStop a json generator: \n"
           "\t--debughal --genfakedata --stopjson [jsonFilePath(string)]\n"
           "\tGenerate key press: \n"
           "\t--debughal --genfakedata --keypress [keyCode(int32)] [display[int32]]\n";
}

IVehicleServer::DumpResult DefaultVehicleHalServer::genFakeData(
        const std::vector<std::string>& options) {
    DumpResult result;
    // This is a debug command for the HAL, caller should not continue to dump state.
    result.callerShouldDumpState = false;

    if (options.size() < 3) {
        result.buffer += "No subcommand specified for genfakedata\n";
        result.buffer += getHelpInfo();
        return result;
    }

    std::string command = options[2];
    if (command == "--startlinear") {
        LOG(INFO) << __func__ << "FakeDataCommand::StartLinear";
        // --debughal --genfakedata --startlinear [propID(int32)] [middleValue(float)]
        // [currentValue(float)] [dispersion(float)] [increment(float)] [interval(int64)]
        if (options.size() != 9) {
            result.buffer +=
                    "incorrect argument count, need 9 arguments for --genfakedata --startlinear\n";
            result.buffer += getHelpInfo();
            return result;
        }
        int32_t propId;
        float middleValue;
        float currentValue;
        float dispersion;
        float increment;
        int64_t interval;
        if (!android::base::ParseInt(options[3], &propId)) {
            result.buffer += "failed to parse propdID as int: \"" + options[3] + "\"\n";
            result.buffer += getHelpInfo();
            return result;
        }
        if (!android::base::ParseFloat(options[4], &middleValue)) {
            result.buffer += "failed to parse middleValue as float: \"" + options[4] + "\"\n";
            result.buffer += getHelpInfo();
            return result;
        }
        if (!android::base::ParseFloat(options[5], &currentValue)) {
            result.buffer += "failed to parse currentValue as float: \"" + options[5] + "\"\n";
            result.buffer += getHelpInfo();
            return result;
        }
        if (!android::base::ParseFloat(options[6], &dispersion)) {
            result.buffer += "failed to parse dispersion as float: \"" + options[6] + "\"\n";
            result.buffer += getHelpInfo();
            return result;
        }
        if (!android::base::ParseFloat(options[7], &increment)) {
            result.buffer += "failed to parse increment as float: \"" + options[7] + "\"\n";
            result.buffer += getHelpInfo();
            return result;
        }
        if (!android::base::ParseInt(options[8], &interval)) {
            result.buffer += "failed to parse interval as int: \"" + options[8] + "\"\n";
            result.buffer += getHelpInfo();
            return result;
        }
        auto generator = std::make_unique<LinearFakeValueGenerator>(
                propId, middleValue, currentValue, dispersion, increment, interval);
        getGeneratorHub()->registerGenerator(propId, std::move(generator));
        return result;
    } else if (command == "--stoplinear") {
        LOG(INFO) << __func__ << "FakeDataCommand::StopLinear";
        // --debughal --genfakedata --stoplinear [propID(int32)]
        if (options.size() != 4) {
            result.buffer +=
                    "incorrect argument count, need 4 arguments for --genfakedata --stoplinear\n";
            result.buffer += getHelpInfo();
            return result;
        }
        int32_t propId;
        if (!android::base::ParseInt(options[3], &propId)) {
            result.buffer += "failed to parse propdID as int: \"" + options[3] + "\"\n";
            result.buffer += getHelpInfo();
            return result;
        }
        getGeneratorHub()->unregisterGenerator(propId);
        return result;
    } else if (command == "--startjson") {
        LOG(INFO) << __func__ << "FakeDataCommand::StartJson";
        // --debughal --genfakedata --startjson [jsonFilePath(string)] [repetition(int32)(optional)]
        if (options.size() != 4 && options.size() != 5) {
            result.buffer +=
                    "incorrect argument count, need 4 or 5 arguments for --genfakedata "
                    "--startjson\n";
            result.buffer += getHelpInfo();
            return result;
        }
        std::string fileName = options[3];
        int32_t cookie = std::hash<std::string>()(fileName);
        // Iterate infinitely if repetition number is not provided
        int32_t repetition = -1;
        if (options.size() == 5) {
            if (!android::base::ParseInt(options[4], &repetition)) {
                result.buffer += "failed to parse repetition as int: \"" + options[4] + "\"\n";
                result.buffer += getHelpInfo();
                return result;
            }
        }
        auto generator = std::make_unique<JsonFakeValueGenerator>(fileName, repetition);
        if (!generator->hasNext()) {
            result.buffer += "invalid JSON file, no events";
            return result;
        }
        getGeneratorHub()->registerGenerator(cookie, std::move(generator));
        return result;
    } else if (command == "--stopjson") {
        LOG(INFO) << __func__ << "FakeDataCommand::StopJson";
        // --debughal --genfakedata --stopjson [jsonFilePath(string)]
        if (options.size() != 4) {
            result.buffer +=
                    "incorrect argument count, need 4 arguments for --genfakedata --stopjson\n";
            result.buffer += getHelpInfo();
            return result;
        }
        std::string fileName = options[3];
        int32_t cookie = std::hash<std::string>()(fileName);
        getGeneratorHub()->unregisterGenerator(cookie);
        return result;
    } else if (command == "--keypress") {
        LOG(INFO) << __func__ << "FakeDataCommand::KeyPress";
        int32_t keyCode;
        int32_t display;
        // --debughal --genfakedata --keypress [keyCode(int32)] [display[int32]]
        if (options.size() != 5) {
            result.buffer +=
                    "incorrect argument count, need 5 arguments for --genfakedata --keypress\n";
            result.buffer += getHelpInfo();
            return result;
        }
        if (!android::base::ParseInt(options[3], &keyCode)) {
            result.buffer += "failed to parse keyCode as int: \"" + options[3] + "\"\n";
            result.buffer += getHelpInfo();
            return result;
        }
        if (!android::base::ParseInt(options[4], &display)) {
            result.buffer += "failed to parse display as int: \"" + options[4] + "\"\n";
            result.buffer += getHelpInfo();
            return result;
        }
        // Send back to HAL
        onPropertyValueFromCar(
                *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display),
                /*updateStatus=*/true);
        onPropertyValueFromCar(
                *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display),
                /*updateStatus=*/true);
        return result;
    }

    result.buffer += "Unknown command: \"" + command + "\"\n";
    result.buffer += getHelpInfo();
    return result;
}

}  // namespace impl

+7 −1
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ class DefaultVehicleHalServer : public IVehicleServer {

  protected:
    using VehiclePropValuePtr = recyclable_ptr<VehiclePropValue>;
    GeneratorHub* getGenerator();
    GeneratorHub* getGeneratorHub();

    VehiclePropValuePool* getValuePool() const;

@@ -67,6 +67,12 @@ class DefaultVehicleHalServer : public IVehicleServer {

    void storePropInitialValue(const ConfigDeclaration& config);

    DumpResult debug(const std::vector<std::string>& options);

    std::string getHelpInfo();

    DumpResult genFakeData(const std::vector<std::string>& options);

  protected:
    GeneratorHub mGeneratorHub{
            [this](const VehiclePropValue& value) { return onFakeValueGenerated(value); }};
Loading