Merge changes I0e0be46f,I2d34138a,I48729915

* changes:
  Create ConnectedClient to manage VHAL clients.
  Add MockVehicleCallback for testing.
  Add ParcelableUtils.
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
index 2b36c72..013d177 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
@@ -25,6 +25,7 @@
 #include <aidl/android/hardware/automotive/vehicle/FuelType.h>
 #include <aidl/android/hardware/automotive/vehicle/GetValueRequest.h>
 #include <aidl/android/hardware/automotive/vehicle/GetValueResult.h>
+#include <aidl/android/hardware/automotive/vehicle/GetValueResults.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2CommonIgnitionMonitors.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2FuelSystemStatus.h>
 #include <aidl/android/hardware/automotive/vehicle/Obd2FuelType.h>
@@ -34,6 +35,7 @@
 #include <aidl/android/hardware/automotive/vehicle/PortLocationType.h>
 #include <aidl/android/hardware/automotive/vehicle/SetValueRequest.h>
 #include <aidl/android/hardware/automotive/vehicle/SetValueResult.h>
+#include <aidl/android/hardware/automotive/vehicle/SetValueResults.h>
 #include <aidl/android/hardware/automotive/vehicle/StatusCode.h>
 #include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReport.h>
 #include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReq.h>
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
index dfc7cbf..63eb747 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
@@ -19,6 +19,7 @@
 
 #include <VehicleHalTypes.h>
 
+#include <android-base/format.h>
 #include <android-base/result.h>
 #include <utils/Log.h>
 
@@ -208,6 +209,37 @@
     return result.error().message();
 }
 
+template <class T>
+::ndk::ScopedAStatus toScopedAStatus(
+        const ::android::base::Result<T>& result,
+        ::aidl::android::hardware::automotive::vehicle::StatusCode status,
+        std::string additionalErrorMsg) {
+    if (result.ok()) {
+        return ::ndk::ScopedAStatus::ok();
+    }
+    return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+            toInt(status),
+            fmt::format("{}, error: {}", additionalErrorMsg, getErrorMsg(result)).c_str());
+}
+
+template <class T>
+::ndk::ScopedAStatus toScopedAStatus(
+        const ::android::base::Result<T>& result,
+        ::aidl::android::hardware::automotive::vehicle::StatusCode status) {
+    return toScopedAStatus(result, status, "");
+}
+
+template <class T>
+::ndk::ScopedAStatus toScopedAStatus(const ::android::base::Result<T>& result) {
+    return toScopedAStatus(result, getErrorCode(result));
+}
+
+template <class T>
+::ndk::ScopedAStatus toScopedAStatus(const ::android::base::Result<T>& result,
+                                     std::string additionalErrorMsg) {
+    return toScopedAStatus(result, getErrorCode(result), additionalErrorMsg);
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/Android.bp b/automotive/vehicle/aidl/impl/vhal/Android.bp
index ef1d0f1..79d3ebd 100644
--- a/automotive/vehicle/aidl/impl/vhal/Android.bp
+++ b/automotive/vehicle/aidl/impl/vhal/Android.bp
@@ -53,7 +53,10 @@
     ],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
-    srcs: ["src/DefaultVehicleHal.cpp"],
+    srcs: [
+        "src/ConnectedClient.cpp",
+        "src/DefaultVehicleHal.cpp",
+    ],
     static_libs: [
         "VehicleHalUtils",
         "android-automotive-large-parcelable-vendor-lib",
diff --git a/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
new file mode 100644
index 0000000..43a9603
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_ConnectedClient_H_
+#define android_hardware_automotive_vehicle_aidl_impl_vhal_include_ConnectedClient_H_
+
+#include <VehicleHalTypes.h>
+
+#include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h>
+#include <android-base/result.h>
+
+#include <memory>
+#include <unordered_set>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+// A class to represent a binder client with a callback interface. Each callback function, e.g.
+// GetValues or SetValues for a specific binder client is a separate {@code ConnectedClient}.
+// For one {@code ConnectedClient}, we use one pending request pool to manage all pending requests,
+// so the request IDs must be unique for one client. We also manage a set of callback functions
+// for one client, e.g. timeoutCallback which could be passed to hardware.
+// This class is thread-safe.
+class ConnectedClient {
+  public:
+    ConnectedClient(
+            std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
+                    callback);
+
+    virtual ~ConnectedClient() = default;
+
+  protected:
+    const std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
+            mCallback;
+};
+
+// A class to represent a client that calls {@code IVehicle.setValues} or {@code
+// IVehicle.getValues}.
+template <class ResultType, class ResultsType>
+class GetSetValuesClient final : public ConnectedClient {
+  public:
+    GetSetValuesClient(
+            std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
+                    callback);
+
+    // Sends the results to this client.
+    void sendResults(const std::vector<ResultType>& results);
+
+    // Sends each result separately to this client. Each result would be sent through one callback
+    // invocation.
+    void sendResultsSeparately(const std::vector<ResultType>& results);
+
+    // Gets the callback to be called when the request for this client has finished.
+    std::shared_ptr<const std::function<void(std::vector<ResultType>)>> getResultCallback();
+
+  private:
+    // The following members are only initialized during construction.
+    std::shared_ptr<const std::function<void(std::vector<ResultType>)>> mResultCallback;
+};
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_aidl_impl_vhal_include_ConnectedClient_H_
diff --git a/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h b/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h
new file mode 100644
index 0000000..dcb15b9
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_ParcelableUtils_H_
+#define android_hardware_automotive_vehicle_aidl_impl_vhal_include_ParcelableUtils_H_
+
+#include <LargeParcelableBase.h>
+#include <VehicleHalTypes.h>
+#include <VehicleUtils.h>
+
+#include <memory>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+template <class T1, class T2>
+::ndk::ScopedAStatus vectorToStableLargeParcelable(std::vector<T1>&& values, T2* output) {
+    auto result = ::android::automotive::car_binder_lib::LargeParcelableBase::
+            parcelableVectorToStableLargeParcelable(values);
+    if (!result.ok()) {
+        return toScopedAStatus(
+                result, ::aidl::android::hardware::automotive::vehicle::StatusCode::INTERNAL_ERROR);
+    }
+    auto& fd = result.value();
+    if (fd == nullptr) {
+        // If we no longer needs values, move it inside the payloads to avoid copying.
+        output->payloads = std::move(values);
+    } else {
+        // Move the returned ScopedFileDescriptor pointer to ScopedFileDescriptor value in
+        // 'sharedMemoryFd' field.
+        output->sharedMemoryFd = std::move(*fd);
+    }
+    return ::ndk::ScopedAStatus::ok();
+}
+
+template <class T1, class T2>
+::ndk::ScopedAStatus vectorToStableLargeParcelable(const std::vector<T1>& values, T2* output) {
+    // Because 'values' is passed in as const reference, we have to do a copy here.
+    std::vector<T1> valuesCopy = values;
+
+    return vectorToStableLargeParcelable(std::move(valuesCopy), output);
+}
+
+template <class T1, class T2>
+::android::base::expected<std::vector<T1>, ::ndk::ScopedAStatus> stableLargeParcelableToVector(
+        const T2& largeParcelable) {
+    ::android::base::Result<std::optional<std::vector<T1>>> result =
+            ::android::automotive::car_binder_lib::LargeParcelableBase::
+                    stableLargeParcelableToParcelableVector<T1>(largeParcelable.sharedMemoryFd);
+
+    if (!result.ok()) {
+        return ::android::base::unexpected(toScopedAStatus(
+                result, ::aidl::android::hardware::automotive::vehicle::StatusCode::INVALID_ARG,
+                "failed to parse large parcelable"));
+    }
+
+    if (!result.value().has_value()) {
+        return ::android::base::unexpected(
+                ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                        toInt(::aidl::android::hardware::automotive::vehicle::StatusCode::
+                                      INVALID_ARG),
+                        "empty request"));
+    }
+
+    return std::move(result.value().value());
+}
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_aidl_impl_vhal_include_ParcelableUtils_H_
diff --git a/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp b/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp
new file mode 100644
index 0000000..656dfaf
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 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 "ConnectedClient.h"
+#include "ParcelableUtils.h"
+
+#include <VehicleHalTypes.h>
+
+#include <utils/Log.h>
+
+#include <inttypes.h>
+#include <unordered_set>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+namespace {
+
+using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
+using ::android::base::Result;
+using ::ndk::ScopedAStatus;
+
+// A function to call the specific callback based on results type.
+template <class T>
+ScopedAStatus callCallback(std::shared_ptr<IVehicleCallback> callback, const T& results);
+
+template <>
+ScopedAStatus callCallback<GetValueResults>(std::shared_ptr<IVehicleCallback> callback,
+                                            const GetValueResults& results) {
+    return callback->onGetValues(results);
+}
+
+template <>
+ScopedAStatus callCallback<SetValueResults>(std::shared_ptr<IVehicleCallback> callback,
+                                            const SetValueResults& results) {
+    return callback->onSetValues(results);
+}
+
+// Send a single GetValue/SetValue result through the callback.
+template <class ResultType, class ResultsType>
+void sendGetOrSetValueResult(std::shared_ptr<IVehicleCallback> callback, const ResultType& result) {
+    ResultsType parcelableResults;
+    parcelableResults.payloads.resize(1);
+    parcelableResults.payloads[0] = result;
+    if (ScopedAStatus callbackStatus = callCallback(callback, parcelableResults);
+        !callbackStatus.isOk()) {
+        ALOGE("failed to call callback, error: %s, code: %d", callbackStatus.getMessage(),
+              callbackStatus.getServiceSpecificError());
+    }
+}
+
+// Send all the GetValue/SetValue results through callback, one result in each callback invocation.
+template <class ResultType, class ResultsType>
+void sendGetOrSetValueResultsSeparately(std::shared_ptr<IVehicleCallback> callback,
+                                        const std::vector<ResultType>& results) {
+    for (const auto& result : results) {
+        sendGetOrSetValueResult<ResultType, ResultsType>(callback, result);
+    }
+}
+
+// Send all the GetValue/SetValue results through callback in a single callback invocation.
+template <class ResultType, class ResultsType>
+void sendGetOrSetValueResults(std::shared_ptr<IVehicleCallback> callback,
+                              const std::vector<ResultType>& results) {
+    ResultsType parcelableResults;
+    ScopedAStatus status = vectorToStableLargeParcelable(results, &parcelableResults);
+    if (status.isOk()) {
+        if (ScopedAStatus callbackStatus = callCallback(callback, parcelableResults);
+            !callbackStatus.isOk()) {
+            ALOGE("failed to call callback, error: %s, code: %d", status.getMessage(),
+                  status.getServiceSpecificError());
+        }
+        return;
+    }
+    int statusCode = status.getServiceSpecificError();
+    ALOGE("failed to marshal result into large parcelable, error: "
+          "%s, code: %d",
+          status.getMessage(), statusCode);
+    sendGetOrSetValueResultsSeparately<ResultType, ResultsType>(callback, results);
+}
+
+// Specify the functions for GetValues and SetValues types.
+template void sendGetOrSetValueResult<GetValueResult, GetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const GetValueResult& result);
+template void sendGetOrSetValueResult<SetValueResult, SetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const SetValueResult& result);
+
+template void sendGetOrSetValueResults<GetValueResult, GetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const std::vector<GetValueResult>& results);
+template void sendGetOrSetValueResults<SetValueResult, SetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const std::vector<SetValueResult>& results);
+
+template void sendGetOrSetValueResultsSeparately<GetValueResult, GetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const std::vector<GetValueResult>& results);
+template void sendGetOrSetValueResultsSeparately<SetValueResult, SetValueResults>(
+        std::shared_ptr<IVehicleCallback> callback, const std::vector<SetValueResult>& results);
+
+}  // namespace
+
+ConnectedClient::ConnectedClient(std::shared_ptr<IVehicleCallback> callback)
+    : mCallback(callback) {}
+
+template <class ResultType, class ResultsType>
+GetSetValuesClient<ResultType, ResultsType>::GetSetValuesClient(
+        std::shared_ptr<IVehicleCallback> callback)
+    : ConnectedClient(callback) {
+    mResultCallback = std::make_shared<const std::function<void(std::vector<ResultType>)>>(
+            [callback](std::vector<ResultType> results) {
+                return sendGetOrSetValueResults<ResultType, ResultsType>(callback, results);
+            });
+}
+
+template <class ResultType, class ResultsType>
+std::shared_ptr<const std::function<void(std::vector<ResultType>)>>
+GetSetValuesClient<ResultType, ResultsType>::getResultCallback() {
+    return mResultCallback;
+}
+
+template <class ResultType, class ResultsType>
+void GetSetValuesClient<ResultType, ResultsType>::sendResults(
+        const std::vector<ResultType>& results) {
+    return sendGetOrSetValueResults<ResultType, ResultsType>(mCallback, results);
+}
+
+template <class ResultType, class ResultsType>
+void GetSetValuesClient<ResultType, ResultsType>::sendResultsSeparately(
+        const std::vector<ResultType>& results) {
+    return sendGetOrSetValueResultsSeparately<ResultType, ResultsType>(mCallback, results);
+}
+
+template class GetSetValuesClient<GetValueResult, GetValueResults>;
+template class GetSetValuesClient<SetValueResult, SetValueResults>;
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp
new file mode 100644
index 0000000..ddd0c65
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2021 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 "ConnectedClient.h"
+#include "MockVehicleCallback.h"
+
+#include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+
+class ConnectedClientTest : public ::testing::Test {
+  public:
+    void SetUp() override {
+        mCallback = ndk::SharedRefBase::make<MockVehicleCallback>();
+        mCallbackClient = IVehicleCallback::fromBinder(mCallback->asBinder());
+    }
+
+    std::shared_ptr<IVehicleCallback> getCallbackClient() { return mCallbackClient; }
+
+    MockVehicleCallback* getCallback() { return mCallback.get(); }
+
+  protected:
+    using GetValuesClient = GetSetValuesClient<GetValueResult, GetValueResults>;
+    using SetValuesClient = GetSetValuesClient<SetValueResult, SetValueResults>;
+
+  private:
+    std::shared_ptr<MockVehicleCallback> mCallback;
+    std::shared_ptr<IVehicleCallback> mCallbackClient;
+};
+
+TEST_F(ConnectedClientTest, testSendGetValueResults) {
+    std::vector<GetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 0,
+                                                           },
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 1,
+                                                           },
+                                           }};
+
+    GetValuesClient client(getCallbackClient());
+
+    client.sendResults(results);
+
+    auto maybeGetValueResults = getCallback()->nextGetValueResults();
+    ASSERT_TRUE(maybeGetValueResults.has_value());
+    ASSERT_EQ(maybeGetValueResults.value().payloads, results);
+}
+
+TEST_F(ConnectedClientTest, testSendGetValueResultsSeparately) {
+    std::vector<GetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 0,
+                                                           },
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 1,
+                                                           },
+                                           }};
+
+    GetValuesClient client(getCallbackClient());
+
+    client.sendResultsSeparately(results);
+
+    for (auto& result : results) {
+        auto maybeGetValueResults = getCallback()->nextGetValueResults();
+        EXPECT_TRUE(maybeGetValueResults.has_value());
+        if (!maybeGetValueResults.has_value()) {
+            continue;
+        }
+        EXPECT_EQ(maybeGetValueResults.value().payloads, std::vector<GetValueResult>({result}));
+    }
+}
+
+TEST_F(ConnectedClientTest, testGetValuesGnResultCallback) {
+    std::vector<GetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 0,
+                                                           },
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                                   .prop =
+                                                           VehiclePropValue{
+                                                                   .prop = 1,
+                                                           },
+                                           }};
+
+    GetValuesClient client(getCallbackClient());
+
+    (*(client.getResultCallback()))(results);
+
+    auto maybeGetValueResults = getCallback()->nextGetValueResults();
+    ASSERT_TRUE(maybeGetValueResults.has_value());
+    ASSERT_EQ(maybeGetValueResults.value().payloads, results);
+}
+
+TEST_F(ConnectedClientTest, testSendSetValueResults) {
+    std::vector<SetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                           }};
+
+    SetValuesClient client(getCallbackClient());
+
+    client.sendResults(results);
+
+    auto maybeSetValueResults = getCallback()->nextSetValueResults();
+    ASSERT_TRUE(maybeSetValueResults.has_value());
+    ASSERT_EQ(maybeSetValueResults.value().payloads, results);
+}
+
+TEST_F(ConnectedClientTest, testSendSetValueResultsSeparately) {
+    std::vector<SetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                           }};
+
+    SetValuesClient client(getCallbackClient());
+
+    client.sendResultsSeparately(results);
+
+    for (auto& result : results) {
+        auto maybeSetValueResults = getCallback()->nextSetValueResults();
+        EXPECT_TRUE(maybeSetValueResults.has_value());
+        if (!maybeSetValueResults.has_value()) {
+            continue;
+        }
+        EXPECT_EQ(maybeSetValueResults.value().payloads, std::vector<SetValueResult>({result}));
+    }
+}
+
+TEST_F(ConnectedClientTest, testSetValuesGetResultCallback) {
+    std::vector<SetValueResult> results = {{
+                                                   .requestId = 0,
+                                                   .status = StatusCode::OK,
+                                           },
+                                           {
+                                                   .requestId = 1,
+                                                   .status = StatusCode::OK,
+                                           }};
+
+    SetValuesClient client(getCallbackClient());
+
+    (*(client.getResultCallback()))(results);
+
+    auto maybeSetValueResults = getCallback()->nextSetValueResults();
+    ASSERT_TRUE(maybeSetValueResults.has_value());
+    ASSERT_EQ(maybeSetValueResults.value().payloads, results);
+}
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp
new file mode 100644
index 0000000..ca366cd
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 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 "MockVehicleCallback.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+namespace {
+
+using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
+using ::ndk::ScopedAStatus;
+using ::ndk::ScopedFileDescriptor;
+
+template <class T>
+std::optional<T> pop(std::list<T>& items) {
+    if (items.size() > 0) {
+        auto item = std::move(items.front());
+        items.pop_front();
+        return item;
+    }
+    return std::nullopt;
+}
+
+template <class T>
+static ScopedAStatus storeResults(const T& results, std::list<T>* storedResults) {
+    T resultsCopy{
+            .payloads = results.payloads,
+    };
+    int fd = results.sharedMemoryFd.get();
+    if (fd != -1) {
+        resultsCopy.sharedMemoryFd = ScopedFileDescriptor(dup(fd));
+    }
+    storedResults->push_back(std::move(resultsCopy));
+    return ScopedAStatus::ok();
+}
+
+}  // namespace
+
+ScopedAStatus MockVehicleCallback::onGetValues(const GetValueResults& results) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return storeResults(results, &mGetValueResults);
+}
+
+ScopedAStatus MockVehicleCallback::onSetValues(const SetValueResults& results) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return storeResults(results, &mSetValueResults);
+}
+
+ScopedAStatus MockVehicleCallback::onPropertyEvent(const VehiclePropValues&, int32_t) {
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus MockVehicleCallback::onPropertySetError(const VehiclePropErrors&) {
+    return ScopedAStatus::ok();
+}
+
+std::optional<GetValueResults> MockVehicleCallback::nextGetValueResults() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return pop(mGetValueResults);
+}
+
+std::optional<SetValueResults> MockVehicleCallback::nextSetValueResults() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return pop(mSetValueResults);
+}
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h
new file mode 100644
index 0000000..916575a
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleCallback_H_
+#define android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleCallback_H_
+
+#include <VehicleHalTypes.h>
+
+#include <aidl/android/hardware/automotive/vehicle/BnVehicleCallback.h>
+#include <android-base/thread_annotations.h>
+
+#include <list>
+#include <mutex>
+#include <optional>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+// MockVehicleCallback is a mock VehicleCallback implementation that simply stores the results.
+class MockVehicleCallback final
+    : public ::aidl::android::hardware::automotive::vehicle::BnVehicleCallback {
+  public:
+    ::ndk::ScopedAStatus onGetValues(
+            const ::aidl::android::hardware::automotive::vehicle::GetValueResults& results)
+            override;
+    ::ndk::ScopedAStatus onSetValues(
+            const ::aidl::android::hardware::automotive::vehicle::SetValueResults& results)
+            override;
+    ::ndk::ScopedAStatus onPropertyEvent(
+            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValues&,
+            int32_t) override;
+    ::ndk::ScopedAStatus onPropertySetError(
+            const ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors&) override;
+
+    // Test functions
+    std::optional<::aidl::android::hardware::automotive::vehicle::GetValueResults>
+    nextGetValueResults();
+    std::optional<::aidl::android::hardware::automotive::vehicle::SetValueResults>
+    nextSetValueResults();
+
+  private:
+    std::mutex mLock;
+    std::list<::aidl::android::hardware::automotive::vehicle::GetValueResults> mGetValueResults
+            GUARDED_BY(mLock);
+    std::list<::aidl::android::hardware::automotive::vehicle::SetValueResults> mSetValueResults
+            GUARDED_BY(mLock);
+};
+
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleCallback_H_