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

Commit 27946f0b authored by Yu Shan's avatar Yu Shan
Browse files

Support more types for set property cmd.

Now support int64, float and bytes.
This CL also allows setting read-only properties or reading
write-only properties through cmdline. The cmdline debug interface
is for test only and does not need to enforce these restrictions.

Test: unit tests.
Bug: b/189969547
Merged-In: Ib0e085f7a68e3d54782c21fb12caa500a0ad82ec
Change-Id: Ib0e085f7a68e3d54782c21fb12caa500a0ad82ec
(cherry picked from commit 8ae3ea98)
parent 1dfa556e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -182,6 +182,7 @@ cc_test {
    ],
    shared_libs: [
        "libbase",
        "libcutils",
    ],
    header_libs: ["libbase_headers"],
    test_suites: ["general-tests"],
+23 −3
Original line number Diff line number Diff line
@@ -76,6 +76,9 @@ public:
    Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;

  private:
    // Set unit test class as friend class to test private functions.
    friend class VehicleHalManagerTestHelper;

    using VehiclePropValuePtr = VehicleHal::VehiclePropValuePtr;
    // Returns true if needs to call again shortly.
    using RetriableAction = std::function<bool()>;
@@ -105,14 +108,20 @@ public:
    void cmdDumpOneProperty(int fd, int32_t prop, int32_t areaId);
    void cmdDumpOneProperty(int fd, int rowNumber, const VehiclePropConfig& config);

    bool cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options);

    static bool checkArgumentsSize(int fd, const hidl_vec<hidl_string>& options, size_t minSize);
    static bool checkCallerHasWritePermissions(int fd);
    static bool safelyParseInt(int fd, int index, std::string s, int* out);
    template <typename T>
    static bool safelyParseInt(int fd, int index, const std::string& s, T* out);
    static bool safelyParseFloat(int fd, int index, const std::string& s, float* out);
    // Parses "s" as a hex string and populate "*bytes". The hex string must be in the format of
    // valid hex format, e.g. "0xABCD".
    static bool parseHexString(int fd, const std::string& s, std::vector<uint8_t>* bytes);
    void cmdHelp(int fd) const;
    void cmdListAllProperties(int fd) const;
    void cmdDumpAllProperties(int fd);
    void cmdDumpSpecificProperties(int fd, const hidl_vec<hidl_string>& options);
    void cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options);

    static bool isSubscribable(const VehiclePropConfig& config,
                               SubscribeFlags flags);
@@ -120,6 +129,17 @@ public:
    static float checkSampleRate(const VehiclePropConfig& config,
                                 float sampleRate);
    static ClientId getClientId(const sp<IVehicleCallback>& callback);

    // Parses the cmdline options for "--set" command. "*prop" would be populated with the
    // the properties to be set. Returns true when the cmdline options are valid, false otherwise.
    static bool parseSetPropOptions(int fd, const hidl_vec<hidl_string>& options,
                                    VehiclePropValue* prop);
    // Parses the options and get the values for the current option specified by "*index". "*index"
    // would advance to the next option field (e.g., the next "-f"). Returns a list of values for
    // the current option.
    static std::vector<std::string> getOptionValues(const hidl_vec<hidl_string>& options,
                                                    size_t* index);

  private:
    VehicleHal* mHal;
    std::unique_ptr<VehiclePropConfigIndex> mConfigIndex;
+220 −86
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@

#include <cmath>
#include <fstream>
#include <unordered_set>

#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android/hardware/automotive/vehicle/2.0/BpHwVehicleCallback.h>
@@ -44,15 +46,34 @@ using ::android::base::EqualsIgnoreCase;
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;

namespace {

constexpr std::chrono::milliseconds kHalEventBatchingTimeWindow(10);

const VehiclePropValue kEmptyValue{};

// A list of supported options for "--set" command.
const std::unordered_set<std::string> kSetPropOptions = {
        // integer.
        "-i",
        // 64bit integer.
        "-i64",
        // float.
        "-f",
        // string.
        "-s",
        // bytes in hex format, e.g. 0xDEADBEEF.
        "-b",
        // Area id in integer.
        "-a"};

}  // namespace

/**
 * Indicates what's the maximum size of hidl_vec<VehiclePropValue> we want
 * to store in reusable object pool.
 */
constexpr auto kMaxHidlVecOfVehiclPropValuePoolSize = 20;
constexpr auto kMaxHidlVecOfVehiclePropValuePoolSize = 20;

Return<void> VehicleHalManager::getAllPropConfigs(getAllPropConfigs_cb _hidl_cb) {
    ALOGI("getAllPropConfigs called");
@@ -213,6 +234,11 @@ void VehicleHalManager::cmdDump(int fd, const hidl_vec<hidl_string>& options) {
    } else if (EqualsIgnoreCase(option, "--get")) {
        cmdDumpSpecificProperties(fd, options);
    } else if (EqualsIgnoreCase(option, "--set")) {
        if (!checkCallerHasWritePermissions(fd)) {
            dprintf(fd, "Caller does not have write permission\n");
            return;
        }
        // Ignore the return value for this.
        cmdSetOneProperty(fd, options);
    } else {
        dprintf(fd, "Invalid option: %s\n", option.c_str());
@@ -239,7 +265,8 @@ bool VehicleHalManager::checkArgumentsSize(int fd, const hidl_vec<hidl_string>&
    return false;
}

bool VehicleHalManager::safelyParseInt(int fd, int index, std::string s, int* out) {
template <typename T>
bool VehicleHalManager::safelyParseInt(int fd, int index, const std::string& s, T* out) {
    if (!android::base::ParseInt(s, out)) {
        dprintf(fd, "non-integer argument at index %d: %s\n", index, s.c_str());
        return false;
@@ -247,19 +274,27 @@ bool VehicleHalManager::safelyParseInt(int fd, int index, std::string s, int* ou
    return true;
}

bool VehicleHalManager::safelyParseFloat(int fd, int index, const std::string& s, float* out) {
    if (!android::base::ParseFloat(s, out)) {
        dprintf(fd, "non-float argument at index %d: %s\n", index, s.c_str());
        return false;
    }
    return true;
}

void VehicleHalManager::cmdHelp(int fd) const {
    dprintf(fd, "Usage: \n\n");
    dprintf(fd, "[no args]: dumps (id and value) all supported properties \n");
    dprintf(fd, "--help: shows this help\n");
    dprintf(fd, "--list: lists the ids of all supported properties\n");
    dprintf(fd, "--get <PROP1> [PROP2] [PROPN]: dumps the value of specific properties \n");
    // TODO: support other formats (int64, float, bytes)
    dprintf(fd,
            "--set <PROP> <i|s> <VALUE_1> [<i|s> <VALUE_N>] [a AREA_ID] : sets the value of "
            "property PROP, using arbitrary number of key/value parameters (i for int32, "
            "s for string) and an optional area.\n"
            "Notice that the string value can be set just once, while the other can have multiple "
            "values (so they're used in the respective array)\n");
            "--set <PROP> [-i INT_VALUE [INT_VALUE ...]] [-i64 INT64_VALUE [INT64_VALUE ...]] "
            "[-f FLOAT_VALUE [FLOAT_VALUE ...]] [-s STR_VALUE] "
            "[-b BYTES_VALUE] [-a AREA_ID] : sets the value of property PROP. "
            "Notice that the string, bytes and area value can be set just once, while the other can"
            " have multiple values (so they're used in the respective array), "
            "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n");
}

void VehicleHalManager::cmdListAllProperties(int fd) const {
@@ -337,102 +372,49 @@ void VehicleHalManager::cmdDumpOneProperty(int fd, int32_t prop, int32_t areaId)
    VehiclePropValue input;
    input.prop = prop;
    input.areaId = areaId;
    auto callback = [&](StatusCode status, const VehiclePropValue& output) {
    auto callback = [&fd, &prop](StatusCode status, const VehiclePropValue& output) {
        if (status == StatusCode::OK) {
            dprintf(fd, "%s\n", toString(output).c_str());
        } else {
            dprintf(fd, "Could not get property %d. Error: %s\n", prop, toString(status).c_str());
        }
    };
    get(input, callback);
}

void VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options) {
    if (!checkCallerHasWritePermissions(fd) || !checkArgumentsSize(fd, options, 3)) return;

    size_t size = options.size();

    // Syntax is --set PROP Type1 Value1 TypeN ValueN, so number of arguments must be even
    if (size % 2 != 0) {
        dprintf(fd, "must pass even number of arguments (passed %zu)\n", size);
        return;
    }
    int numberValues = (size - 2) / 2;

    VehiclePropValue prop;
    if (!safelyParseInt(fd, 1, options[1], &prop.prop)) return;
    prop.timestamp = elapsedRealtimeNano();
    prop.status = VehiclePropertyStatus::AVAILABLE;

    // First pass: calculate sizes
    int sizeInt32 = 0;
    int stringIndex = 0;
    int areaIndex = 0;
    for (int i = 2, kv = 1; kv <= numberValues; kv++) {
        // iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step
        std::string type = options[i];
        std::string value = options[i + 1];
        if (EqualsIgnoreCase(type, "i")) {
            sizeInt32++;
        } else if (EqualsIgnoreCase(type, "s")) {
            if (stringIndex != 0) {
                dprintf(fd,
                        "defining string value (%s) again at index %d (already defined at %d=%s"
                        ")\n",
                        value.c_str(), i, stringIndex, options[stringIndex + 1].c_str());
                return;
            }
            stringIndex = i;
        } else if (EqualsIgnoreCase(type, "a")) {
            if (areaIndex != 0) {
                dprintf(fd,
                        "defining area value (%s) again at index %d (already defined at %d=%s"
                        ")\n",
                        value.c_str(), i, areaIndex, options[areaIndex + 1].c_str());
                return;
            }
            areaIndex = i;
        } else {
            dprintf(fd, "invalid (%s) type at index %d\n", type.c_str(), i);
            return;
        }
        i += 2;
    StatusCode status;
    auto value = mHal->get(input, &status);
    callback(status, value.get() ? *value : kEmptyValue);
}
    prop.value.int32Values.resize(sizeInt32);

    // Second pass: populate it
    int indexInt32 = 0;
    for (int i = 2, kv = 1; kv <= numberValues; kv++) {
        // iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step
        int valueIndex = i + 1;
        std::string type = options[i];
        std::string value = options[valueIndex];
        if (EqualsIgnoreCase(type, "i")) {
            int safeInt;
            if (!safelyParseInt(fd, valueIndex, value, &safeInt)) return;
            prop.value.int32Values[indexInt32++] = safeInt;
        } else if (EqualsIgnoreCase(type, "s")) {
            prop.value.stringValue = value;
        } else if (EqualsIgnoreCase(type, "a")) {
            if (!safelyParseInt(fd, valueIndex, value, &prop.areaId)) return;
bool VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options) {
    if (!checkArgumentsSize(fd, options, 4)) {
        dprintf(fd, "Requires at least 4 options, see help\n");
        return false;
    }
        i += 2;

    VehiclePropValue prop = {};
    if (!parseSetPropOptions(fd, options, &prop)) {
        return false;
    }
    ALOGD("Setting prop %s", toString(prop).c_str());
    auto status = set(prop);

    // Do not use VehicleHalManager::set here because we don't want to check write permission.
    // Caller should be able to use the debug interface to set read-only properties.
    handlePropertySetEvent(prop);
    auto status = mHal->set(prop);

    if (status == StatusCode::OK) {
        dprintf(fd, "Set property %s\n", toString(prop).c_str());
    } else {
        return true;
    }
    dprintf(fd, "Failed to set property %s: %s\n", toString(prop).c_str(),
            toString(status).c_str());
    }
    return false;
}

void VehicleHalManager::init() {
    ALOGI("VehicleHalManager::init");

    mHidlVecOfVehiclePropValuePool.resize(kMaxHidlVecOfVehiclPropValuePoolSize);

    mHidlVecOfVehiclePropValuePool.resize(kMaxHidlVecOfVehiclePropValuePoolSize);

    mBatchingConsumer.run(&mEventQueue,
                          kHalEventBatchingTimeWindow,
@@ -486,7 +468,7 @@ void VehicleHalManager::onBatchHalEvent(const std::vector<VehiclePropValuePtr>&
    for (const HalClientValues& cv : clientValues) {
        auto vecSize = cv.values.size();
        hidl_vec<VehiclePropValue> vec;
        if (vecSize < kMaxHidlVecOfVehiclPropValuePoolSize) {
        if (vecSize < kMaxHidlVecOfVehiclePropValuePoolSize) {
            vec.setToExternal(&mHidlVecOfVehiclePropValuePool[0], vecSize);
        } else {
            vec.resize(vecSize);
@@ -595,6 +577,158 @@ ClientId VehicleHalManager::getClientId(const sp<IVehicleCallback>& callback) {
    }
}

std::vector<std::string> VehicleHalManager::getOptionValues(const hidl_vec<hidl_string>& options,
                                                            size_t* index) {
    std::vector<std::string> values;
    while (*index < options.size()) {
        std::string option = options[*index];
        if (kSetPropOptions.find(option) != kSetPropOptions.end()) {
            return std::move(values);
        }
        values.push_back(option);
        (*index)++;
    }
    return std::move(values);
}

bool VehicleHalManager::parseSetPropOptions(int fd, const hidl_vec<hidl_string>& options,
                                            VehiclePropValue* prop) {
    // Options format:
    // --set PROP [-f f1 f2...] [-i i1 i2...] [-i64 i1 i2...] [-s s1 s2...] [-b b1 b2...] [-a a]
    size_t optionIndex = 1;
    int propValue;
    if (!safelyParseInt(fd, optionIndex, options[optionIndex], &propValue)) {
        dprintf(fd, "property value: \"%s\" is not a valid int\n", options[optionIndex].c_str());
        return false;
    }
    prop->prop = propValue;
    prop->timestamp = elapsedRealtimeNano();
    prop->status = VehiclePropertyStatus::AVAILABLE;
    optionIndex++;
    std::unordered_set<std::string> parsedOptions;

    while (optionIndex < options.size()) {
        std::string type = options[optionIndex];
        optionIndex++;
        size_t currentIndex = optionIndex;
        std::vector<std::string> values = getOptionValues(options, &optionIndex);
        if (parsedOptions.find(type) != parsedOptions.end()) {
            dprintf(fd, "duplicate \"%s\" options\n", type.c_str());
            return false;
        }
        parsedOptions.insert(type);
        if (EqualsIgnoreCase(type, "-i")) {
            if (values.size() == 0) {
                dprintf(fd, "no values specified when using \"-i\"\n");
                return false;
            }
            prop->value.int32Values.resize(values.size());
            for (size_t i = 0; i < values.size(); i++) {
                int32_t safeInt;
                if (!safelyParseInt(fd, currentIndex + i, values[i], &safeInt)) {
                    dprintf(fd, "value: \"%s\" is not a valid int\n", values[i].c_str());
                    return false;
                }
                prop->value.int32Values[i] = safeInt;
            }
        } else if (EqualsIgnoreCase(type, "-i64")) {
            if (values.size() == 0) {
                dprintf(fd, "no values specified when using \"-i64\"\n");
                return false;
            }
            prop->value.int64Values.resize(values.size());
            for (size_t i = 0; i < values.size(); i++) {
                int64_t safeInt;
                if (!safelyParseInt(fd, currentIndex + i, values[i], &safeInt)) {
                    dprintf(fd, "value: \"%s\" is not a valid int64\n", values[i].c_str());
                    return false;
                }
                prop->value.int64Values[i] = safeInt;
            }
        } else if (EqualsIgnoreCase(type, "-f")) {
            if (values.size() == 0) {
                dprintf(fd, "no values specified when using \"-f\"\n");
                return false;
            }
            prop->value.floatValues.resize(values.size());
            for (size_t i = 0; i < values.size(); i++) {
                float safeFloat;
                if (!safelyParseFloat(fd, currentIndex + i, values[i], &safeFloat)) {
                    dprintf(fd, "value: \"%s\" is not a valid float\n", values[i].c_str());
                    return false;
                }
                prop->value.floatValues[i] = safeFloat;
            }
        } else if (EqualsIgnoreCase(type, "-s")) {
            if (values.size() != 1) {
                dprintf(fd, "expect exact one value when using \"-s\"\n");
                return false;
            }
            prop->value.stringValue = values[0];
        } else if (EqualsIgnoreCase(type, "-b")) {
            if (values.size() != 1) {
                dprintf(fd, "expect exact one value when using \"-b\"\n");
                return false;
            }
            std::vector<uint8_t> bytes;
            if (!parseHexString(fd, values[0], &bytes)) {
                dprintf(fd, "value: \"%s\" is not a valid hex string\n", values[0].c_str());
                return false;
            }
            prop->value.bytes = bytes;
        } else if (EqualsIgnoreCase(type, "-a")) {
            if (values.size() != 1) {
                dprintf(fd, "expect exact one value when using \"-a\"\n");
                return false;
            }
            if (!safelyParseInt(fd, currentIndex, values[0], &(prop->areaId))) {
                dprintf(fd, "area ID: \"%s\" is not a valid int\n", values[0].c_str());
                return false;
            }
        } else {
            dprintf(fd, "unknown option: %s\n", type.c_str());
            return false;
        }
    }

    return true;
}

bool VehicleHalManager::parseHexString(int fd, const std::string& s, std::vector<uint8_t>* bytes) {
    if (s.size() % 2 != 0) {
        dprintf(fd, "invalid hex string: %s, should have even size\n", s.c_str());
        return false;
    }
    if (strncmp(s.substr(0, 2).c_str(), "0x", 2)) {
        dprintf(fd, "hex string should start with \"0x\", got %s\n", s.c_str());
        return false;
    }
    std::string subs = s.substr(2);
    std::transform(subs.begin(), subs.end(), subs.begin(),
                   [](unsigned char c) { return std::tolower(c); });

    bool highDigit = true;
    for (size_t i = 0; i < subs.size(); i++) {
        char c = subs[i];
        uint8_t v;
        if (c >= '0' && c <= '9') {
            v = c - '0';
        } else if (c >= 'a' && c <= 'f') {
            v = c - 'a' + 10;
        } else {
            dprintf(fd, "invalid character %c in hex string %s\n", c, subs.c_str());
            return false;
        }
        if (highDigit) {
            (*bytes).push_back(v * 16);
        } else {
            (*bytes)[bytes->size() - 1] += v;
        }
        highDigit = !highDigit;
    }
    return true;
}

}  // namespace V2_0
}  // namespace vehicle
}  // namespace automotive
+179 −28

File changed.

Preview size limit exceeded, changes collapsed.