diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2023-04-13 00:37:33 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2023-04-13 00:37:33 +0000 |
commit | d6818349ef03e44bd19b4fa6bef731a0cdc826c2 (patch) | |
tree | a7aa3c9be6f316858660e495a545418d37f674bc /automotive/vehicle | |
parent | a9eeef439547ef7c0f3ef9be12750e20eccb0a0f (diff) | |
parent | 6cb8689d10900efab12b1fae57427a5c5611327f (diff) | |
download | interfaces-d6818349ef03e44bd19b4fa6bef731a0cdc826c2.tar.gz |
Merge "AIDL VHAL GRPC Client" into udc-dev
Diffstat (limited to 'automotive/vehicle')
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 |