Implment getValues in Default VHAL.

Test: atest DefaultVehicleHalTest
Bug: 200737967

Change-Id: I46a0ebdf1c8e211cacc7568e7a8e6f5332083c14
diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
index 43bdca2..da83e94 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
@@ -17,10 +17,15 @@
 #ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_DefaultVehicleHal_H_
 #define android_hardware_automotive_vehicle_aidl_impl_vhal_include_DefaultVehicleHal_H_
 
+#include "ConnectedClient.h"
+#include "ParcelableUtils.h"
+
 #include <IVehicleHardware.h>
 #include <LargeParcelableBase.h>
 #include <VehicleUtils.h>
 #include <aidl/android/hardware/automotive/vehicle/BnVehicle.h>
+#include <android-base/expected.h>
+#include <android-base/thread_annotations.h>
 #include <android/binder_auto_utils.h>
 
 #include <memory>
@@ -40,12 +45,20 @@
 template <class T>
 ::ndk::ScopedAStatus toScopedAStatus(
         const ::android::base::Result<T>& result,
-        ::aidl::android::hardware::automotive::vehicle::StatusCode status) {
+        ::aidl::android::hardware::automotive::vehicle::StatusCode status,
+        const std::string& additionalErrorMsg) {
     if (result.ok()) {
         return ::ndk::ScopedAStatus::ok();
     }
-    return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(toInt(status),
-                                                                     getErrorMsg(result).c_str());
+    return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+            toInt(status), (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>
@@ -53,43 +66,30 @@
     return toScopedAStatus(result, getErrorCode(result));
 }
 
-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) {
-        output->payloads = values;
-    } else {
-        // Move the returned ScopedFileDescriptor pointer to ScopedFileDescriptor value in
-        // 'sharedMemoryFd' field.
-        output->sharedMemoryFd.set(fd->get());
-        *(fd->getR()) = INVALID_MEMORY_FD;
-    }
-    return ::ndk::ScopedAStatus::ok();
+template <class T>
+::ndk::ScopedAStatus toScopedAStatus(const ::android::base::Result<T>& result,
+                                     const std::string& additionalErrorMsg) {
+    return toScopedAStatus(result, getErrorCode(result), additionalErrorMsg);
 }
 
 }  // namespace defaultvehiclehal_impl
 
 class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::vehicle::BnVehicle {
   public:
+    using CallbackType =
+            std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>;
+
     explicit DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware);
 
     ::ndk::ScopedAStatus getAllPropConfigs(
             ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs* returnConfigs)
             override;
     ::ndk::ScopedAStatus getValues(
-            const std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>&
-                    callback,
+            const CallbackType& callback,
             const ::aidl::android::hardware::automotive::vehicle::GetValueRequests& requests)
             override;
     ::ndk::ScopedAStatus setValues(
-            const std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>&
-                    callback,
+            const CallbackType& callback,
             const ::aidl::android::hardware::automotive::vehicle::SetValueRequests& requests)
             override;
     ::ndk::ScopedAStatus getPropConfigs(
@@ -97,27 +97,38 @@
             ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs* returnConfigs)
             override;
     ::ndk::ScopedAStatus subscribe(
-            const std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>&
-                    callback,
+            const CallbackType& callback,
             const std::vector<::aidl::android::hardware::automotive::vehicle::SubscribeOptions>&
                     options,
             int32_t maxSharedMemoryFileCount) override;
-    ::ndk::ScopedAStatus unsubscribe(
-            const std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>&
-                    callback,
-            const std::vector<int32_t>& propIds) override;
-    ::ndk::ScopedAStatus returnSharedMemory(
-            const std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>&
-                    callback,
-            int64_t sharedMemoryId) override;
+    ::ndk::ScopedAStatus unsubscribe(const CallbackType& callback,
+                                     const std::vector<int32_t>& propIds) override;
+    ::ndk::ScopedAStatus returnSharedMemory(const CallbackType& callback,
+                                            int64_t sharedMemoryId) override;
 
     IVehicleHardware* getHardware();
 
   private:
+    // friend class for unit testing.
+    friend class DefaultVehicleHalTest;
+
+    using GetValuesClient =
+            GetSetValuesClient<::aidl::android::hardware::automotive::vehicle::GetValueResult,
+                               ::aidl::android::hardware::automotive::vehicle::GetValueResults>;
+
     const std::unique_ptr<IVehicleHardware> mVehicleHardware;
     std::unordered_map<int32_t, ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
             mConfigsByPropId;
     std::unique_ptr<::ndk::ScopedFileDescriptor> mConfigFile;
+
+    std::mutex mLock;
+    std::unordered_map<CallbackType, std::shared_ptr<GetValuesClient>> mGetValuesClients
+            GUARDED_BY(mLock);
+
+    template <class T>
+    std::shared_ptr<T> getOrCreateClient(
+            std::unordered_map<CallbackType, std::shared_ptr<T>>* clients,
+            const CallbackType& callback) REQUIRES(mLock);
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index fd9e331..41753a6 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -21,6 +21,7 @@
 #include <LargeParcelableBase.h>
 #include <VehicleHalTypes.h>
 #include <VehicleUtils.h>
+
 #include <android-base/result.h>
 #include <utils/Log.h>
 
@@ -29,14 +30,19 @@
 namespace automotive {
 namespace vehicle {
 
+using ::aidl::android::hardware::automotive::vehicle::GetValueRequest;
 using ::aidl::android::hardware::automotive::vehicle::GetValueRequests;
+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::SetValueRequests;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 using ::android::automotive::car_binder_lib::LargeParcelableBase;
+using ::android::base::expected;
 using ::android::base::Result;
 using ::ndk::ScopedAStatus;
 
@@ -70,14 +76,58 @@
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus DefaultVehicleHal::getValues(const std::shared_ptr<IVehicleCallback>&,
-                                           const GetValueRequests&) {
-    // TODO(b/200737967): implement this.
+template <class T>
+std::shared_ptr<T> DefaultVehicleHal::getOrCreateClient(
+        std::unordered_map<CallbackType, std::shared_ptr<T>>* clients,
+        const CallbackType& callback) {
+    if (clients->find(callback) == clients->end()) {
+        // TODO(b/204943359): Remove client from clients when linkToDeath is implemented.
+        (*clients)[callback] = std::make_shared<T>(callback);
+    }
+    return (*clients)[callback];
+}
+
+template std::shared_ptr<DefaultVehicleHal::GetValuesClient>
+DefaultVehicleHal::getOrCreateClient<DefaultVehicleHal::GetValuesClient>(
+        std::unordered_map<CallbackType, std::shared_ptr<GetValuesClient>>* clients,
+        const CallbackType& callback);
+
+ScopedAStatus DefaultVehicleHal::getValues(const CallbackType& callback,
+                                           const GetValueRequests& requests) {
+    // TODO(b/203713317): check for duplicate properties and duplicate request IDs.
+
+    const std::vector<GetValueRequest>* getValueRequests;
+    // Define deserializedResults here because we need it to have the same lifetime as
+    // getValueRequests.
+    expected<std::vector<GetValueRequest>, ScopedAStatus> deserializedResults;
+    if (!requests.payloads.empty()) {
+        getValueRequests = &requests.payloads;
+    } else {
+        deserializedResults = stableLargeParcelableToVector<GetValueRequest>(requests);
+        if (!deserializedResults.ok()) {
+            ALOGE("failed to parse getValues requests");
+            return std::move(deserializedResults.error());
+        }
+        getValueRequests = &deserializedResults.value();
+    }
+
+    std::shared_ptr<GetValuesClient> client;
+    {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        client = getOrCreateClient(&mGetValuesClients, callback);
+    }
+
+    if (StatusCode status =
+                mVehicleHardware->getValues(client->getResultCallback(), *getValueRequests);
+        status != StatusCode::OK) {
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                toInt(status), "failed to get value from VehicleHardware");
+    }
+
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus DefaultVehicleHal::setValues(const std::shared_ptr<IVehicleCallback>&,
-                                           const SetValueRequests&) {
+ScopedAStatus DefaultVehicleHal::setValues(const CallbackType&, const SetValueRequests&) {
     // TODO(b/200737967): implement this.
     return ScopedAStatus::ok();
 }
@@ -90,23 +140,21 @@
             configs.push_back(mConfigsByPropId[prop]);
         }
     }
-    return defaultvehiclehal_impl::vectorToStableLargeParcelable(configs, output);
+    return vectorToStableLargeParcelable(std::move(configs), output);
 }
 
-ScopedAStatus DefaultVehicleHal::subscribe(const std::shared_ptr<IVehicleCallback>&,
+ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType&,
                                            const std::vector<SubscribeOptions>&, int32_t) {
     // TODO(b/200737967): implement this.
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus DefaultVehicleHal::unsubscribe(const std::shared_ptr<IVehicleCallback>&,
-                                             const std::vector<int32_t>&) {
+ScopedAStatus DefaultVehicleHal::unsubscribe(const CallbackType&, const std::vector<int32_t>&) {
     // TODO(b/200737967): implement this.
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus DefaultVehicleHal::returnSharedMemory(const std::shared_ptr<IVehicleCallback>&,
-                                                    int64_t) {
+ScopedAStatus DefaultVehicleHal::returnSharedMemory(const CallbackType&, int64_t) {
     // TODO(b/200737967): implement this.
     return ScopedAStatus::ok();
 }
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index 2b5ca70..26380b3 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -15,17 +15,23 @@
  */
 
 #include "DefaultVehicleHal.h"
+#include "MockVehicleCallback.h"
 
 #include <IVehicleHardware.h>
 #include <LargeParcelableBase.h>
 #include <aidl/android/hardware/automotive/vehicle/IVehicle.h>
-#include <android-base/thread_annotations.h>
+#include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h>
 
+#include <android-base/thread_annotations.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <utils/Log.h>
 
+#include <list>
 #include <memory>
+#include <mutex>
 #include <optional>
+#include <unordered_map>
 #include <vector>
 
 namespace android {
@@ -36,21 +42,40 @@
 namespace {
 
 using ::aidl::android::hardware::automotive::vehicle::GetValueRequest;
+using ::aidl::android::hardware::automotive::vehicle::GetValueRequests;
 using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
+using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
 using ::aidl::android::hardware::automotive::vehicle::IVehicle;
+using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
 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::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
 
 using ::android::automotive::car_binder_lib::LargeParcelableBase;
 using ::android::base::Result;
 
+using ::ndk::ScopedAStatus;
+using ::ndk::ScopedFileDescriptor;
+
 using ::testing::Eq;
 using ::testing::WhenSortedBy;
 
+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;
+}
+
 class MockVehicleHardware final : public IVehicleHardware {
   public:
     std::vector<VehiclePropConfig> getAllPropertyConfigs() const override {
@@ -58,16 +83,16 @@
         return mPropertyConfigs;
     }
 
-    StatusCode setValues(std::shared_ptr<const SetValuesCallback>,
-                         const std::vector<SetValueRequest>&) override {
-        // TODO(b/200737967): mock this.
-        return StatusCode::OK;
+    StatusCode setValues(std::shared_ptr<const SetValuesCallback> callback,
+                         const std::vector<SetValueRequest>& requests) override {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        return handleRequests(__func__, callback, requests, mSetValueRequests, mSetValueResponses);
     }
 
-    StatusCode getValues(std::shared_ptr<const GetValuesCallback>,
-                         const std::vector<GetValueRequest>&) const override {
-        // TODO(b/200737967): mock this.
-        return StatusCode::OK;
+    StatusCode getValues(std::shared_ptr<const GetValuesCallback> callback,
+                         const std::vector<GetValueRequest>& requests) const override {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        return handleRequests(__func__, callback, requests, mGetValueRequests, mGetValueResponses);
     }
 
     DumpResult dump(const std::vector<std::string>&) override {
@@ -94,11 +119,110 @@
         mPropertyConfigs = configs;
     }
 
+    void addGetValueResponses(const std::vector<GetValueResult>& responses) {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        mGetValueResponses.push_back(responses);
+    }
+
+    std::vector<GetValueRequest> nextGetValueRequests() {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        std::optional<std::vector<GetValueRequest>> request = pop(mGetValueRequests);
+        if (!request.has_value()) {
+            return std::vector<GetValueRequest>();
+        }
+        return std::move(request.value());
+    }
+
+    std::vector<SetValueRequest> nextSetValueRequests() {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        std::optional<std::vector<SetValueRequest>> request = pop(mSetValueRequests);
+        if (!request.has_value()) {
+            return std::vector<SetValueRequest>();
+        }
+        return std::move(request.value());
+    }
+
+    void setStatus(const char* functionName, StatusCode status) {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        mStatusByFunctions[functionName] = status;
+    }
+
   private:
     mutable std::mutex mLock;
     std::vector<VehiclePropConfig> mPropertyConfigs GUARDED_BY(mLock);
+    mutable std::list<std::vector<GetValueRequest>> mGetValueRequests GUARDED_BY(mLock);
+    mutable std::list<std::vector<GetValueResult>> mGetValueResponses GUARDED_BY(mLock);
+    mutable std::list<std::vector<SetValueRequest>> mSetValueRequests GUARDED_BY(mLock);
+    mutable std::list<std::vector<SetValueResult>> mSetValueResponses GUARDED_BY(mLock);
+    std::unordered_map<const char*, StatusCode> mStatusByFunctions GUARDED_BY(mLock);
+
+    template <class ResultType>
+    StatusCode returnResponse(
+            std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
+            std::list<std::vector<ResultType>>& storedResponses) const;
+
+    template <class RequestType, class ResultType>
+    StatusCode handleRequests(
+            const char* functionName,
+            std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
+            const std::vector<RequestType>& requests,
+            std::list<std::vector<RequestType>>& storedRequests,
+            std::list<std::vector<ResultType>>& storedResponses) const REQUIRES(mLock);
 };
 
+template <class ResultType>
+StatusCode MockVehicleHardware::returnResponse(
+        std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
+        std::list<std::vector<ResultType>>& storedResponses) const {
+    if (storedResponses.size() > 0) {
+        (*callback)(std::move(storedResponses.front()));
+        storedResponses.pop_front();
+        return StatusCode::OK;
+    } else {
+        ALOGE("no more response");
+        return StatusCode::INTERNAL_ERROR;
+    }
+}
+
+template StatusCode MockVehicleHardware::returnResponse<GetValueResult>(
+        std::shared_ptr<const std::function<void(std::vector<GetValueResult>)>> callback,
+        std::list<std::vector<GetValueResult>>& storedResponses) const;
+
+template StatusCode MockVehicleHardware::returnResponse<SetValueResult>(
+        std::shared_ptr<const std::function<void(std::vector<SetValueResult>)>> callback,
+        std::list<std::vector<SetValueResult>>& storedResponses) const;
+
+template <class RequestType, class ResultType>
+StatusCode MockVehicleHardware::handleRequests(
+        const char* functionName,
+        std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
+        const std::vector<RequestType>& requests,
+        std::list<std::vector<RequestType>>& storedRequests,
+        std::list<std::vector<ResultType>>& storedResponses) const {
+    storedRequests.push_back(requests);
+    if (auto it = mStatusByFunctions.find(functionName); it != mStatusByFunctions.end()) {
+        if (StatusCode status = it->second; status != StatusCode::OK) {
+            return status;
+        }
+    }
+
+    return returnResponse(callback, storedResponses);
+}
+
+template StatusCode MockVehicleHardware::handleRequests<GetValueRequest, GetValueResult>(
+        const char* functionName,
+        std::shared_ptr<const std::function<void(std::vector<GetValueResult>)>> callback,
+        const std::vector<GetValueRequest>& requests,
+        std::list<std::vector<GetValueRequest>>& storedRequests,
+        std::list<std::vector<GetValueResult>>& storedResponses) const;
+
+template StatusCode MockVehicleHardware::handleRequests<SetValueRequest, SetValueResult>(
+        const char* functionName,
+        std::shared_ptr<const std::function<void(std::vector<SetValueResult>)>> callback,
+        const std::vector<SetValueRequest>& requests,
+        std::list<std::vector<SetValueRequest>>& storedRequests,
+        std::list<std::vector<SetValueResult>>& storedResponses) const;
+
 struct PropConfigCmp {
     bool operator()(const VehiclePropConfig& a, const VehiclePropConfig& b) const {
         return (a.prop < b.prop);
@@ -107,7 +231,77 @@
 
 }  // namespace
 
-TEST(DefaultVehicleHalTest, testGetAllPropConfigsSmall) {
+class DefaultVehicleHalTest : public ::testing::Test {
+  public:
+    void SetUp() override {
+        auto hardware = std::make_unique<MockVehicleHardware>();
+        mHardwarePtr = hardware.get();
+        mVhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+        mVhalClient = IVehicle::fromBinder(mVhal->asBinder());
+        mCallback = ndk::SharedRefBase::make<MockVehicleCallback>();
+        mCallbackClient = IVehicleCallback::fromBinder(mCallback->asBinder());
+    }
+
+    MockVehicleHardware* getHardware() { return mHardwarePtr; }
+
+    std::shared_ptr<IVehicle> getClient() { return mVhal; }
+
+    std::shared_ptr<IVehicleCallback> getCallbackClient() { return mCallbackClient; }
+
+    MockVehicleCallback* getCallback() { return mCallback.get(); }
+
+    static Result<void> getValuesTestCases(size_t size, GetValueRequests& requests,
+                                           std::vector<GetValueResult>& expectedResults,
+                                           std::vector<GetValueRequest>& expectedHardwareRequests) {
+        expectedHardwareRequests.clear();
+        for (size_t i = 0; i < size; i++) {
+            int64_t requestId = static_cast<int64_t>(i);
+            int32_t propId = static_cast<int32_t>(i);
+            expectedHardwareRequests.push_back(GetValueRequest{
+                    .prop =
+                            VehiclePropValue{
+                                    .prop = propId,
+                            },
+                    .requestId = requestId,
+            });
+            expectedResults.push_back(GetValueResult{
+                    .requestId = requestId,
+                    .status = StatusCode::OK,
+                    .prop =
+                            VehiclePropValue{
+                                    .prop = propId,
+                                    .value.int32Values = {1, 2, 3, 4},
+                            },
+            });
+        }
+
+        auto result = LargeParcelableBase::parcelableVectorToStableLargeParcelable(
+                expectedHardwareRequests);
+        if (!result.ok()) {
+            return result.error();
+        }
+        if (result.value() == nullptr) {
+            requests.payloads = expectedHardwareRequests;
+        } else {
+            requests.sharedMemoryFd = std::move(*result.value());
+        }
+        return {};
+    }
+
+    size_t countClients() {
+        std::scoped_lock<std::mutex> lockGuard(mVhal->mLock);
+        return mVhal->mGetValuesClients.size();
+    }
+
+  private:
+    std::shared_ptr<DefaultVehicleHal> mVhal;
+    std::shared_ptr<IVehicle> mVhalClient;
+    MockVehicleHardware* mHardwarePtr;
+    std::shared_ptr<MockVehicleCallback> mCallback;
+    std::shared_ptr<IVehicleCallback> mCallbackClient;
+};
+
+TEST_F(DefaultVehicleHalTest, testGetAllPropConfigsSmall) {
     auto testConfigs = std::vector<VehiclePropConfig>({
             VehiclePropConfig{
                     .prop = 1,
@@ -125,14 +319,14 @@
     VehiclePropConfigs output;
     auto status = client->getAllPropConfigs(&output);
 
-    ASSERT_TRUE(status.isOk());
+    ASSERT_TRUE(status.isOk()) << "getAllPropConfigs failed: " << status.getMessage();
     ASSERT_THAT(output.payloads, WhenSortedBy(propConfigCmp, Eq(testConfigs)));
 }
 
-TEST(DefaultVehicleHalTest, testGetAllPropConfigsLarge) {
+TEST_F(DefaultVehicleHalTest, testGetAllPropConfigsLarge) {
     std::vector<VehiclePropConfig> testConfigs;
-    // 10000 VehiclePropConfig exceeds 4k memory limit, so it would be sent through shared memory.
-    for (size_t i = 0; i < 10000; i++) {
+    // 5000 VehiclePropConfig exceeds 4k memory limit, so it would be sent through shared memory.
+    for (size_t i = 0; i < 5000; i++) {
         testConfigs.push_back(VehiclePropConfig{
                 .prop = static_cast<int32_t>(i),
         });
@@ -146,16 +340,95 @@
     VehiclePropConfigs output;
     auto status = client->getAllPropConfigs(&output);
 
-    ASSERT_TRUE(status.isOk());
+    ASSERT_TRUE(status.isOk()) << "getAllPropConfigs failed: " << status.getMessage();
     ASSERT_TRUE(output.payloads.empty());
     Result<std::optional<std::vector<VehiclePropConfig>>> result =
             LargeParcelableBase::stableLargeParcelableToParcelableVector<VehiclePropConfig>(
                     output.sharedMemoryFd);
-    ASSERT_TRUE(result.ok());
-    ASSERT_TRUE(result.value().has_value());
+    ASSERT_TRUE(result.ok()) << "failed to parse result shared memory file: "
+                             << result.error().message();
+    ASSERT_TRUE(result.value().has_value()) << "empty parsed value";
     ASSERT_EQ(result.value().value(), testConfigs);
 }
 
+TEST_F(DefaultVehicleHalTest, testGetValuesSmall) {
+    GetValueRequests requests;
+    std::vector<GetValueResult> expectedResults;
+    std::vector<GetValueRequest> expectedHardwareRequests;
+
+    ASSERT_TRUE(getValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok());
+
+    getHardware()->addGetValueResponses(expectedResults);
+
+    auto status = getClient()->getValues(getCallbackClient(), requests);
+
+    ASSERT_TRUE(status.isOk()) << "getValues failed: " << status.getMessage();
+
+    EXPECT_EQ(getHardware()->nextGetValueRequests(), expectedHardwareRequests)
+            << "requests to hardware mismatch";
+
+    auto maybeGetValueResults = getCallback()->nextGetValueResults();
+    ASSERT_TRUE(maybeGetValueResults.has_value()) << "no results in callback";
+    EXPECT_EQ(maybeGetValueResults.value().payloads, expectedResults) << "results mismatch";
+    EXPECT_EQ(countClients(), static_cast<size_t>(1));
+}
+
+TEST_F(DefaultVehicleHalTest, testGetValuesLarge) {
+    GetValueRequests requests;
+    std::vector<GetValueResult> expectedResults;
+    std::vector<GetValueRequest> expectedHardwareRequests;
+
+    ASSERT_TRUE(getValuesTestCases(5000, requests, expectedResults, expectedHardwareRequests).ok())
+            << "requests to hardware mismatch";
+    ;
+
+    getHardware()->addGetValueResponses(expectedResults);
+
+    auto status = getClient()->getValues(getCallbackClient(), requests);
+
+    ASSERT_TRUE(status.isOk()) << "getValues failed: " << status.getMessage();
+
+    EXPECT_EQ(getHardware()->nextGetValueRequests(), expectedHardwareRequests);
+
+    auto maybeGetValueResults = getCallback()->nextGetValueResults();
+    ASSERT_TRUE(maybeGetValueResults.has_value()) << "no results in callback";
+    const GetValueResults& getValueResults = maybeGetValueResults.value();
+    ASSERT_TRUE(getValueResults.payloads.empty())
+            << "payload should be empty, shared memory file should be used";
+
+    auto result = LargeParcelableBase::stableLargeParcelableToParcelableVector<GetValueResult>(
+            getValueResults.sharedMemoryFd);
+    ASSERT_TRUE(result.ok()) << "failed to parse shared memory file";
+    ASSERT_TRUE(result.value().has_value()) << "no parsed value";
+    ASSERT_EQ(result.value().value(), expectedResults) << "results mismatch";
+    EXPECT_EQ(countClients(), static_cast<size_t>(1));
+}
+
+TEST_F(DefaultVehicleHalTest, testGetValuesErrorFromHardware) {
+    GetValueRequests requests;
+    std::vector<GetValueResult> expectedResults;
+    std::vector<GetValueRequest> expectedHardwareRequests;
+
+    ASSERT_TRUE(getValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok());
+
+    getHardware()->setStatus("getValues", StatusCode::INTERNAL_ERROR);
+
+    auto status = getClient()->getValues(getCallbackClient(), requests);
+
+    ASSERT_FALSE(status.isOk()) << "expect getValues to fail when hardware returns error";
+    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INTERNAL_ERROR));
+}
+
+TEST_F(DefaultVehicleHalTest, testGetValuesInvalidLargeParcelableInput) {
+    GetValueRequests requests;
+    requests.sharedMemoryFd = ScopedFileDescriptor(0);
+
+    auto status = getClient()->getValues(getCallbackClient(), requests);
+
+    ASSERT_FALSE(status.isOk()) << "expect getValues to fail when input parcelable is not valid";
+    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::INVALID_ARG));
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware