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

Commit 6cb8689d authored by Hao Chen's avatar Hao Chen
Browse files

AIDL VHAL GRPC Client

Bug: 266001013
Test: `atest GRPCVehicleHardwareUnitTest`
Change-Id: I89b1c260aa2c34f353e88547a92718bb1cc37e5f
parent 3af3d5bd
Loading
Loading
Loading
Loading
+102 −0
Original line number Diff line number Diff line
// Copyright (C) 2023 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.

package {
    default_applicable_licenses: ["Android-Apache-2.0"],
}

genrule {
    name: "VehicleServerProtoStub_h@default-grpc",
    tools: [
        "aprotoc",
        "protoc-gen-grpc-cpp-plugin",
    ],
    cmd: "$(location aprotoc) -I$$(dirname $(in)) -Ihardware/interfaces/automotive/vehicle/aidl/impl/proto -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
    srcs: [
        "proto/VehicleServer.proto",
    ],
    out: [
        "VehicleServer.pb.h",
        "VehicleServer.grpc.pb.h",
    ],
    visibility: ["//visibility:private"],
}

genrule {
    name: "VehicleServerProtoStub_cc@default-grpc",
    tools: [
        "aprotoc",
        "protoc-gen-grpc-cpp-plugin",
    ],
    cmd: "$(location aprotoc) -I$$(dirname $(in)) -Ihardware/interfaces/automotive/vehicle/aidl/impl/proto -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
    srcs: [
        "proto/VehicleServer.proto",
    ],
    out: [
        "VehicleServer.pb.cc",
        "VehicleServer.grpc.pb.cc",
    ],
    visibility: ["//visibility:private"],
}

cc_library_static {
    name: "android.hardware.automotive.vehicle@default-grpc-libgrpc",
    vendor: true,
    host_supported: true,
    include_dirs: [
        "external/protobuf/src",
    ],
    generated_headers: [
        "VehicleServerProtoStub_h@default-grpc",
    ],
    export_generated_headers: [
        "VehicleServerProtoStub_h@default-grpc",
    ],
    generated_sources: [
        "VehicleServerProtoStub_cc@default-grpc",
    ],
    whole_static_libs: [
        "VehicleHalProtos",
    ],
    shared_libs: [
        "libgrpc++",
    ],
    cflags: [
        "-Wno-unused-parameter",
    ],
}

cc_library_static {
    name: "android.hardware.automotive.vehicle@default-grpc-hardware-lib",
    defaults: ["VehicleHalDefaults"],
    vendor: true,
    srcs: [
        "GRPCVehicleHardware.cpp",
    ],
    whole_static_libs: [
        "android.hardware.automotive.vehicle@default-grpc-libgrpc",
        "VehicleHalProtoMessageConverter",
    ],
    header_libs: [
        "IVehicleHardware",
    ],
    shared_libs: [
        "libgrpc++",
        "libprotobuf-cpp-full",
    ],
    export_include_dirs: ["."],
    cflags: [
        "-Wno-unused-parameter",
    ],
}
+221 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 <GRPCVehicleHardware.h>

#include "ProtoMessageConverter.h"

#include <android-base/logging.h>
#include <grpc++/grpc++.h>

#include <cstdlib>
#include <mutex>
#include <shared_mutex>
#include <utility>

namespace android::hardware::automotive::vehicle::virtualization {

static std::shared_ptr<::grpc::ChannelCredentials> getChannelCredentials() {
    // TODO(chenhaosjtuacm): get secured credentials here
    return ::grpc::InsecureChannelCredentials();
}

GRPCVehicleHardware::GRPCVehicleHardware(std::string service_addr)
    : mServiceAddr(std::move(service_addr)),
      mGrpcChannel(::grpc::CreateChannel(mServiceAddr, getChannelCredentials())),
      mGrpcStub(proto::VehicleServer::NewStub(mGrpcChannel)),
      mValuePollingThread([this] { ValuePollingLoop(); }) {}

GRPCVehicleHardware::~GRPCVehicleHardware() {
    {
        std::lock_guard lck(mShutdownMutex);
        mShuttingDownFlag.store(true);
    }
    mShutdownCV.notify_all();
    mValuePollingThread.join();
}

std::vector<aidlvhal::VehiclePropConfig> GRPCVehicleHardware::getAllPropertyConfigs() const {
    std::vector<aidlvhal::VehiclePropConfig> configs;
    ::grpc::ClientContext context;
    auto config_stream = mGrpcStub->GetAllPropertyConfig(&context, ::google::protobuf::Empty());
    proto::VehiclePropConfig protoConfig;
    while (config_stream->Read(&protoConfig)) {
        aidlvhal::VehiclePropConfig config;
        proto_msg_converter::protoToAidl(protoConfig, &config);
        configs.push_back(std::move(config));
    }
    auto grpc_status = config_stream->Finish();
    if (!grpc_status.ok()) {
        LOG(ERROR) << __func__
                   << ": GRPC GetAllPropertyConfig Failed: " << grpc_status.error_message();
    }
    return configs;
}

aidlvhal::StatusCode GRPCVehicleHardware::setValues(
        std::shared_ptr<const SetValuesCallback> callback,
        const std::vector<aidlvhal::SetValueRequest>& requests) {
    ::grpc::ClientContext context;
    proto::VehiclePropValueRequests protoRequests;
    proto::SetValueResults protoResults;
    for (const auto& request : requests) {
        auto& protoRequest = *protoRequests.add_requests();
        protoRequest.set_request_id(request.requestId);
        proto_msg_converter::aidlToProto(request.value, protoRequest.mutable_value());
    }
    // TODO(chenhaosjtuacm): Make it Async.
    auto grpc_status = mGrpcStub->SetValues(&context, protoRequests, &protoResults);
    if (!grpc_status.ok()) {
        LOG(ERROR) << __func__ << ": GRPC SetValues Failed: " << grpc_status.error_message();
        {
            std::shared_lock lck(mCallbackMutex);
            // TODO(chenhaosjtuacm): call on-set-error callback.
        }
        return aidlvhal::StatusCode::INTERNAL_ERROR;
    }
    std::vector<aidlvhal::SetValueResult> results;
    for (const auto& protoResult : protoResults.results()) {
        auto& result = results.emplace_back();
        result.requestId = protoResult.request_id();
        result.status = static_cast<aidlvhal::StatusCode>(protoResult.status());
        // TODO(chenhaosjtuacm): call on-set-error callback.
    }
    (*callback)(std::move(results));

    return aidlvhal::StatusCode::OK;
}

aidlvhal::StatusCode GRPCVehicleHardware::getValues(
        std::shared_ptr<const GetValuesCallback> callback,
        const std::vector<aidlvhal::GetValueRequest>& requests) const {
    ::grpc::ClientContext context;
    proto::VehiclePropValueRequests protoRequests;
    proto::GetValueResults protoResults;
    for (const auto& request : requests) {
        auto& protoRequest = *protoRequests.add_requests();
        protoRequest.set_request_id(request.requestId);
        proto_msg_converter::aidlToProto(request.prop, protoRequest.mutable_value());
    }
    // TODO(chenhaosjtuacm): Make it Async.
    auto grpc_status = mGrpcStub->GetValues(&context, protoRequests, &protoResults);
    if (!grpc_status.ok()) {
        LOG(ERROR) << __func__ << ": GRPC GetValues Failed: " << grpc_status.error_message();
        return aidlvhal::StatusCode::INTERNAL_ERROR;
    }
    std::vector<aidlvhal::GetValueResult> results;
    for (const auto& protoResult : protoResults.results()) {
        auto& result = results.emplace_back();
        result.requestId = protoResult.request_id();
        result.status = static_cast<aidlvhal::StatusCode>(protoResult.status());
        if (protoResult.has_value()) {
            aidlvhal::VehiclePropValue value;
            proto_msg_converter::protoToAidl(protoResult.value(), &value);
            result.prop = std::move(value);
        }
    }
    (*callback)(std::move(results));

    return aidlvhal::StatusCode::OK;
}

void GRPCVehicleHardware::registerOnPropertyChangeEvent(
        std::unique_ptr<const PropertyChangeCallback> callback) {
    std::lock_guard lck(mCallbackMutex);
    if (mOnPropChange) {
        LOG(ERROR) << __func__ << " must only be called once.";
        return;
    }
    mOnPropChange = std::move(callback);
}

void GRPCVehicleHardware::registerOnPropertySetErrorEvent(
        std::unique_ptr<const PropertySetErrorCallback> callback) {
    std::lock_guard lck(mCallbackMutex);
    if (mOnSetErr) {
        LOG(ERROR) << __func__ << " must only be called once.";
        return;
    }
    mOnSetErr = std::move(callback);
}

DumpResult GRPCVehicleHardware::dump(const std::vector<std::string>& /* options */) {
    // TODO(chenhaosjtuacm): To be implemented.
    return {};
}

aidlvhal::StatusCode GRPCVehicleHardware::checkHealth() {
    // TODO(chenhaosjtuacm): To be implemented.
    return aidlvhal::StatusCode::OK;
}

aidlvhal::StatusCode GRPCVehicleHardware::updateSampleRate(int32_t /* propId */,
                                                           int32_t /* areaId */,
                                                           float /* sampleRate */) {
    // TODO(chenhaosjtuacm): To be implemented.
    return aidlvhal::StatusCode::OK;
}

bool GRPCVehicleHardware::waitForConnected(std::chrono::milliseconds waitTime) {
    return mGrpcChannel->WaitForConnected(gpr_time_add(
            gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_millis(waitTime.count(), GPR_TIMESPAN)));
}

void GRPCVehicleHardware::ValuePollingLoop() {
    while (!mShuttingDownFlag.load()) {
        ::grpc::ClientContext context;

        bool rpc_stopped{false};
        std::thread shuttingdown_watcher([this, &rpc_stopped, &context]() {
            std::unique_lock<std::mutex> lck(mShutdownMutex);
            mShutdownCV.wait(lck, [this, &rpc_stopped]() {
                return rpc_stopped || mShuttingDownFlag.load();
            });
            context.TryCancel();
        });

        auto value_stream =
                mGrpcStub->StartPropertyValuesStream(&context, ::google::protobuf::Empty());
        LOG(INFO) << __func__ << ": GRPC Value Streaming Started";
        proto::VehiclePropValues protoValues;
        while (!mShuttingDownFlag.load() && value_stream->Read(&protoValues)) {
            std::vector<aidlvhal::VehiclePropValue> values;
            for (const auto protoValue : protoValues.values()) {
                values.push_back(aidlvhal::VehiclePropValue());
                proto_msg_converter::protoToAidl(protoValue, &values.back());
            }
            std::shared_lock lck(mCallbackMutex);
            if (mOnPropChange) {
                (*mOnPropChange)(values);
            }
        }

        {
            std::lock_guard lck(mShutdownMutex);
            rpc_stopped = true;
        }
        mShutdownCV.notify_all();
        shuttingdown_watcher.join();

        auto grpc_status = value_stream->Finish();
        // never reach here until connection lost
        LOG(ERROR) << __func__ << ": GRPC Value Streaming Failed: " << grpc_status.error_message();

        // try to reconnect
    }
}

}  // namespace android::hardware::automotive::vehicle::virtualization
+100 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */

#pragma once

#include <IVehicleHardware.h>
#include <VehicleHalTypes.h>
#include <VehicleUtils.h>
#include <android-base/result.h>

#include "VehicleServer.grpc.pb.h"
#include "VehicleServer.pb.h"

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <memory>
#include <shared_mutex>
#include <string>
#include <thread>
#include <vector>

namespace android::hardware::automotive::vehicle::virtualization {

namespace aidlvhal = ::aidl::android::hardware::automotive::vehicle;

class GRPCVehicleHardware : public IVehicleHardware {
  public:
    explicit GRPCVehicleHardware(std::string service_addr);

    ~GRPCVehicleHardware();

    // Get all the property configs.
    std::vector<aidlvhal::VehiclePropConfig> getAllPropertyConfigs() const override;

    // Set property values asynchronously. Server could return before the property set requests
    // are sent to vehicle bus or before property set confirmation is received. The callback is
    // safe to be called after the function returns and is safe to be called in a different thread.
    aidlvhal::StatusCode setValues(std::shared_ptr<const SetValuesCallback> callback,
                                   const std::vector<aidlvhal::SetValueRequest>& requests) override;

    // Get property values asynchronously. Server could return before the property values are ready.
    // The callback is safe to be called after the function returns and is safe to be called in a
    // different thread.
    aidlvhal::StatusCode getValues(
            std::shared_ptr<const GetValuesCallback> callback,
            const std::vector<aidlvhal::GetValueRequest>& requests) const override;

    // Dump debug information in the server.
    DumpResult dump(const std::vector<std::string>& options) override;

    // Check whether the system is healthy, return {@code StatusCode::OK} for healthy.
    aidlvhal::StatusCode checkHealth() override;

    // Register a callback that would be called when there is a property change event from vehicle.
    void registerOnPropertyChangeEvent(
            std::unique_ptr<const PropertyChangeCallback> callback) override;

    // Register a callback that would be called when there is a property set error event from
    // vehicle.
    void registerOnPropertySetErrorEvent(
            std::unique_ptr<const PropertySetErrorCallback> callback) override;

    // Update the sample rate for the [propId, areaId] pair.
    aidlvhal::StatusCode updateSampleRate(int32_t propId, int32_t areaId,
                                          float sampleRate) override;

    bool waitForConnected(std::chrono::milliseconds waitTime);

  private:
    void ValuePollingLoop();

    std::string mServiceAddr;
    std::shared_ptr<::grpc::Channel> mGrpcChannel;
    std::unique_ptr<proto::VehicleServer::Stub> mGrpcStub;
    std::thread mValuePollingThread;

    std::shared_mutex mCallbackMutex;
    std::unique_ptr<const PropertyChangeCallback> mOnPropChange;
    std::unique_ptr<const PropertySetErrorCallback> mOnSetErr;

    std::mutex mShutdownMutex;
    std::condition_variable mShutdownCV;
    std::atomic<bool> mShuttingDownFlag{false};
};

}  // namespace android::hardware::automotive::vehicle::virtualization
+3 −0
Original line number Diff line number Diff line
shanyu@google.com
chenhaosjtuacm@google.com
egranata@google.com
+43 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */

syntax = "proto3";

package android.hardware.automotive.vehicle.proto;

import "android/hardware/automotive/vehicle/StatusCode.proto";
import "android/hardware/automotive/vehicle/VehiclePropConfig.proto";
import "android/hardware/automotive/vehicle/VehiclePropValue.proto";
import "android/hardware/automotive/vehicle/VehiclePropValueRequest.proto";
import "google/protobuf/empty.proto";

message VehicleHalCallStatus {
    StatusCode status_code = 1;
}

message VehiclePropValues {
    repeated VehiclePropValue values = 1;
}

service VehicleServer {
    rpc GetAllPropertyConfig(google.protobuf.Empty) returns (stream VehiclePropConfig) {}

    rpc SetValues(VehiclePropValueRequests) returns (SetValueResults) {}

    rpc GetValues(VehiclePropValueRequests) returns (GetValueResults) {}

    rpc StartPropertyValuesStream(google.protobuf.Empty) returns (stream VehiclePropValues) {}
}
Loading