summaryrefslogtreecommitdiff
path: root/automotive/vehicle
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2023-04-13 00:37:33 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2023-04-13 00:37:33 +0000
commitd6818349ef03e44bd19b4fa6bef731a0cdc826c2 (patch)
treea7aa3c9be6f316858660e495a545418d37f674bc /automotive/vehicle
parenta9eeef439547ef7c0f3ef9be12750e20eccb0a0f (diff)
parent6cb8689d10900efab12b1fae57427a5c5611327f (diff)
downloadinterfaces-d6818349ef03e44bd19b4fa6bef731a0cdc826c2.tar.gz
Merge "AIDL VHAL GRPC Client" into udc-dev
Diffstat (limited to 'automotive/vehicle')
-rw-r--r--automotive/vehicle/aidl/impl/grpc/Android.bp102
-rw-r--r--automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.cpp221
-rw-r--r--automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.h100
-rw-r--r--automotive/vehicle/aidl/impl/grpc/OWNERS3
-rw-r--r--automotive/vehicle/aidl/impl/grpc/proto/VehicleServer.proto43
-rw-r--r--automotive/vehicle/aidl/impl/grpc/test/Android.bp46
-rw-r--r--automotive/vehicle/aidl/impl/grpc/test/GRPCVehicleHardwareUnitTest.cpp94
7 files changed, 609 insertions, 0 deletions
diff --git a/automotive/vehicle/aidl/impl/grpc/Android.bp b/automotive/vehicle/aidl/impl/grpc/Android.bp
new file mode 100644
index 0000000000..e8f0970c4d
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/grpc/Android.bp
@@ -0,0 +1,102 @@
+// 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",
+ ],
+}
diff --git a/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.cpp b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.cpp
new file mode 100644
index 0000000000..015157d425
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.cpp
@@ -0,0 +1,221 @@
+/*
+ * 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
diff --git a/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.h b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.h
new file mode 100644
index 0000000000..90588aa375
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.h
@@ -0,0 +1,100 @@
+/*
+ * 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
diff --git a/automotive/vehicle/aidl/impl/grpc/OWNERS b/automotive/vehicle/aidl/impl/grpc/OWNERS
new file mode 100644
index 0000000000..7a96f236d1
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/grpc/OWNERS
@@ -0,0 +1,3 @@
+shanyu@google.com
+chenhaosjtuacm@google.com
+egranata@google.com
diff --git a/automotive/vehicle/aidl/impl/grpc/proto/VehicleServer.proto b/automotive/vehicle/aidl/impl/grpc/proto/VehicleServer.proto
new file mode 100644
index 0000000000..349019595b
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/grpc/proto/VehicleServer.proto
@@ -0,0 +1,43 @@
+/*
+ * 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) {}
+}
diff --git a/automotive/vehicle/aidl/impl/grpc/test/Android.bp b/automotive/vehicle/aidl/impl/grpc/test/Android.bp
new file mode 100644
index 0000000000..efdf4ca3c1
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/grpc/test/Android.bp
@@ -0,0 +1,46 @@
+// 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"],
+}
+
+cc_test {
+ name: "GRPCVehicleHardwareUnitTest",
+ vendor: true,
+ srcs: ["GRPCVehicleHardwareUnitTest.cpp"],
+ whole_static_libs: [
+ "android.hardware.automotive.vehicle@default-grpc-hardware-lib",
+ ],
+ header_libs: [
+ "IVehicleHardware",
+ ],
+ static_libs: [
+ "libgtest",
+ "libgmock",
+ ],
+ shared_libs: [
+ "libgrpc++",
+ "libprotobuf-cpp-full",
+ ],
+ // libgrpc++.so is installed as root, require root to access it.
+ require_root: true,
+ defaults: [
+ "VehicleHalDefaults",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ ],
+ test_suites: ["device-tests"],
+}
diff --git a/automotive/vehicle/aidl/impl/grpc/test/GRPCVehicleHardwareUnitTest.cpp b/automotive/vehicle/aidl/impl/grpc/test/GRPCVehicleHardwareUnitTest.cpp
new file mode 100644
index 0000000000..f578021f1b
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/grpc/test/GRPCVehicleHardwareUnitTest.cpp
@@ -0,0 +1,94 @@
+// 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 "VehicleServer.grpc.pb.h"
+#include "VehicleServer.pb.h"
+
+#include <gmock/gmock.h>
+#include <grpc++/grpc++.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <memory>
+#include <string>
+
+namespace android::hardware::automotive::vehicle::virtualization {
+
+const std::string kFakeServerAddr = "0.0.0.0:54321";
+
+class FakeVehicleServer : public proto::VehicleServer::Service {
+ public:
+ ::grpc::Status StartPropertyValuesStream(
+ ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+ ::grpc::ServerWriter<proto::VehiclePropValues>* stream) override {
+ stream->Write(proto::VehiclePropValues());
+ // A fake disconnection.
+ return ::grpc::Status(::grpc::StatusCode::ABORTED, "Connection lost.");
+ }
+
+ // Functions that we do not care.
+ ::grpc::Status GetAllPropertyConfig(
+ ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+ ::grpc::ServerWriter<proto::VehiclePropConfig>* stream) override {
+ return ::grpc::Status::OK;
+ }
+
+ ::grpc::Status SetValues(::grpc::ServerContext* context,
+ const proto::VehiclePropValueRequests* requests,
+ proto::SetValueResults* results) override {
+ return ::grpc::Status::OK;
+ }
+
+ ::grpc::Status GetValues(::grpc::ServerContext* context,
+ const proto::VehiclePropValueRequests* requests,
+ proto::GetValueResults* results) override {
+ return ::grpc::Status::OK;
+ }
+};
+
+TEST(GRPCVehicleHardwareUnitTest, Reconnect) {
+ auto receivedUpdate = std::make_shared<std::atomic<int>>(0);
+ auto vehicleHardware = std::make_unique<GRPCVehicleHardware>(kFakeServerAddr);
+ vehicleHardware->registerOnPropertyChangeEvent(
+ std::make_unique<const IVehicleHardware::PropertyChangeCallback>(
+ [receivedUpdate](const auto&) { receivedUpdate->fetch_add(1); }));
+
+ constexpr size_t kServerRestartTimes = 5;
+ for (size_t serverStart = 0; serverStart < kServerRestartTimes; ++serverStart) {
+ EXPECT_EQ(receivedUpdate->load(), 0);
+ auto fakeServer = std::make_unique<FakeVehicleServer>();
+ ::grpc::ServerBuilder builder;
+ builder.RegisterService(fakeServer.get());
+ builder.AddListeningPort(kFakeServerAddr, ::grpc::InsecureServerCredentials());
+ auto grpcServer = builder.BuildAndStart();
+
+ // Wait until the vehicle hardware received the second update (after one fake
+ // disconnection).
+ constexpr auto kMaxWaitTime = std::chrono::seconds(5);
+ auto startTime = std::chrono::steady_clock::now();
+ while (receivedUpdate->load() <= 1 &&
+ std::chrono::steady_clock::now() - startTime < kMaxWaitTime)
+ ;
+
+ grpcServer->Shutdown();
+ grpcServer->Wait();
+ EXPECT_GT(receivedUpdate->load(), 1);
+
+ // Reset for the next round.
+ receivedUpdate->store(0);
+ }
+}
+
+} // namespace android::hardware::automotive::vehicle::virtualization