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_