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

Commit 744fe1a7 authored by Hao Chen's avatar Hao Chen Committed by Android (Google) Code Review
Browse files

Merge changes Iab77a0ae,Ibf525ffe

* changes:
  Applying the vehicle connector to the VHAL
  Implement the connector for emulated vehicles
parents 9b839488 fba3ac86
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ cc_library_static {
    defaults: ["vhal_v2_0_defaults"],
    srcs: [
        "impl/vhal_v2_0/CommConn.cpp",
        "impl/vhal_v2_0/EmulatedVehicleConnector.cpp",
        "impl/vhal_v2_0/EmulatedVehicleHal.cpp",
        "impl/vhal_v2_0/VehicleEmulator.cpp",
        "impl/vhal_v2_0/PipeComm.cpp",
+5 −2
Original line number Diff line number Diff line
@@ -20,8 +20,9 @@

#include <iostream>

#include <vhal_v2_0/VehicleHalManager.h>
#include <vhal_v2_0/EmulatedVehicleConnector.h>
#include <vhal_v2_0/EmulatedVehicleHal.h>
#include <vhal_v2_0/VehicleHalManager.h>

using namespace android;
using namespace android::hardware;
@@ -29,9 +30,11 @@ using namespace android::hardware::automotive::vehicle::V2_0;

int main(int /* argc */, char* /* argv */ []) {
    auto store = std::make_unique<VehiclePropertyStore>();
    auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get());
    auto connector = impl::makeEmulatedPassthroughConnector();
    auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get());
    auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
    auto service = std::make_unique<VehicleHalManager>(hal.get());
    connector->setValuePool(hal->getValuePool());

    configureRpcThreadpool(4, true /* callerWillJoin */);

+254 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.
 */
#include <android-base/logging.h>
#include <utils/SystemClock.h>

#include "DefaultConfig.h"
#include "EmulatedVehicleConnector.h"
#include "JsonFakeValueGenerator.h"
#include "LinearFakeValueGenerator.h"
#include "Obd2SensorStore.h"

namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace V2_0 {

namespace impl {

void EmulatedVehicleClient::onPropertyValue(const VehiclePropValue& value) {
    if (!mPropCallback) {
        LOG(ERROR) << __func__ << ": PropertyCallBackType is not registered!";
        return;
    }
    return mPropCallback(value);
}

void EmulatedVehicleClient::registerPropertyValueCallback(PropertyCallBackType&& callback) {
    if (mPropCallback) {
        LOG(ERROR) << __func__ << ": Cannot register multiple callbacks!";
        return;
    }
    mPropCallback = std::move(callback);
}

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

VehiclePropValuePool* EmulatedVehicleServer::getValuePool() const {
    if (!mValuePool) {
        LOG(WARNING) << __func__ << ": Value pool not set!";
    }
    return mValuePool;
}

void EmulatedVehicleServer::setValuePool(VehiclePropValuePool* valuePool) {
    if (!valuePool) {
        LOG(WARNING) <<  __func__ << ": Setting value pool to nullptr!";
    }
    mValuePool = valuePool;
}

void EmulatedVehicleServer::onFakeValueGenerated(const VehiclePropValue& value) {
    LOG(DEBUG) << __func__ << ": " << toString(value);
    auto updatedPropValue = getValuePool()->obtain(value);
    if (updatedPropValue) {
        updatedPropValue->timestamp = value.timestamp;
        updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
        onPropertyValueFromCar(*updatedPropValue);
    }
}

std::vector<VehiclePropConfig> EmulatedVehicleServer::onGetAllPropertyConfig() const {
    std::vector<VehiclePropConfig> vehiclePropConfigs;
    constexpr size_t numOfVehiclePropConfigs =
            sizeof(kVehicleProperties) / sizeof(kVehicleProperties[0]);
    vehiclePropConfigs.reserve(numOfVehiclePropConfigs);
    for (auto& it : kVehicleProperties) {
        vehiclePropConfigs.emplace_back(it.config);
    }
    return vehiclePropConfigs;
}

StatusCode EmulatedVehicleServer::handleGenerateFakeDataRequest(const VehiclePropValue& request) {
    LOG(INFO) << __func__;
    const auto& v = request.value;
    if (!v.int32Values.size()) {
        LOG(ERROR) << __func__ << ": expected at least \"command\" field in int32Values";
        return StatusCode::INVALID_ARG;
    }

    FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]);

    switch (command) {
        case FakeDataCommand::StartLinear: {
            LOG(INFO) << __func__ << ", FakeDataCommand::StartLinear";
            if (v.int32Values.size() < 2) {
                LOG(ERROR) << __func__ << ": expected property ID in int32Values";
                return StatusCode::INVALID_ARG;
            }
            if (!v.int64Values.size()) {
                LOG(ERROR) << __func__ << ": interval is not provided in int64Values";
                return StatusCode::INVALID_ARG;
            }
            if (v.floatValues.size() < 3) {
                LOG(ERROR) << __func__ << ": expected at least 3 elements in floatValues, got: "
                      << v.floatValues.size();
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = v.int32Values[1];
            getGenerator()->registerGenerator(cookie,
                                              std::make_unique<LinearFakeValueGenerator>(request));
            break;
        }
        case FakeDataCommand::StartJson: {
            LOG(INFO) << __func__ << ", FakeDataCommand::StartJson";
            if (v.stringValue.empty()) {
                LOG(ERROR) << __func__ << ": path to JSON file is missing";
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = std::hash<std::string>()(v.stringValue);
            getGenerator()->registerGenerator(cookie,
                                              std::make_unique<JsonFakeValueGenerator>(request));
            break;
        }
        case FakeDataCommand::StopLinear: {
            LOG(INFO) << __func__ << ", FakeDataCommand::StopLinear";
            if (v.int32Values.size() < 2) {
                LOG(ERROR) << __func__ << ": expected property ID in int32Values";
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = v.int32Values[1];
            getGenerator()->unregisterGenerator(cookie);
            break;
        }
        case FakeDataCommand::StopJson: {
            LOG(INFO) << __func__ << ", FakeDataCommand::StopJson";
            if (v.stringValue.empty()) {
                LOG(ERROR) << __func__ << ": path to JSON file is missing";
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = std::hash<std::string>()(v.stringValue);
            getGenerator()->unregisterGenerator(cookie);
            break;
        }
        case FakeDataCommand::KeyPress: {
            LOG(INFO) << __func__ << ", FakeDataCommand::KeyPress";
            int32_t keyCode = request.value.int32Values[2];
            int32_t display = request.value.int32Values[3];
            // Send back to HAL
            onPropertyValueFromCar(
                    *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display));
            onPropertyValueFromCar(
                    *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display));
            break;
        }
        default: {
            LOG(ERROR) << __func__ << ": unexpected command: " << toInt(command);
            return StatusCode::INVALID_ARG;
        }
    }
    return StatusCode::OK;
}

VehicleHal::VehiclePropValuePtr EmulatedVehicleServer::createApPowerStateReq(
    VehicleApPowerStateReq state, int32_t param) {
    auto req = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 2);
    req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ);
    req->areaId = 0;
    req->timestamp = elapsedRealtimeNano();
    req->status = VehiclePropertyStatus::AVAILABLE;
    req->value.int32Values[0] = toInt(state);
    req->value.int32Values[1] = param;
    return req;
}

VehicleHal::VehiclePropValuePtr EmulatedVehicleServer::createHwInputKeyProp(
        VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay) {
    auto keyEvent = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 3);
    keyEvent->prop = toInt(VehicleProperty::HW_KEY_INPUT);
    keyEvent->areaId = 0;
    keyEvent->timestamp = elapsedRealtimeNano();
    keyEvent->status = VehiclePropertyStatus::AVAILABLE;
    keyEvent->value.int32Values[0] = toInt(action);
    keyEvent->value.int32Values[1] = keyCode;
    keyEvent->value.int32Values[2] = targetDisplay;
    return keyEvent;
}

StatusCode EmulatedVehicleServer::onSetProperty(const VehiclePropValue& value) {
    // Some properties need to be treated non-trivially
    switch (value.prop) {
        case AP_POWER_STATE_REPORT:
            switch (value.value.int32Values[0]) {
                case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT):
                case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED):
                case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL):
                    // CPMS is in WAIT_FOR_VHAL state, simply move to ON
                    // Send back to HAL
                    onPropertyValueFromCar(
                            *createApPowerStateReq(VehicleApPowerStateReq::ON, 0));
                    break;
                case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY):
                case toInt(VehicleApPowerStateReport::SHUTDOWN_START):
                    // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command
                    // Send back to HAL
                    onPropertyValueFromCar(
                            *createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0));
                    break;
                case toInt(VehicleApPowerStateReport::ON):
                case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE):
                case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE):
                    // Do nothing
                    break;
                default:
                    // Unknown state
                    break;
            }
            break;
        default:
            break;
    }

    // In the real vhal, the value will be sent to Car ECU.
    // We just pretend it is done here.
    return StatusCode::OK;
}

StatusCode EmulatedVehicleServer::onSetPropertyFromVehicle(const VehiclePropValue& value) {
    if (value.prop == kGenerateFakeDataControllingProperty) {
        auto status = handleGenerateFakeDataRequest(value);
        return status;
    } else {
        // Send back to HAL
        onPropertyValueFromCar(value);
        return StatusCode::OK;
    }
}

EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector() {
    return std::make_unique<EmulatedPassthroughConnector>();
}

}  // namespace impl

}  // namespace V2_0
}  // namespace vehicle
}  // namespace automotive
}  // namespace hardware
}  // namespace android
+110 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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_V2_0_impl_EmulatedVehicleConnector_H_
#define android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_

#include <vhal_v2_0/VehicleConnector.h>
#include <vhal_v2_0/VehicleHal.h>

#include "GeneratorHub.h"

namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace V2_0 {

namespace impl {

// Extension of the client/server interfaces for emulated vehicle

class EmulatedVehicleClient : public IVehicleClient {
  public:
    // Type of callback function for handling the new property values
    using PropertyCallBackType = std::function<void(const VehiclePropValue&)>;

    // Method from IVehicleClient
    void onPropertyValue(const VehiclePropValue& value) override;

    // Request to change the value on the VEHICLE side (for testing)
    virtual StatusCode setPropertyFromVehicle(const VehiclePropValue& value) = 0;

    void registerPropertyValueCallback(PropertyCallBackType&& callback);

  private:
    PropertyCallBackType mPropCallback;
};

class EmulatedVehicleServer : public IVehicleServer {
  public:
    // Methods from IVehicleServer

    std::vector<VehiclePropConfig> onGetAllPropertyConfig() const override;

    StatusCode onSetProperty(const VehiclePropValue& value) override;

    // Process the request to change the value on the VEHICLE side (for testing)
    StatusCode onSetPropertyFromVehicle(const VehiclePropValue& value);

    // Set the Property Value Pool used in this server
    void setValuePool(VehiclePropValuePool* valuePool);

  private:
    GeneratorHub* getGenerator();

    VehiclePropValuePool* getValuePool() const;

    void onFakeValueGenerated(const VehiclePropValue& value);

    StatusCode handleGenerateFakeDataRequest(const VehiclePropValue& request);

    VehicleHal::VehiclePropValuePtr createApPowerStateReq(VehicleApPowerStateReq req, int32_t param);

    VehicleHal::VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action,
                                                         int32_t keyCode, int32_t targetDisplay);

    // private data members

    GeneratorHub mGeneratorHub{
            std::bind(&EmulatedVehicleServer::onFakeValueGenerated, this, std::placeholders::_1)};

    VehiclePropValuePool* mValuePool{nullptr};
};

class EmulatedPassthroughConnector
    : public IPassThroughConnector<EmulatedVehicleClient, EmulatedVehicleServer> {
  public:
    StatusCode setPropertyFromVehicle(const VehiclePropValue& value) override {
        return this->onSetPropertyFromVehicle(value);
    }
};

// Helper functions

using EmulatedPassthroughConnectorPtr = std::unique_ptr<EmulatedPassthroughConnector>;

EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector();

}  // namespace impl

}  // namespace V2_0
}  // namespace vehicle
}  // namespace automotive
}  // namespace hardware
}  // namespace android

#endif  // android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_
+20 −159
Original line number Diff line number Diff line
@@ -87,17 +87,19 @@ static std::unique_ptr<Obd2SensorStore> fillDefaultObd2Frame(size_t numVendorInt
    return sensorStore;
}

EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore)
EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore,
                                       EmulatedVehicleClient* client)
    : mPropStore(propStore),
      mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
      mRecurrentTimer(
          std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)),
      mGeneratorHub(
          std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1)) {
      mVehicleClient(client) {
    initStaticConfig();
    for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
        mPropStore->registerProperty(kVehicleProperties[i].config);
    }
    mVehicleClient->registerPropertyValueCallback(
        std::bind(&EmulatedVehicleHal::onPropertyValue, this, std::placeholders::_1));
}

VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
@@ -162,7 +164,8 @@ StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
    }

    if (propValue.prop == kGenerateFakeDataControllingProperty) {
        StatusCode status = handleGenerateFakeDataRequest(propValue);
        // send the generator controlling request to the server
        auto status = mVehicleClient->setPropertyFromVehicle(propValue);
        if (status != StatusCode::OK) {
            return status;
        }
@@ -186,29 +189,6 @@ StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
                // Placeholder for future implementation of VMS property in the default hal. For
                // now, just returns OK; otherwise, hal clients crash with property not supported.
                return StatusCode::OK;
            case AP_POWER_STATE_REPORT:
                switch (propValue.value.int32Values[0]) {
                    case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT):
                    case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED):
                    case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL):
                        // CPMS is in WAIT_FOR_VHAL state, simply move to ON
                        doHalEvent(createApPowerStateReq(VehicleApPowerStateReq::ON, 0));
                        break;
                    case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY):
                    case toInt(VehicleApPowerStateReport::SHUTDOWN_START):
                        // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command
                        doHalEvent(createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0));
                        break;
                    case toInt(VehicleApPowerStateReport::ON):
                    case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE):
                    case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE):
                        // Do nothing
                        break;
                    default:
                        // Unknown state
                        break;
                }
                break;
        }
    }

@@ -231,10 +211,16 @@ StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
    /**
     * After checking all conditions, such as the property is available, a real vhal will
     * sent the events to Car ECU to take actions.
     * Google HAL will just add a timestamp for the value and triggle the callback to android.
     */
    VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(propValue);
    updatedPropValue->timestamp = elapsedRealtimeNano();

    // Send the value to the vehicle server, the server will talk to the (real or emulated) car
    auto setValueStatus = mVehicleClient->setProperty(*updatedPropValue);
    if (setValueStatus != StatusCode::OK) {
        return setValueStatus;
    }

    if (!mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus)) {
        return StatusCode::INTERNAL_ERROR;
    }
@@ -361,146 +347,21 @@ bool EmulatedVehicleHal::isContinuousProperty(int32_t propId) const {
}

bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) {
    static constexpr bool shouldUpdateStatus = true;

    if (propValue.prop == kGenerateFakeDataControllingProperty) {
        StatusCode status = handleGenerateFakeDataRequest(propValue);
        if (status != StatusCode::OK) {
            return false;
        }
    }

    if (mPropStore->writeValue(propValue, shouldUpdateStatus)) {
        doHalEvent(getValuePool()->obtain(propValue));
        return true;
    } else {
        return false;
    }
    return mVehicleClient->setPropertyFromVehicle(propValue) == StatusCode::OK;
}

std::vector<VehiclePropValue> EmulatedVehicleHal::getAllProperties() const  {
    return mPropStore->readAllValues();
}

StatusCode EmulatedVehicleHal::handleGenerateFakeDataRequest(const VehiclePropValue& request) {
    ALOGI("%s", __func__);
    const auto& v = request.value;
    if (!v.int32Values.size()) {
        ALOGE("%s: expected at least \"command\" field in int32Values", __func__);
        return StatusCode::INVALID_ARG;
    }

    FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]);

    switch (command) {
        case FakeDataCommand::StartLinear: {
            ALOGI("%s, FakeDataCommand::StartLinear", __func__);
            if (v.int32Values.size() < 2) {
                ALOGE("%s: expected property ID in int32Values", __func__);
                return StatusCode::INVALID_ARG;
            }
            if (!v.int64Values.size()) {
                ALOGE("%s: interval is not provided in int64Values", __func__);
                return StatusCode::INVALID_ARG;
            }
            if (v.floatValues.size() < 3) {
                ALOGE("%s: expected at least 3 elements in floatValues, got: %zu", __func__,
                      v.floatValues.size());
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = v.int32Values[1];
            mGeneratorHub.registerGenerator(cookie,
                                            std::make_unique<LinearFakeValueGenerator>(request));
            break;
        }
        case FakeDataCommand::StartJson: {
            ALOGI("%s, FakeDataCommand::StartJson", __func__);
            if (v.stringValue.empty()) {
                ALOGE("%s: path to JSON file is missing", __func__);
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = std::hash<std::string>()(v.stringValue);
            mGeneratorHub.registerGenerator(cookie,
                                            std::make_unique<JsonFakeValueGenerator>(request));
            break;
        }
        case FakeDataCommand::StopLinear: {
            ALOGI("%s, FakeDataCommand::StopLinear", __func__);
            if (v.int32Values.size() < 2) {
                ALOGE("%s: expected property ID in int32Values", __func__);
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = v.int32Values[1];
            mGeneratorHub.unregisterGenerator(cookie);
            break;
        }
        case FakeDataCommand::StopJson: {
            ALOGI("%s, FakeDataCommand::StopJson", __func__);
            if (v.stringValue.empty()) {
                ALOGE("%s: path to JSON file is missing", __func__);
                return StatusCode::INVALID_ARG;
            }
            int32_t cookie = std::hash<std::string>()(v.stringValue);
            mGeneratorHub.unregisterGenerator(cookie);
            break;
        }
        case FakeDataCommand::KeyPress: {
            ALOGI("%s, FakeDataCommand::KeyPress", __func__);
            int32_t keyCode = request.value.int32Values[2];
            int32_t display = request.value.int32Values[3];
            doHalEvent(
                createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display));
            doHalEvent(createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display));
            break;
        }
        default: {
            ALOGE("%s: unexpected command: %d", __func__, command);
            return StatusCode::INVALID_ARG;
        }
    }
    return StatusCode::OK;
}

VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::createApPowerStateReq(
    VehicleApPowerStateReq state, int32_t param) {
    auto req = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 2);
    req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ);
    req->areaId = 0;
    req->timestamp = elapsedRealtimeNano();
    req->status = VehiclePropertyStatus::AVAILABLE;
    req->value.int32Values[0] = toInt(state);
    req->value.int32Values[1] = param;
    return req;
}

VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::createHwInputKeyProp(
    VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay) {
    auto keyEvent = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 3);
    keyEvent->prop = toInt(VehicleProperty::HW_KEY_INPUT);
    keyEvent->areaId = 0;
    keyEvent->timestamp = elapsedRealtimeNano();
    keyEvent->status = VehiclePropertyStatus::AVAILABLE;
    keyEvent->value.int32Values[0] = toInt(action);
    keyEvent->value.int32Values[1] = keyCode;
    keyEvent->value.int32Values[2] = targetDisplay;
    return keyEvent;
}

void EmulatedVehicleHal::onFakeValueGenerated(const VehiclePropValue& value) {
    ALOGD("%s: %s", __func__, toString(value).c_str());
    static constexpr bool shouldUpdateStatus = false;

void EmulatedVehicleHal::onPropertyValue(const VehiclePropValue& value) {
    static constexpr bool shouldUpdateStatus = true;
    VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
    if (updatedPropValue) {
        updatedPropValue->timestamp = value.timestamp;
        updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
        mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus);
        auto changeMode = mPropStore->getConfigOrDie(value.prop)->changeMode;
        if (VehiclePropertyChangeMode::ON_CHANGE == changeMode) {

    if (mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus)) {
        doHalEvent(std::move(updatedPropValue));
    }
}
}

void EmulatedVehicleHal::initStaticConfig() {
    for (auto&& it = std::begin(kVehicleProperties); it != std::end(kVehicleProperties); ++it) {
Loading