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

Commit 7e9e37fa authored by Hao Chen's avatar Hao Chen
Browse files

Split client and server impl

Some of the code in VHAL client implementation contains Android-specific
code, and some of the server operations only works in the native case.
So we split them up so that the AGL VHAL server can selectivly pick the
parts it needs.

It won't change the logic of native VHAL.

Bug: 148877226

Test: Build

Change-Id: Ie142b19a5c435a0b4252ffd297504bde69eb44b0
parent 8dfac92f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -63,6 +63,8 @@ cc_library_static {
        "impl/vhal_v2_0/CommConn.cpp",
        "impl/vhal_v2_0/EmulatedVehicleConnector.cpp",
        "impl/vhal_v2_0/EmulatedVehicleHal.cpp",
        "impl/vhal_v2_0/VehicleHalClient.cpp",
        "impl/vhal_v2_0/VehicleHalServer.cpp",
        "impl/vhal_v2_0/VehicleEmulator.cpp",
        "impl/vhal_v2_0/PipeComm.cpp",
        "impl/vhal_v2_0/ProtoMessageConverter.cpp",
+10 −340
Original line number Diff line number Diff line
@@ -35,345 +35,15 @@ namespace V2_0 {

namespace impl {

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

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) {
    constexpr bool updateStatus = true;
    LOG(DEBUG) << __func__ << ": " << toString(value);
    auto updatedPropValue = getValuePool()->obtain(value);
    if (updatedPropValue) {
        updatedPropValue->timestamp = value.timestamp;
        updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
        onPropertyValueFromCar(*updatedPropValue, updateStatus);
    }
}

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;
}
class EmulatedPassthroughConnector : public PassthroughConnector {
  public:
    bool onDump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;

StatusCode EmulatedVehicleServer::handleGenerateFakeDataRequest(const VehiclePropValue& request) {
    constexpr bool updateStatus = true;

    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),
                    updateStatus);
            onPropertyValueFromCar(
                    *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display),
                    updateStatus);
            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, bool updateStatus) {
    // Some properties need to be treated non-trivially
    switch (value.prop) {
        case kGenerateFakeDataControllingProperty:
            return handleGenerateFakeDataRequest(value);

        // set the value from vehicle side, used in end to end test.
        case kSetIntPropertyFromVehicleForTest: {
            auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::INT32, 1);
            updatedPropValue->prop = value.value.int32Values[0];
            updatedPropValue->value.int32Values[0] = value.value.int32Values[1];
            updatedPropValue->timestamp = value.value.int64Values[0];
            updatedPropValue->areaId = value.areaId;
            onPropertyValueFromCar(*updatedPropValue, updateStatus);
            return StatusCode::OK;
        }
        case kSetFloatPropertyFromVehicleForTest: {
            auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::FLOAT, 1);
            updatedPropValue->prop = value.value.int32Values[0];
            updatedPropValue->value.floatValues[0] = value.value.floatValues[0];
            updatedPropValue->timestamp = value.value.int64Values[0];
            updatedPropValue->areaId = value.areaId;
            onPropertyValueFromCar(*updatedPropValue, updateStatus);
            return StatusCode::OK;
        }
        case kSetBooleanPropertyFromVehicleForTest: {
            auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::BOOLEAN, 1);
            updatedPropValue->prop = value.value.int32Values[1];
            updatedPropValue->value.int32Values[0] = value.value.int32Values[0];
            updatedPropValue->timestamp = value.value.int64Values[0];
            updatedPropValue->areaId = value.areaId;
            onPropertyValueFromCar(*updatedPropValue, updateStatus);
            return StatusCode::OK;
        }

        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
                    // ALWAYS update status for generated property value
                    onPropertyValueFromCar(*createApPowerStateReq(VehicleApPowerStateReq::ON, 0),
                                           true /* updateStatus */);
                    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
                    // ALWAYS update status for generated property value
                    onPropertyValueFromCar(
                            *createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0),
                            true /* updateStatus */);
                    break;
                case toInt(VehicleApPowerStateReport::ON):
                case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE):
                case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE):
                    // Do nothing
                    break;
                default:
                    // Unknown state
                    break;
            }
            break;
        case INITIAL_USER_INFO:
            return onSetInitialUserInfo(value, updateStatus);
        default:
            break;
    }

    // In the real vhal, the value will be sent to Car ECU.
    // We just pretend it is done here and send back to HAL
    auto updatedPropValue = getValuePool()->obtain(value);
    updatedPropValue->timestamp = elapsedRealtimeNano();

    onPropertyValueFromCar(*updatedPropValue, updateStatus);
    return StatusCode::OK;
}

/**
 * INITIAL_USER_INFO is called by Android when it starts, and it's expecting a property change
 * indicating what the initial user should be.
 *
 * During normal circumstances, the emulator will reply right away, passing a response if
 * InitialUserInfoResponseAction::DEFAULT (so Android could use its own logic to decide which user
 * to boot).
 *
 * But during development / testing, the behavior can be changed using lshal dump, which must use
 * the areaId to indicate what should happen next.
 *
 * So, the behavior of set(INITIAL_USER_INFO) is:
 *
 * - if it has an areaId, store the property into mInitialUserResponseFromCmd (as it was called by
 *   lshal).
 * - else if mInitialUserResponseFromCmd is not set, return a response with the same request id and
 *   InitialUserInfoResponseAction::DEFAULT
 * - else the behavior is defined by the areaId on mInitialUserResponseFromCmd:
 * - if it's 1, reply with mInitialUserResponseFromCmd and the right request id
 * - if it's 2, reply with mInitialUserResponseFromCmd but a wrong request id (so Android can test
 *   this error scenario)
 * - if it's 3, then don't send a property change (so Android can emulate a timeout)
 *
 */
StatusCode EmulatedVehicleServer::onSetInitialUserInfo(const VehiclePropValue& value,
                                                       bool updateStatus) {
    // TODO: LOG calls below might be more suited to be DEBUG, but those are not being logged
    // (even when explicitly calling setprop log.tag. As this class should be using ALOG instead of
    // LOG, it's not worth investigating why...

    if (value.value.int32Values.size() == 0) {
        LOG(ERROR) << "set(INITIAL_USER_INFO): no int32values, ignoring it: " << toString(value);
        return StatusCode::INVALID_ARG;
    }

    if (value.areaId != 0) {
        LOG(INFO) << "set(INITIAL_USER_INFO) called from lshal; storing it: " << toString(value);
        mInitialUserResponseFromCmd.reset(new VehiclePropValue(value));
        return StatusCode::OK;
    }
    LOG(INFO) << "set(INITIAL_USER_INFO) called from Android: " << toString(value);

    int32_t requestId = value.value.int32Values[0];

    // Create the update property and set common values
    auto updatedValue = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
    updatedValue->prop = INITIAL_USER_INFO;
    updatedValue->timestamp = elapsedRealtimeNano();

    if (mInitialUserResponseFromCmd == nullptr) {
        updatedValue->value.int32Values.resize(2);
        updatedValue->value.int32Values[0] = requestId;
        updatedValue->value.int32Values[1] = (int32_t)InitialUserInfoResponseAction::DEFAULT;
        LOG(INFO) << "no lshal response; returning InitialUserInfoResponseAction::DEFAULT: "
                  << toString(*updatedValue);
        onPropertyValueFromCar(*updatedValue, updateStatus);
        return StatusCode::OK;
    }

    // mInitialUserResponseFromCmd is used for just one request
    std::unique_ptr<VehiclePropValue> response = std::move(mInitialUserResponseFromCmd);

    // TODO(b/138709788): rather than populate the raw values directly, it should use the
    // libraries that convert a InitialUserInfoResponse into a VehiclePropValue)

    switch (response->areaId) {
        case 1:
            LOG(INFO) << "returning response with right request id";
            *updatedValue = *response;
            updatedValue->areaId = 0;
            updatedValue->value.int32Values[0] = requestId;
            break;
        case 2:
            LOG(INFO) << "returning response with wrong request id";
            *updatedValue = *response;
            updatedValue->areaId = 0;
            updatedValue->value.int32Values[0] = -requestId;
            break;
        case 3:
            LOG(INFO) << "not generating a property change event because of lshal prop: "
                      << toString(*response);
            return StatusCode::OK;
        default:
            LOG(ERROR) << "invalid action on lshal response: " << toString(*response);
            return StatusCode::INTERNAL_ERROR;
    }

    LOG(INFO) << "updating property to: " << toString(*updatedValue);
    onPropertyValueFromCar(*updatedValue, updateStatus);
    return StatusCode::OK;
}
  private:
    void dumpUserHal(int fd, std::string indent);
};

bool EmulatedVehicleServer::onDump(const hidl_handle& handle,
bool EmulatedPassthroughConnector::onDump(const hidl_handle& handle,
                                          const hidl_vec<hidl_string>& options) {
    int fd = handle->data[0];

@@ -401,7 +71,7 @@ bool EmulatedVehicleServer::onDump(const hidl_handle& handle,
    return true;
}

void EmulatedVehicleServer::dumpUserHal(int fd, std::string indent) {
void EmulatedPassthroughConnector::dumpUserHal(int fd, std::string indent) {
    if (mInitialUserResponseFromCmd != nullptr) {
        dprintf(fd, "%sInitial User Info: %s\n", indent.c_str(),
                toString(*mInitialUserResponseFromCmd).c_str());
@@ -410,7 +80,7 @@ void EmulatedVehicleServer::dumpUserHal(int fd, std::string indent) {
    }
}

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

+5 −64
Original line number Diff line number Diff line
@@ -18,9 +18,9 @@
#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"
#include "VehicleHalClient.h"
#include "VehicleHalServer.h"

namespace android {
namespace hardware {
@@ -30,69 +30,10 @@ namespace V2_0 {

namespace impl {

// Extension of the client/server interfaces for emulated vehicle
using PassthroughConnector = IPassThroughConnector<VehicleHalClient, VehicleHalServer>;
using PassthroughConnectorPtr = std::unique_ptr<PassthroughConnector>;

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

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

    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, bool updateStatus) override;

    bool onDump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;

    // 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};

    // TODO(b/146207078): it might be clearer to move members below to an EmulatedUserHal class
    std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
    StatusCode onSetInitialUserInfo(const VehiclePropValue& value, bool updateStatus);
    void dumpUserHal(int fd, std::string indent);
};

// Helper functions

using EmulatedPassthroughConnector =
        IPassThroughConnector<EmulatedVehicleClient, EmulatedVehicleServer>;
using EmulatedPassthroughConnectorPtr = std::unique_ptr<EmulatedPassthroughConnector>;

EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector();
PassthroughConnectorPtr makeEmulatedPassthroughConnector();

}  // namespace impl

+3 −4
Original line number Diff line number Diff line
@@ -87,12 +87,11 @@ static std::unique_ptr<Obd2SensorStore> fillDefaultObd2Frame(size_t numVendorInt
    return sensorStore;
}

EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore,
                                       EmulatedVehicleClient* client)
EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client)
    : mPropStore(propStore),
      mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
      mRecurrentTimer(
          std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)),
      mRecurrentTimer(std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this,
                                std::placeholders::_1)),
      mVehicleClient(client) {
    initStaticConfig();
    for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
+2 −2
Original line number Diff line number Diff line
@@ -46,7 +46,7 @@ namespace impl {
class EmulatedVehicleHal : public EmulatedVehicleHalIface {
public:
    EmulatedVehicleHal(VehiclePropertyStore* propStore,
                       EmulatedVehicleClient* client);
                       VehicleHalClient* client);
    ~EmulatedVehicleHal() = default;

    //  Methods from VehicleHal
@@ -85,7 +85,7 @@ private:
    VehiclePropertyStore* mPropStore;
    std::unordered_set<int32_t> mHvacPowerProps;
    RecurrentTimer mRecurrentTimer;
    EmulatedVehicleClient* mVehicleClient;
    VehicleHalClient* mVehicleClient;
};

}  // impl
Loading