Merge Android U (ab/10368041)

Bug: 291102124
Merged-In: I7b6fffac2ada0e039f79bad8cc9b4d954e9c3460
Change-Id: I9466127d8d0fa38df36ca99f704853b2db871e67
diff --git a/automotive/vehicle/aidl/impl/grpc/Android.bp b/automotive/vehicle/aidl/impl/grpc/Android.bp
new file mode 100644
index 0000000..06c9600
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/grpc/Android.bp
@@ -0,0 +1,126 @@
+// 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",
+    ],
+}
+
+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
new file mode 100644
index 0000000..0742283
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.cpp
@@ -0,0 +1,249 @@
+/*
+ * 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) {
+    ::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() {
+    ::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<aidlvhal::StatusCode>(protoStatus.status_code());
+}
+
+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<aidlvhal::StatusCode>(protoStatus.status_code());
+}
+
+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 0000000..e740da7
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.h
@@ -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.
+ */
+
+#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 <grpc++/grpc++.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/GRPCVehicleProxyServer.cpp b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleProxyServer.cpp
new file mode 100644
index 0000000..af3dd59
--- /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 <grpc++/grpc++.h>
+
+#include <android-base/logging.h>
+
+#include <algorithm>
+#include <condition_variable>
+#include <mutex>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::automotive::vehicle::virtualization {
+
+std::atomic<uint64_t> 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<IVehicleHardware>&& hardware)
+    : mServiceAddr(std::move(serverAddr)), mHardware(std::move(hardware)) {
+    mHardware->registerOnPropertyChangeEvent(
+            std::make_unique<const IVehicleHardware::PropertyChangeCallback>(
+                    [this](std::vector<aidlvhal::VehiclePropValue> values) {
+                        OnVehiclePropChange(values);
+                    }));
+}
+
+::grpc::Status GrpcVehicleProxyServer::GetAllPropertyConfig(
+        ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+        ::grpc::ServerWriter<proto::VehiclePropConfig>* 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<aidlvhal::SetValueRequest> 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<std::mutex>();
+    auto waitCV = std::make_shared<std::condition_variable>();
+    auto complete = std::make_shared<bool>(false);
+    auto tmpResults = std::make_shared<proto::SetValueResults>();
+    auto aidlStatus = mHardware->setValues(
+            std::make_shared<const IVehicleHardware::SetValuesCallback>(
+                    [waitMtx, waitCV, complete,
+                     tmpResults](std::vector<aidlvhal::SetValueResult> setValueResults) {
+                        for (const auto& aidlResult : setValueResults) {
+                            auto& protoResult = *tmpResults->add_results();
+                            protoResult.set_request_id(aidlResult.requestId);
+                            protoResult.set_status(
+                                    static_cast<proto::StatusCode>(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<aidlvhal::GetValueRequest> 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<std::mutex>();
+    auto waitCV = std::make_shared<std::condition_variable>();
+    auto complete = std::make_shared<bool>(false);
+    auto tmpResults = std::make_shared<proto::GetValueResults>();
+    auto aidlStatus = mHardware->getValues(
+            std::make_shared<const IVehicleHardware::GetValuesCallback>(
+                    [waitMtx, waitCV, complete,
+                     tmpResults](std::vector<aidlvhal::GetValueResult> getValueResults) {
+                        for (const auto& aidlResult : getValueResults) {
+                            auto& protoResult = *tmpResults->add_results();
+                            protoResult.set_request_id(aidlResult.requestId);
+                            protoResult.set_status(
+                                    static_cast<proto::StatusCode>(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<proto::StatusCode>(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<proto::StatusCode>(mHardware->checkHealth()));
+    return ::grpc::Status::OK;
+}
+
+::grpc::Status GrpcVehicleProxyServer::Dump(::grpc::ServerContext* context,
+                                            const proto::DumpOptions* options,
+                                            proto::DumpResult* result) {
+    std::vector<std::string> 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<proto::VehiclePropValues>* stream) {
+    auto conn = std::make_shared<ConnectionDescriptor>(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<aidlvhal::VehiclePropValue>& values) {
+    std::unordered_set<uint64_t> 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 0000000..3596354
--- /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 <grpc++/grpc++.h>
+
+#include <atomic>
+#include <chrono>
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <shared_mutex>
+#include <string>
+#include <utility>
+
+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<IVehicleHardware>&& hardware);
+
+    ::grpc::Status GetAllPropertyConfig(
+            ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+            ::grpc::ServerWriter<proto::VehiclePropConfig>* 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<proto::VehiclePropValues>* stream) override;
+
+    GrpcVehicleProxyServer& Start();
+
+    GrpcVehicleProxyServer& Shutdown();
+
+    void Wait();
+
+  private:
+    void OnVehiclePropChange(const std::vector<aidlvhal::VehiclePropValue>& values);
+
+    // We keep long-lasting connection for streaming the prop values.
+    struct ConnectionDescriptor {
+        explicit ConnectionDescriptor(::grpc::ServerWriter<proto::VehiclePropValues>* stream)
+            : mStream(stream),
+              mConnectionID(connection_id_counter_.fetch_add(1) + 1),
+              mMtx(std::make_unique<std::mutex>()),
+              mCV(std::make_unique<std::condition_variable>()) {}
+
+        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<proto::VehiclePropValues>* mStream;
+        uint64_t mConnectionID{0};
+        std::unique_ptr<std::mutex> mMtx;
+        std::unique_ptr<std::condition_variable> mCV;
+        bool mShutdownFlag{false};
+
+        static std::atomic<uint64_t> connection_id_counter_;
+    };
+
+    std::string mServiceAddr;
+    std::unique_ptr<::grpc::Server> mServer{nullptr};
+    std::unique_ptr<IVehicleHardware> mHardware;
+
+    std::shared_mutex mConnectionMutex;
+    std::vector<std::shared_ptr<ConnectionDescriptor>> mValueStreamingConnections;
+
+    static constexpr auto kHardwareOpTimeout = std::chrono::seconds(1);
+};
+
+}  // 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 0000000..7a96f23
--- /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 0000000..22b11d8
--- /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/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";
+
+service VehicleServer {
+    rpc GetAllPropertyConfig(google.protobuf.Empty) returns (stream VehiclePropConfig) {}
+
+    rpc SetValues(VehiclePropValueRequests) returns (SetValueResults) {}
+
+    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
new file mode 100644
index 0000000..e53826f
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/grpc/test/Android.bp
@@ -0,0 +1,74 @@
+// 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"],
+}
+
+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/GRPCVehicleHardwareUnitTest.cpp b/automotive/vehicle/aidl/impl/grpc/test/GRPCVehicleHardwareUnitTest.cpp
new file mode 100644
index 0000000..f578021
--- /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
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 0000000..49e6fc9
--- /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 <gmock/gmock.h>
+#include <grpc++/grpc++.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+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<const PropertyChangeCallback> callback) override {
+        mOnProp = std::move(callback);
+    }
+
+    void onPropertyEvent(
+            std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue> values) {
+        if (mOnProp) {
+            (*mOnProp)(std::move(values));
+        }
+    }
+
+    // Functions that we do not care.
+    std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
+    getAllPropertyConfigs() const override {
+        return {};
+    }
+
+    aidl::android::hardware::automotive::vehicle::StatusCode setValues(
+            std::shared_ptr<const SetValuesCallback> callback,
+            const std::vector<aidl::android::hardware::automotive::vehicle::SetValueRequest>&
+                    requests) override {
+        return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
+    }
+
+    aidl::android::hardware::automotive::vehicle::StatusCode getValues(
+            std::shared_ptr<const GetValuesCallback> callback,
+            const std::vector<aidl::android::hardware::automotive::vehicle::GetValueRequest>&
+                    requests) const override {
+        return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
+    }
+
+    DumpResult dump(const std::vector<std::string>& options) override { return {}; }
+
+    aidl::android::hardware::automotive::vehicle::StatusCode checkHealth() override {
+        return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
+    }
+
+    void registerOnPropertySetErrorEvent(
+            std::unique_ptr<const PropertySetErrorCallback> callback) override {}
+
+  private:
+    std::unique_ptr<const PropertyChangeCallback> mOnProp;
+};
+
+TEST(GRPCVehicleProxyServerUnitTest, ClientConnectDisconnect) {
+    auto testHardware = std::make_unique<VehicleHardwareForTest>();
+    // HACK: manipulate the underlying hardware via raw pointer for testing.
+    auto* testHardwareRaw = testHardware.get();
+    auto vehicleServer =
+            std::make_unique<GrpcVehicleProxyServer>(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<bool>(false);
+    auto vehicleHardware1 = std::make_unique<GRPCVehicleHardware>(kFakeServerAddr);
+    vehicleHardware1->registerOnPropertyChangeEvent(
+            std::make_unique<const IVehicleHardware::PropertyChangeCallback>(
+                    [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<bool>(false);
+    auto vehicleHardware2 = std::make_unique<GRPCVehicleHardware>(kFakeServerAddr);
+    vehicleHardware2->registerOnPropertyChangeEvent(
+            std::make_unique<const IVehicleHardware::PropertyChangeCallback>(
+                    [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/grpc/utils/proto_message_converter/Android.bp b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/Android.bp
index 7670c25..2b4059c 100644
--- a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/Android.bp
+++ b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/Android.bp
@@ -47,13 +47,19 @@
     ],
     vendor: true,
     defaults: ["VehicleHalDefaults"],
-    shared_libs: ["libprotobuf-cpp-full"],
+    shared_libs: [
+        "libprotobuf-cpp-full",
+        "libjsoncpp",
+    ],
     static_libs: [
+        "VehicleHalJsonConfigLoaderEnableTestProperties",
         "VehicleHalProtoMessageConverter",
         "VehicleHalProtos",
         "VehicleHalUtils",
         "libgtest",
     ],
-    header_libs: ["VehicleHalDefaultConfig"],
+    data: [
+        ":VehicleHalDefaultProperties_JSON",
+    ],
     test_suites: ["device-tests"],
 }
diff --git a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp
index c742db5..308be46 100644
--- a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp
+++ b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp
@@ -16,9 +16,11 @@
 
 #include <vector>
 
-#include <DefaultConfig.h>
+#include <JsonConfigLoader.h>
 #include <ProtoMessageConverter.h>
 #include <VehicleHalTypes.h>
+
+#include <android-base/file.h>
 #include <android-base/format.h>
 #include <android/hardware/automotive/vehicle/VehiclePropConfig.pb.h>
 #include <android/hardware/automotive/vehicle/VehiclePropValue.pb.h>
@@ -35,23 +37,39 @@
 namespace proto = ::android::hardware::automotive::vehicle::proto;
 namespace aidl_vehicle = ::aidl::android::hardware::automotive::vehicle;
 
+constexpr char DEFAULT_PROPERTIES_CONFIG[] = "DefaultProperties.json";
+
+inline std::string getConfigPath(const std::string& name) {
+    return android::base::GetExecutableDirectory() + "/" + name;
+}
+
 std::vector<aidl_vehicle::VehiclePropConfig> prepareTestConfigs() {
+    JsonConfigLoader loader;
+    auto result = loader.loadPropConfig(getConfigPath(DEFAULT_PROPERTIES_CONFIG));
+    if (!result.ok()) {
+        return {};
+    }
     std::vector<aidl_vehicle::VehiclePropConfig> configs;
-    for (auto& property : defaultconfig::getDefaultConfigs()) {
-        configs.push_back(property.config);
+    for (auto& [_, configDeclaration] : result.value()) {
+        configs.push_back(configDeclaration.config);
     }
     return configs;
 }
 
 std::vector<aidl_vehicle::VehiclePropValue> prepareTestValues() {
+    JsonConfigLoader loader;
+    auto result = loader.loadPropConfig(getConfigPath(DEFAULT_PROPERTIES_CONFIG));
+    if (!result.ok()) {
+        return {};
+    }
     std::vector<aidl_vehicle::VehiclePropValue> values;
     int64_t timestamp = 1;
-    for (auto& property : defaultconfig::getDefaultConfigs()) {
+    for (auto& [_, configDeclaration] : result.value()) {
         values.push_back({
                 .timestamp = timestamp,
                 .areaId = 123,
-                .prop = property.config.prop,
-                .value = property.initialValue,
+                .prop = configDeclaration.config.prop,
+                .value = configDeclaration.initialValue,
                 .status = aidl_vehicle::VehiclePropertyStatus::ERROR,
         });
     }