diff --git a/automotive/vehicle/aidl/impl/grpc/Android.bp b/automotive/vehicle/aidl/impl/grpc/Android.bp index e8f0970c4d..06c9600100 100644 --- a/automotive/vehicle/aidl/impl/grpc/Android.bp +++ b/automotive/vehicle/aidl/impl/grpc/Android.bp @@ -100,3 +100,27 @@ cc_library_static { "-Wno-unused-parameter", ], } + +cc_library_static { + name: "android.hardware.automotive.vehicle@default-grpc-server-lib", + defaults: ["VehicleHalDefaults"], + vendor: true, + srcs: [ + "GRPCVehicleProxyServer.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 index 015157d425..07422835d2 100644 --- a/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.cpp +++ b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.cpp @@ -152,21 +152,49 @@ void GRPCVehicleHardware::registerOnPropertySetErrorEvent( mOnSetErr = std::move(callback); } -DumpResult GRPCVehicleHardware::dump(const std::vector& /* options */) { - // TODO(chenhaosjtuacm): To be implemented. - return {}; +DumpResult GRPCVehicleHardware::dump(const std::vector& options) { + ::grpc::ClientContext context; + proto::DumpOptions protoDumpOptions; + proto::DumpResult protoDumpResult; + for (const auto& option : options) { + protoDumpOptions.add_options(option); + } + auto grpc_status = mGrpcStub->Dump(&context, protoDumpOptions, &protoDumpResult); + if (!grpc_status.ok()) { + LOG(ERROR) << __func__ << ": GRPC Dump Failed: " << grpc_status.error_message(); + return {}; + } + return { + .callerShouldDumpState = protoDumpResult.caller_should_dump_state(), + .buffer = protoDumpResult.buffer(), + }; } aidlvhal::StatusCode GRPCVehicleHardware::checkHealth() { - // TODO(chenhaosjtuacm): To be implemented. - return aidlvhal::StatusCode::OK; + ::grpc::ClientContext context; + proto::VehicleHalCallStatus protoStatus; + auto grpc_status = mGrpcStub->CheckHealth(&context, ::google::protobuf::Empty(), &protoStatus); + if (!grpc_status.ok()) { + LOG(ERROR) << __func__ << ": GRPC CheckHealth Failed: " << grpc_status.error_message(); + return aidlvhal::StatusCode::INTERNAL_ERROR; + } + return static_cast(protoStatus.status_code()); } -aidlvhal::StatusCode GRPCVehicleHardware::updateSampleRate(int32_t /* propId */, - int32_t /* areaId */, - float /* sampleRate */) { - // TODO(chenhaosjtuacm): To be implemented. - return aidlvhal::StatusCode::OK; +aidlvhal::StatusCode GRPCVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, + float sampleRate) { + ::grpc::ClientContext context; + proto::UpdateSampleRateRequest request; + proto::VehicleHalCallStatus protoStatus; + request.set_prop(propId); + request.set_area_id(areaId); + request.set_sample_rate(sampleRate); + auto grpc_status = mGrpcStub->UpdateSampleRate(&context, request, &protoStatus); + if (!grpc_status.ok()) { + LOG(ERROR) << __func__ << ": GRPC UpdateSampleRate Failed: " << grpc_status.error_message(); + return aidlvhal::StatusCode::INTERNAL_ERROR; + } + return static_cast(protoStatus.status_code()); } bool GRPCVehicleHardware::waitForConnected(std::chrono::milliseconds waitTime) { diff --git a/automotive/vehicle/aidl/impl/grpc/GRPCVehicleProxyServer.cpp b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleProxyServer.cpp new file mode 100644 index 0000000000..af3dd595a1 --- /dev/null +++ b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleProxyServer.cpp @@ -0,0 +1,296 @@ +/* + * 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 "GRPCVehicleProxyServer.h" + +#include "ProtoMessageConverter.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace android::hardware::automotive::vehicle::virtualization { + +std::atomic GrpcVehicleProxyServer::ConnectionDescriptor::connection_id_counter_{0}; + +static std::shared_ptr<::grpc::ServerCredentials> getServerCredentials() { + // TODO(chenhaosjtuacm): get secured credentials here + return ::grpc::InsecureServerCredentials(); +} + +GrpcVehicleProxyServer::GrpcVehicleProxyServer(std::string serverAddr, + std::unique_ptr&& hardware) + : mServiceAddr(std::move(serverAddr)), mHardware(std::move(hardware)) { + mHardware->registerOnPropertyChangeEvent( + std::make_unique( + [this](std::vector values) { + OnVehiclePropChange(values); + })); +} + +::grpc::Status GrpcVehicleProxyServer::GetAllPropertyConfig( + ::grpc::ServerContext* context, const ::google::protobuf::Empty* request, + ::grpc::ServerWriter* stream) { + for (const auto& config : mHardware->getAllPropertyConfigs()) { + proto::VehiclePropConfig protoConfig; + proto_msg_converter::aidlToProto(config, &protoConfig); + if (!stream->Write(protoConfig)) { + return ::grpc::Status(::grpc::StatusCode::ABORTED, "Connection lost."); + } + } + return ::grpc::Status::OK; +} + +::grpc::Status GrpcVehicleProxyServer::SetValues(::grpc::ServerContext* context, + const proto::VehiclePropValueRequests* requests, + proto::SetValueResults* results) { + std::vector aidlRequests; + for (const auto& protoRequest : requests->requests()) { + auto& aidlRequest = aidlRequests.emplace_back(); + aidlRequest.requestId = protoRequest.request_id(); + proto_msg_converter::protoToAidl(protoRequest.value(), &aidlRequest.value); + } + auto waitMtx = std::make_shared(); + auto waitCV = std::make_shared(); + auto complete = std::make_shared(false); + auto tmpResults = std::make_shared(); + auto aidlStatus = mHardware->setValues( + std::make_shared( + [waitMtx, waitCV, complete, + tmpResults](std::vector setValueResults) { + for (const auto& aidlResult : setValueResults) { + auto& protoResult = *tmpResults->add_results(); + protoResult.set_request_id(aidlResult.requestId); + protoResult.set_status( + static_cast(aidlResult.status)); + } + { + std::lock_guard lck(*waitMtx); + *complete = true; + } + waitCV->notify_all(); + }), + aidlRequests); + if (aidlStatus != aidlvhal::StatusCode::OK) { + return ::grpc::Status(::grpc::StatusCode::INTERNAL, + "The underlying hardware fails to set values, VHAL status: " + + toString(aidlStatus)); + } + std::unique_lock lck(*waitMtx); + bool success = waitCV->wait_for(lck, kHardwareOpTimeout, [complete] { return *complete; }); + if (!success) { + return ::grpc::Status(::grpc::StatusCode::INTERNAL, + "The underlying hardware set values timeout."); + } + *results = std::move(*tmpResults); + return ::grpc::Status::OK; +} + +::grpc::Status GrpcVehicleProxyServer::GetValues(::grpc::ServerContext* context, + const proto::VehiclePropValueRequests* requests, + proto::GetValueResults* results) { + std::vector aidlRequests; + for (const auto& protoRequest : requests->requests()) { + auto& aidlRequest = aidlRequests.emplace_back(); + aidlRequest.requestId = protoRequest.request_id(); + proto_msg_converter::protoToAidl(protoRequest.value(), &aidlRequest.prop); + } + auto waitMtx = std::make_shared(); + auto waitCV = std::make_shared(); + auto complete = std::make_shared(false); + auto tmpResults = std::make_shared(); + auto aidlStatus = mHardware->getValues( + std::make_shared( + [waitMtx, waitCV, complete, + tmpResults](std::vector getValueResults) { + for (const auto& aidlResult : getValueResults) { + auto& protoResult = *tmpResults->add_results(); + protoResult.set_request_id(aidlResult.requestId); + protoResult.set_status( + static_cast(aidlResult.status)); + if (aidlResult.prop) { + auto* valuePtr = protoResult.mutable_value(); + proto_msg_converter::aidlToProto(*aidlResult.prop, valuePtr); + } + } + { + std::lock_guard lck(*waitMtx); + *complete = true; + } + waitCV->notify_all(); + }), + aidlRequests); + if (aidlStatus != aidlvhal::StatusCode::OK) { + return ::grpc::Status(::grpc::StatusCode::INTERNAL, + "The underlying hardware fails to get values, VHAL status: " + + toString(aidlStatus)); + } + std::unique_lock lck(*waitMtx); + bool success = waitCV->wait_for(lck, kHardwareOpTimeout, [complete] { return *complete; }); + if (!success) { + return ::grpc::Status(::grpc::StatusCode::INTERNAL, + "The underlying hardware get values timeout."); + } + *results = std::move(*tmpResults); + return ::grpc::Status::OK; +} + +::grpc::Status GrpcVehicleProxyServer::UpdateSampleRate( + ::grpc::ServerContext* context, const proto::UpdateSampleRateRequest* request, + proto::VehicleHalCallStatus* status) { + const auto status_code = mHardware->updateSampleRate(request->prop(), request->area_id(), + request->sample_rate()); + status->set_status_code(static_cast(status_code)); + return ::grpc::Status::OK; +} + +::grpc::Status GrpcVehicleProxyServer::CheckHealth(::grpc::ServerContext* context, + const ::google::protobuf::Empty*, + proto::VehicleHalCallStatus* status) { + status->set_status_code(static_cast(mHardware->checkHealth())); + return ::grpc::Status::OK; +} + +::grpc::Status GrpcVehicleProxyServer::Dump(::grpc::ServerContext* context, + const proto::DumpOptions* options, + proto::DumpResult* result) { + std::vector dumpOptionStrings(options->options().begin(), + options->options().end()); + auto dumpResult = mHardware->dump(dumpOptionStrings); + result->set_caller_should_dump_state(dumpResult.callerShouldDumpState); + result->set_buffer(dumpResult.buffer); + return ::grpc::Status::OK; +} + +::grpc::Status GrpcVehicleProxyServer::StartPropertyValuesStream( + ::grpc::ServerContext* context, const ::google::protobuf::Empty* request, + ::grpc::ServerWriter* stream) { + auto conn = std::make_shared(stream); + { + std::lock_guard lck(mConnectionMutex); + mValueStreamingConnections.push_back(conn); + } + conn->Wait(); + LOG(ERROR) << __func__ << ": Stream lost, ID : " << conn->ID(); + return ::grpc::Status(::grpc::StatusCode::ABORTED, "Connection lost."); +} + +void GrpcVehicleProxyServer::OnVehiclePropChange( + const std::vector& values) { + std::unordered_set brokenConn; + proto::VehiclePropValues protoValues; + for (const auto& value : values) { + auto* protoValuePtr = protoValues.add_values(); + proto_msg_converter::aidlToProto(value, protoValuePtr); + } + { + std::shared_lock read_lock(mConnectionMutex); + for (auto& connection : mValueStreamingConnections) { + auto writeOK = connection->Write(protoValues); + if (!writeOK) { + LOG(ERROR) << __func__ + << ": Server Write failed, connection lost. ID: " << connection->ID(); + brokenConn.insert(connection->ID()); + } + } + } + if (brokenConn.empty()) { + return; + } + std::unique_lock write_lock(mConnectionMutex); + mValueStreamingConnections.erase( + std::remove_if(mValueStreamingConnections.begin(), mValueStreamingConnections.end(), + [&brokenConn](const auto& conn) { + return brokenConn.find(conn->ID()) != brokenConn.end(); + }), + mValueStreamingConnections.end()); +} + +GrpcVehicleProxyServer& GrpcVehicleProxyServer::Start() { + if (mServer) { + LOG(WARNING) << __func__ << ": GrpcVehicleProxyServer has already started."; + return *this; + } + ::grpc::ServerBuilder builder; + builder.RegisterService(this); + builder.AddListeningPort(mServiceAddr, getServerCredentials()); + mServer = builder.BuildAndStart(); + CHECK(mServer) << __func__ << ": failed to create the GRPC server, " + << "please make sure the configuration and permissions are correct"; + return *this; +} + +GrpcVehicleProxyServer& GrpcVehicleProxyServer::Shutdown() { + std::shared_lock read_lock(mConnectionMutex); + for (auto& conn : mValueStreamingConnections) { + conn->Shutdown(); + } + if (mServer) { + mServer->Shutdown(); + } + return *this; +} + +void GrpcVehicleProxyServer::Wait() { + if (mServer) { + mServer->Wait(); + } + mServer.reset(); +} + +GrpcVehicleProxyServer::ConnectionDescriptor::~ConnectionDescriptor() { + Shutdown(); +} + +bool GrpcVehicleProxyServer::ConnectionDescriptor::Write(const proto::VehiclePropValues& values) { + if (!mStream) { + LOG(ERROR) << __func__ << ": Empty stream. ID: " << ID(); + Shutdown(); + return false; + } + { + std::lock_guard lck(*mMtx); + if (!mShutdownFlag && mStream->Write(values)) { + return true; + } else { + LOG(ERROR) << __func__ << ": Server Write failed, connection lost. ID: " << ID(); + } + } + Shutdown(); + return false; +} + +void GrpcVehicleProxyServer::ConnectionDescriptor::Wait() { + std::unique_lock lck(*mMtx); + mCV->wait(lck, [this] { return mShutdownFlag; }); +} + +void GrpcVehicleProxyServer::ConnectionDescriptor::Shutdown() { + { + std::lock_guard lck(*mMtx); + mShutdownFlag = true; + } + mCV->notify_all(); +} + +} // namespace android::hardware::automotive::vehicle::virtualization diff --git a/automotive/vehicle/aidl/impl/grpc/GRPCVehicleProxyServer.h b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleProxyServer.h new file mode 100644 index 0000000000..3596354539 --- /dev/null +++ b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleProxyServer.h @@ -0,0 +1,122 @@ +/* + * 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 "VehicleServer.grpc.pb.h" +#include "VehicleServer.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android::hardware::automotive::vehicle::virtualization { + +namespace aidlvhal = ::aidl::android::hardware::automotive::vehicle; + +// Connect other GRPC vehicle hardware(s) to the underlying vehicle hardware. +class GrpcVehicleProxyServer : public proto::VehicleServer::Service { + public: + GrpcVehicleProxyServer(std::string serverAddr, std::unique_ptr&& hardware); + + ::grpc::Status GetAllPropertyConfig( + ::grpc::ServerContext* context, const ::google::protobuf::Empty* request, + ::grpc::ServerWriter* stream) override; + + ::grpc::Status SetValues(::grpc::ServerContext* context, + const proto::VehiclePropValueRequests* requests, + proto::SetValueResults* results) override; + + ::grpc::Status GetValues(::grpc::ServerContext* context, + const proto::VehiclePropValueRequests* requests, + proto::GetValueResults* results) override; + + ::grpc::Status UpdateSampleRate(::grpc::ServerContext* context, + const proto::UpdateSampleRateRequest* request, + proto::VehicleHalCallStatus* status) override; + + ::grpc::Status CheckHealth(::grpc::ServerContext* context, const ::google::protobuf::Empty*, + proto::VehicleHalCallStatus* status) override; + + ::grpc::Status Dump(::grpc::ServerContext* context, const proto::DumpOptions* options, + proto::DumpResult* result) override; + + ::grpc::Status StartPropertyValuesStream( + ::grpc::ServerContext* context, const ::google::protobuf::Empty* request, + ::grpc::ServerWriter* stream) override; + + GrpcVehicleProxyServer& Start(); + + GrpcVehicleProxyServer& Shutdown(); + + void Wait(); + + private: + void OnVehiclePropChange(const std::vector& values); + + // We keep long-lasting connection for streaming the prop values. + struct ConnectionDescriptor { + explicit ConnectionDescriptor(::grpc::ServerWriter* stream) + : mStream(stream), + mConnectionID(connection_id_counter_.fetch_add(1) + 1), + mMtx(std::make_unique()), + mCV(std::make_unique()) {} + + ConnectionDescriptor(const ConnectionDescriptor&) = delete; + ConnectionDescriptor(ConnectionDescriptor&& cd) = default; + ConnectionDescriptor& operator=(const ConnectionDescriptor&) = delete; + ConnectionDescriptor& operator=(ConnectionDescriptor&& cd) = default; + + ~ConnectionDescriptor(); + + uint64_t ID() const { return mConnectionID; } + + bool Write(const proto::VehiclePropValues& values); + + void Wait(); + + void Shutdown(); + + private: + ::grpc::ServerWriter* mStream; + uint64_t mConnectionID{0}; + std::unique_ptr mMtx; + std::unique_ptr mCV; + bool mShutdownFlag{false}; + + static std::atomic connection_id_counter_; + }; + + std::string mServiceAddr; + std::unique_ptr<::grpc::Server> mServer{nullptr}; + std::unique_ptr mHardware; + + std::shared_mutex mConnectionMutex; + std::vector> mValueStreamingConnections; + + static constexpr auto kHardwareOpTimeout = std::chrono::seconds(1); +}; + +} // namespace android::hardware::automotive::vehicle::virtualization diff --git a/automotive/vehicle/aidl/impl/grpc/proto/VehicleServer.proto b/automotive/vehicle/aidl/impl/grpc/proto/VehicleServer.proto index 349019595b..22b11d8c87 100644 --- a/automotive/vehicle/aidl/impl/grpc/proto/VehicleServer.proto +++ b/automotive/vehicle/aidl/impl/grpc/proto/VehicleServer.proto @@ -18,20 +18,14 @@ syntax = "proto3"; package android.hardware.automotive.vehicle.proto; +import "android/hardware/automotive/vehicle/DumpOptions.proto"; +import "android/hardware/automotive/vehicle/DumpResult.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) {} @@ -39,5 +33,11 @@ service VehicleServer { rpc GetValues(VehiclePropValueRequests) returns (GetValueResults) {} + rpc UpdateSampleRate(UpdateSampleRateRequest) returns (VehicleHalCallStatus) {} + + rpc CheckHealth(google.protobuf.Empty) returns (VehicleHalCallStatus) {} + + rpc Dump(DumpOptions) returns (DumpResult) {} + 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 index efdf4ca3c1..e53826f7f3 100644 --- a/automotive/vehicle/aidl/impl/grpc/test/Android.bp +++ b/automotive/vehicle/aidl/impl/grpc/test/Android.bp @@ -44,3 +44,31 @@ cc_test { ], test_suites: ["device-tests"], } + +cc_test { + name: "GRPCVehicleProxyServerUnitTest", + vendor: true, + srcs: ["GRPCVehicleProxyServerUnitTest.cpp"], + header_libs: [ + "IVehicleHardware", + ], + static_libs: [ + "android.hardware.automotive.vehicle@default-grpc-hardware-lib", + "android.hardware.automotive.vehicle@default-grpc-server-lib", + "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/GRPCVehicleProxyServerUnitTest.cpp b/automotive/vehicle/aidl/impl/grpc/test/GRPCVehicleProxyServerUnitTest.cpp new file mode 100644 index 0000000000..49e6fc9d21 --- /dev/null +++ b/automotive/vehicle/aidl/impl/grpc/test/GRPCVehicleProxyServerUnitTest.cpp @@ -0,0 +1,147 @@ +// 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 "GRPCVehicleProxyServer.h" +#include "IVehicleHardware.h" +#include "VehicleServer.grpc.pb.h" +#include "VehicleServer.pb.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace android::hardware::automotive::vehicle::virtualization { + +const std::string kFakeServerAddr = "0.0.0.0:54321"; + +class VehicleHardwareForTest : public IVehicleHardware { + public: + void registerOnPropertyChangeEvent( + std::unique_ptr callback) override { + mOnProp = std::move(callback); + } + + void onPropertyEvent( + std::vector values) { + if (mOnProp) { + (*mOnProp)(std::move(values)); + } + } + + // Functions that we do not care. + std::vector + getAllPropertyConfigs() const override { + return {}; + } + + aidl::android::hardware::automotive::vehicle::StatusCode setValues( + std::shared_ptr callback, + const std::vector& + requests) override { + return aidl::android::hardware::automotive::vehicle::StatusCode::OK; + } + + aidl::android::hardware::automotive::vehicle::StatusCode getValues( + std::shared_ptr callback, + const std::vector& + requests) const override { + return aidl::android::hardware::automotive::vehicle::StatusCode::OK; + } + + DumpResult dump(const std::vector& options) override { return {}; } + + aidl::android::hardware::automotive::vehicle::StatusCode checkHealth() override { + return aidl::android::hardware::automotive::vehicle::StatusCode::OK; + } + + void registerOnPropertySetErrorEvent( + std::unique_ptr callback) override {} + + private: + std::unique_ptr mOnProp; +}; + +TEST(GRPCVehicleProxyServerUnitTest, ClientConnectDisconnect) { + auto testHardware = std::make_unique(); + // HACK: manipulate the underlying hardware via raw pointer for testing. + auto* testHardwareRaw = testHardware.get(); + auto vehicleServer = + std::make_unique(kFakeServerAddr, std::move(testHardware)); + vehicleServer->Start(); + + constexpr auto kWaitForConnectionMaxTime = std::chrono::seconds(5); + constexpr auto kWaitForStreamStartTime = std::chrono::seconds(1); + constexpr auto kWaitForUpdateDeliveryTime = std::chrono::milliseconds(100); + + auto updateReceived1 = std::make_shared(false); + auto vehicleHardware1 = std::make_unique(kFakeServerAddr); + vehicleHardware1->registerOnPropertyChangeEvent( + std::make_unique( + [updateReceived1](const auto&) { *updateReceived1 = true; })); + EXPECT_TRUE(vehicleHardware1->waitForConnected(kWaitForConnectionMaxTime)); + std::this_thread::sleep_for(kWaitForStreamStartTime); + + // Client hardware 1 received update from the server. + EXPECT_FALSE(*updateReceived1); + testHardwareRaw->onPropertyEvent({}); + // Wait for the update delivery. + std::this_thread::sleep_for(kWaitForUpdateDeliveryTime); + EXPECT_TRUE(*updateReceived1); + + // Reset. + *updateReceived1 = false; + + auto updateReceived2 = std::make_shared(false); + auto vehicleHardware2 = std::make_unique(kFakeServerAddr); + vehicleHardware2->registerOnPropertyChangeEvent( + std::make_unique( + [updateReceived2](const auto&) { *updateReceived2 = true; })); + EXPECT_TRUE(vehicleHardware2->waitForConnected(kWaitForConnectionMaxTime)); + std::this_thread::sleep_for(kWaitForStreamStartTime); + + // Both client hardware 1 and 2 received update from the server. + EXPECT_FALSE(*updateReceived1); + EXPECT_FALSE(*updateReceived2); + testHardwareRaw->onPropertyEvent({}); + // Wait for the update delivery. + std::this_thread::sleep_for(kWaitForUpdateDeliveryTime); + EXPECT_TRUE(*updateReceived1); + EXPECT_TRUE(*updateReceived2); + + // Reset. + *updateReceived1 = false; + *updateReceived2 = false; + + vehicleHardware1.reset(); + + // Client 1 exited, only client hardware 2 received update from the server. + EXPECT_FALSE(*updateReceived1); + EXPECT_FALSE(*updateReceived2); + testHardwareRaw->onPropertyEvent({}); + // Wait for the update delivery. + std::this_thread::sleep_for(kWaitForUpdateDeliveryTime); + EXPECT_FALSE(*updateReceived1); + EXPECT_TRUE(*updateReceived2); + + vehicleServer->Shutdown().Wait(); +} + +} // namespace android::hardware::automotive::vehicle::virtualization diff --git a/automotive/vehicle/aidl/impl/proto/Android.bp b/automotive/vehicle/aidl/impl/proto/Android.bp index 709307d3e1..56fad7ed91 100644 --- a/automotive/vehicle/aidl/impl/proto/Android.bp +++ b/automotive/vehicle/aidl/impl/proto/Android.bp @@ -40,6 +40,7 @@ genrule { ":VehicleHalProtoFiles", ], out: [ + "android/hardware/automotive/vehicle/DumpOptions.pb.h", "android/hardware/automotive/vehicle/DumpResult.pb.h", "android/hardware/automotive/vehicle/StatusCode.pb.h", "android/hardware/automotive/vehicle/VehicleAreaConfig.pb.h", @@ -63,6 +64,7 @@ genrule { ":VehicleHalProtoFiles", ], out: [ + "android/hardware/automotive/vehicle/DumpOptions.pb.cc", "android/hardware/automotive/vehicle/DumpResult.pb.cc", "android/hardware/automotive/vehicle/StatusCode.pb.cc", "android/hardware/automotive/vehicle/VehicleAreaConfig.pb.cc", diff --git a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/DumpOptions.proto b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/DumpOptions.proto new file mode 100644 index 0000000000..4bed927288 --- /dev/null +++ b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/DumpOptions.proto @@ -0,0 +1,23 @@ +/* + * 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; + +message DumpOptions { + repeated string options = 1; +} diff --git a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/StatusCode.proto b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/StatusCode.proto index 97cb0f88b2..63d7933e7d 100644 --- a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/StatusCode.proto +++ b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/StatusCode.proto @@ -39,3 +39,7 @@ enum StatusCode { /* Something unexpected has happened in Vehicle HAL */ INTERNAL_ERROR = 5; }; + +message VehicleHalCallStatus { + StatusCode status_code = 1; +} diff --git a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehiclePropValue.proto b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehiclePropValue.proto index 80c73cbb77..dda9ff5119 100644 --- a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehiclePropValue.proto +++ b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehiclePropValue.proto @@ -52,3 +52,7 @@ message VehiclePropValue { /* This is used for properties of type VehiclePropertyType#STRING */ string string_value = 9; }; + +message VehiclePropValues { + repeated VehiclePropValue values = 1; +} diff --git a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehiclePropValueRequest.proto b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehiclePropValueRequest.proto index 749ad6adc8..c347a800c3 100644 --- a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehiclePropValueRequest.proto +++ b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehiclePropValueRequest.proto @@ -26,6 +26,17 @@ message VehiclePropValueRequest { VehiclePropValue value = 2; }; +message UpdateSampleRateRequest { + /* Property identifier */ + int32 prop = 1; + + /* Area type(s) for non-global property it must be one of the value from + * VehicleArea* enums or 0 for global properties. */ + int32 area_id = 2; + + float sample_rate = 3; +}; + message SetValueResult { int64 request_id = 1; StatusCode status = 2;