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

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

Merge "Migrate fake value generator."

parents 4ef92a94 f2b8b8f9
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -11,6 +11,9 @@
    },
    {
      "name": "FakeVehicleHardwareTest"
    },
    {
      "name": "FakeVehicleHalValueGeneratorsTest"
    }
  ]
}
+72 −0
Original line number 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 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 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 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