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

Commit f2b8b8f9 authored by Yu Shan's avatar Yu Shan
Browse files

Migrate fake value generator.

Test: atest FakeVehicleHalValueGeneratorsTest
Bug: 201830716
Change-Id: Ic113c9b189a07140a288d6d03d3a8a2b9061881f
parent 7987654e
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -11,6 +11,9 @@
    },
    },
    {
    {
      "name": "FakeVehicleHardwareTest"
      "name": "FakeVehicleHardwareTest"
    },
    {
      "name": "FakeVehicleHalValueGeneratorsTest"
    }
    }
  ]
  ]
}
}
+72 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_JsonFakeValueGenerator_H_
#define android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_JsonFakeValueGenerator_H_

#include "FakeValueGenerator.h"

#include <json/json.h>

#include <iostream>
#include <vector>

namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace fake {

class JsonFakeValueGenerator : public FakeValueGenerator {
  public:
    // Create a new JSON fake value generator. {@code request.value.stringValue} is the JSON file
    // name. {@code request.value.int32Values[1]} if exists, is the number of iterations. If
    // {@code int32Values} has less than 2 elements, number of iterations would be set to -1, which
    // means iterate indefinitely.
    explicit JsonFakeValueGenerator(
            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& request);
    // Create a new JSON fake value generator using the specified JSON file path. All the events
    // in the JSON file would be generated for number of {@code iteration}. If iteration is 0, no
    // value would be generated. If iteration is less than 0, it would iterate indefinitely.
    explicit JsonFakeValueGenerator(const std::string& path, int32_t iteration);
    // Create a new JSON fake value generator using the specified JSON file path. All the events
    // in the JSON file would be generated once.
    explicit JsonFakeValueGenerator(const std::string& path);

    ~JsonFakeValueGenerator() = default;

    std::optional<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> nextEvent()
            override;
    const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
    getAllEvents();

  private:
    size_t mEventIndex = 0;
    std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> mEvents;
    long mLastEventTimestamp = 0;
    int32_t mNumOfIterations = 0;

    void setBit(std::vector<uint8_t>& bytes, size_t idx);
    void init(const std::string& path, int32_t iteration);
};

}  // namespace fake
}  // namespace vehicle
}  // namespace automotive
}  // namespace hardware
}  // namespace android

#endif  // android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_JsonFakeValueGenerator_H_
+75 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_LinearFakeValueGenerator_H_
#define android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_LinearFakeValueGenerator_H_

#include "FakeValueGenerator.h"

namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace fake {

class LinearFakeValueGenerator : public FakeValueGenerator {
  public:
    // A linear value generator initialized using values in request.
    // int32Values[1]: propId
    // floatValues[0]: middleValue and currentValue
    // floatValues[1]: dispersion
    // floatValues[2]: increment
    // int64Values[0]: interval
    // {@code propId} must be INT32 or INT64 or FLOAT type.
    explicit LinearFakeValueGenerator(
            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& request);
    // A linear value generator in range [middleValue - dispersion, middleValue + dispersion),
    // starts at 'currentValue' and at each 'interval', increase by 'increment' and loop back if
    // exceeds middleValue + dispersion. {@code propId} must be INT32 or INT64 or FLOAT type.
    explicit LinearFakeValueGenerator(int32_t propId, float middleValue, float initValue,
                                      float dispersion, float increment, int64_t interval);
    ~LinearFakeValueGenerator() = default;

    std::optional<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> nextEvent()
            override;

  private:
    // In every timer tick we may want to generate new value based on initial value for debug
    // purpose. It's better to have sequential values to see if events gets delivered in order
    // to the client.
    struct GeneratorCfg {
        int32_t propId;
        float middleValue;
        float currentValue;  //  Should be in range (middleValue +/- dispersion).
        float dispersion;    //  Defines minimum and maximum value based on initial value.
        float increment;     //  Value that we will be added to currentValue with each timer tick.
        int64_t interval;
        long lastEventTimestamp;
    };

    GeneratorCfg mGenCfg;

    void initGenCfg(int32_t propId, float middleValue, float initValue, float dispersion,
                    float increment, int64_t interval);
};

}  // namespace fake
}  // namespace vehicle
}  // namespace automotive
}  // namespace hardware
}  // namespace android

#endif  // android_hardware_automotive_vehicle_aidl_impl_fake_impl_GeneratorHub_include_LinearFakeValueGenerator_H_
+256 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "JsonFakeValueGenerator"

#include "JsonFakeValueGenerator.h"

#include <fstream>
#include <type_traits>
#include <typeinfo>

#include <VehicleUtils.h>
#include <android/binder_enums.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>

namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace fake {

namespace {

using ::aidl::android::hardware::automotive::vehicle::DiagnosticFloatSensorIndex;
using ::aidl::android::hardware::automotive::vehicle::DiagnosticIntegerSensorIndex;
using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;

template <class T>
int getLastIndex() {
    auto range = ::ndk::enum_range<T>();
    auto it = range.begin();
    while (std::next(it) != range.end()) {
        it++;
    }
    return toInt(*it);
}

bool isDiagnosticProperty(int32_t prop) {
    return prop == toInt(VehicleProperty::OBD2_LIVE_FRAME) ||
           prop == toInt(VehicleProperty::OBD2_FREEZE_FRAME);
}

void setBit(std::vector<uint8_t>& bytes, size_t idx) {
    uint8_t mask = 1 << (idx % 8);
    bytes[idx / 8] |= mask;
}

template <typename T>
void copyJsonArray(const Json::Value& jsonArray, std::vector<T>& dest) {
    dest.resize(jsonArray.size());
    for (Json::Value::ArrayIndex i = 0; i < jsonArray.size(); i++) {
        if (std::is_same<T, int32_t>::value) {
            dest[i] = jsonArray[i].asInt();
        } else if (std::is_same<T, int64_t>::value) {
            dest[i] = jsonArray[i].asInt64();
        } else if (std::is_same<T, float>::value) {
            dest[i] = jsonArray[i].asFloat();
        }
    }
}

void copyMixedValueJson(const Json::Value& jsonValue, RawPropValues& dest) {
    copyJsonArray(jsonValue["int32Values"], dest.int32Values);
    copyJsonArray(jsonValue["int64Values"], dest.int64Values);
    copyJsonArray(jsonValue["floatValues"], dest.floatValues);
    dest.stringValue = jsonValue["stringValue"].asString();
}

std::vector<uint8_t> generateDiagnosticBytes(const RawPropValues& diagnosticValue) {
    size_t lastIntegerSensorIndex =
            static_cast<size_t>(getLastIndex<DiagnosticIntegerSensorIndex>());
    size_t lastFloatSensorIndex = static_cast<size_t>(getLastIndex<DiagnosticFloatSensorIndex>());

    size_t byteSize = (lastIntegerSensorIndex + lastFloatSensorIndex + 2);
    std::vector<uint8_t> bytes((byteSize + 7) / 8);

    auto& int32Values = diagnosticValue.int32Values;
    for (size_t i = 0; i < int32Values.size(); i++) {
        if (int32Values[i] != 0) {
            setBit(bytes, i);
        }
    }

    auto& floatValues = diagnosticValue.floatValues;
    for (size_t i = 0; i < floatValues.size(); i++) {
        if (floatValues[i] != 0.0) {
            setBit(bytes, i + lastIntegerSensorIndex + 1);
        }
    }
    return bytes;
}

std::vector<VehiclePropValue> parseFakeValueJson(std::istream& is) {
    std::vector<VehiclePropValue> fakeVhalEvents;

    Json::CharReaderBuilder builder;
    Json::Value rawEvents;
    std::string errorMessage;
    if (!Json::parseFromStream(builder, is, &rawEvents, &errorMessage)) {
        ALOGE("%s: Failed to parse fake data JSON file. Error: %s", __func__, errorMessage.c_str());
        return fakeVhalEvents;
    }

    for (Json::Value::ArrayIndex i = 0; i < rawEvents.size(); i++) {
        Json::Value rawEvent = rawEvents[i];
        if (!rawEvent.isObject()) {
            ALOGE("%s: VHAL JSON event should be an object, %s", __func__,
                  rawEvent.toStyledString().c_str());
            continue;
        }
        if (rawEvent["prop"].empty() || rawEvent["areaId"].empty() || rawEvent["value"].empty() ||
            rawEvent["timestamp"].empty()) {
            ALOGE("%s: VHAL JSON event has missing fields, skip it, %s", __func__,
                  rawEvent.toStyledString().c_str());
            continue;
        }
        VehiclePropValue event = {
                .timestamp = rawEvent["timestamp"].asInt64(),
                .areaId = rawEvent["areaId"].asInt(),
                .prop = rawEvent["prop"].asInt(),
        };

        Json::Value rawEventValue = rawEvent["value"];
        auto& value = event.value;
        int32_t count;
        switch (getPropType(event.prop)) {
            case VehiclePropertyType::BOOLEAN:
            case VehiclePropertyType::INT32:
                value.int32Values.resize(1);
                value.int32Values[0] = rawEventValue.asInt();
                break;
            case VehiclePropertyType::INT64:
                value.int64Values.resize(1);
                value.int64Values[0] = rawEventValue.asInt64();
                break;
            case VehiclePropertyType::FLOAT:
                value.floatValues.resize(1);
                value.floatValues[0] = rawEventValue.asFloat();
                break;
            case VehiclePropertyType::STRING:
                value.stringValue = rawEventValue.asString();
                break;
            case VehiclePropertyType::INT32_VEC:
                value.int32Values.resize(rawEventValue.size());
                count = 0;
                for (auto& it : rawEventValue) {
                    value.int32Values[count++] = it.asInt();
                }
                break;
            case VehiclePropertyType::MIXED:
                copyMixedValueJson(rawEventValue, value);
                if (isDiagnosticProperty(event.prop)) {
                    value.byteValues = generateDiagnosticBytes(value);
                }
                break;
            default:
                ALOGE("%s: unsupported type for property: 0x%x", __func__, event.prop);
                continue;
        }
        fakeVhalEvents.push_back(event);
    }
    return fakeVhalEvents;
}

}  // namespace

JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path) {
    init(path, 1);
}

JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path, int32_t iteration) {
    init(path, iteration);
}

JsonFakeValueGenerator::JsonFakeValueGenerator(const VehiclePropValue& request) {
    const auto& v = request.value;
    // Iterate infinitely if iteration number is not provided
    int32_t numOfIterations = v.int32Values.size() < 2 ? -1 : v.int32Values[1];

    init(v.stringValue, numOfIterations);
}

void JsonFakeValueGenerator::init(const std::string& path, int32_t iteration) {
    std::ifstream ifs(path);
    if (!ifs) {
        ALOGE("%s: couldn't open %s for parsing.", __func__, path.c_str());
        return;
    }
    mEvents = parseFakeValueJson(ifs);
    mNumOfIterations = iteration;
}

const std::vector<VehiclePropValue>& JsonFakeValueGenerator::getAllEvents() {
    return mEvents;
}

std::optional<VehiclePropValue> JsonFakeValueGenerator::nextEvent() {
    if (mNumOfIterations == 0 || mEvents.size() == 0) {
        return std::nullopt;
    }

    VehiclePropValue generatedValue = mEvents[mEventIndex];

    if (mLastEventTimestamp == 0) {
        mLastEventTimestamp = elapsedRealtimeNano();
    } else {
        long nextEventTime = 0;
        if (mEventIndex > 0) {
            // All events (start from 2nd one) are supposed to happen in the future with a delay
            // equals to the duration between previous and current event.
            nextEventTime = mLastEventTimestamp +
                            (mEvents[mEventIndex].timestamp - mEvents[mEventIndex - 1].timestamp);
        } else {
            // We are starting another iteration, immediately send the next event after 1ms.
            nextEventTime = mLastEventTimestamp + 1000000;
        }
        // Prevent overflow.
        assert(nextEventTime > mLastEventTimestamp);
        mLastEventTimestamp = nextEventTime;
    }

    mEventIndex++;
    if (mEventIndex == mEvents.size()) {
        mEventIndex = 0;
        if (mNumOfIterations > 0) {
            mNumOfIterations--;
        }
    }

    generatedValue.timestamp = mLastEventTimestamp;

    return generatedValue;
}

}  // namespace fake
}  // namespace vehicle
}  // namespace automotive
}  // namespace hardware
}  // namespace android
+108 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "LinearFakeValueGenerator"

#include "LinearFakeValueGenerator.h"

#include <VehicleUtils.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>

namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace fake {

using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;

LinearFakeValueGenerator::LinearFakeValueGenerator(int32_t propId, float middleValue,
                                                   float initValue, float dispersion,
                                                   float increment, int64_t interval) {
    initGenCfg(propId, middleValue, initValue, dispersion, increment, interval);
}

LinearFakeValueGenerator::LinearFakeValueGenerator(const VehiclePropValue& request) {
    const auto& v = request.value;
    initGenCfg(v.int32Values[1], v.floatValues[0], v.floatValues[0], v.floatValues[1],
               v.floatValues[2], v.int64Values[0]);
}

void LinearFakeValueGenerator::initGenCfg(int32_t propId, float middleValue, float initValue,
                                          float dispersion, float increment, int64_t interval) {
    // Other types are not supported.
    assert(getPropType(propId) == VehicleProperty::INT32 ||
           getPropType(propId) == VehicleProperty::INT64 ||
           getPropType(propId) == VehicleProperty::FLOAT);

    if (initValue < middleValue - dispersion || initValue >= middleValue + dispersion) {
        ALOGW("%s: invalid initValue: %f, out of range, default to %f", __func__, initValue,
              middleValue);
        initValue = middleValue;
    }
    mGenCfg = GeneratorCfg{
            .propId = propId,
            .middleValue = middleValue,
            .currentValue = initValue,
            .dispersion = dispersion,
            .increment = increment,
            .interval = interval,
    };
}

std::optional<VehiclePropValue> LinearFakeValueGenerator::nextEvent() {
    VehiclePropValue event = {
            .prop = mGenCfg.propId,
    };
    auto& value = event.value;
    switch (getPropType(event.prop)) {
        case VehiclePropertyType::INT32:
            value.int32Values = {static_cast<int32_t>(mGenCfg.currentValue)};
            break;
        case VehiclePropertyType::INT64:
            value.int64Values = {static_cast<int64_t>(mGenCfg.currentValue)};
            break;
        case VehiclePropertyType::FLOAT:
            value.floatValues = {mGenCfg.currentValue};
            break;
        default:
            ALOGE("%s: unsupported property type for 0x%x", __func__, event.prop);
    }
    if (mGenCfg.lastEventTimestamp == 0) {
        mGenCfg.lastEventTimestamp = elapsedRealtimeNano();
    } else {
        long nextEventTime = mGenCfg.lastEventTimestamp + mGenCfg.interval;
        // Prevent overflow.
        assert(nextEventTime > mGenCfg.lastEventTimestamp);
        mGenCfg.lastEventTimestamp = nextEventTime;
    }
    event.timestamp = mGenCfg.lastEventTimestamp;

    mGenCfg.currentValue += mGenCfg.increment;
    if (mGenCfg.currentValue >= mGenCfg.middleValue + mGenCfg.dispersion) {
        // Wrap around, (i - d) + c - (i + d) = c - 2 * d
        mGenCfg.currentValue = mGenCfg.currentValue - 2 * mGenCfg.dispersion;
    }
    return event;
}

}  // namespace fake
}  // namespace vehicle
}  // namespace automotive
}  // namespace hardware
}  // namespace android
Loading