Add a native UserHalHelper library.
This library contains helper functions to convert the User HAL reponse
structs to VehiclePropValues and request VehiclePropValues to HAL
structs.
Test: Tested with unit tests.
Bug: 150409377
Change-Id: I978b39e2f3ee58eb3b154f507c714ca1ba2d1e3b
diff --git a/automotive/vehicle/2.0/utils/Android.bp b/automotive/vehicle/2.0/utils/Android.bp
new file mode 100644
index 0000000..e354634
--- /dev/null
+++ b/automotive/vehicle/2.0/utils/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 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.
+
+// User HAL helper library.
+cc_library_static {
+ name: "android.hardware.automotive.vehicle@2.0-user-hal-helper-lib",
+ defaults: ["vhal_v2_0_defaults"],
+ vendor: true,
+ host_supported: true,
+ srcs: [
+ "UserHalHelper.cpp",
+ ],
+ export_include_dirs: [
+ ".",
+ ],
+}
+
+cc_test {
+ name: "android.hardware.automotive.vehicle@2.0-utils-unit-tests",
+ defaults: ["vhal_v2_0_defaults"],
+ vendor: true,
+ srcs: [
+ "tests/UserHalHelper_test.cpp",
+ ],
+ static_libs: [
+ "android.hardware.automotive.vehicle@2.0-user-hal-helper-lib",
+ "libgmock",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/automotive/vehicle/2.0/utils/UserHalHelper.cpp b/automotive/vehicle/2.0/utils/UserHalHelper.cpp
new file mode 100644
index 0000000..33b3948
--- /dev/null
+++ b/automotive/vehicle/2.0/utils/UserHalHelper.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+#define LOG_TAG "UserHalHelper"
+
+#include "UserHalHelper.h"
+
+#include <log/log.h>
+#include <utils/SystemClock.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace user_hal_helper {
+namespace {
+
+using android::base::Error;
+using android::base::Result;
+
+static constexpr const char* kSeparator = "||";
+static const size_t kNumFieldsPerUserInfo = 2;
+static const size_t kNumFieldsPerSetAssociation = 2;
+
+template <typename T>
+Result<T> verifyAndCast(int32_t value) {
+ T castValue = static_cast<T>(value);
+ const auto iter = hidl_enum_range<T>();
+ if (castValue < *iter.begin() || castValue > *std::prev(iter.end())) {
+ return Error() << "Value " << value << " not in range [" << toString(*iter.begin()) << ", "
+ << toString(*std::prev(iter.end())) << "]";
+ }
+ for (const auto& v : hidl_enum_range<T>()) {
+ if (castValue == v) {
+ return castValue;
+ }
+ }
+ return Error() << "Value " << value << " not in enum values";
+}
+
+Result<void> verifyPropValue(const VehiclePropValue& propValue, VehicleProperty vehicleProperty,
+ size_t minInt32Values) {
+ auto prop = verifyAndCast<VehicleProperty>(propValue.prop);
+ if (!prop.ok()) {
+ return Error() << "Invalid vehicle property: " << prop.error();
+ }
+ if (*prop != vehicleProperty) {
+ return Error() << "Mismatching " << toString(vehicleProperty) << " request, received "
+ << toString(*prop) << " property";
+ }
+ if (propValue.value.int32Values.size() < minInt32Values) {
+ return Error() << "Int32Values must have at least " << minInt32Values
+ << " values, received " << propValue.value.int32Values.size();
+ }
+ return {};
+}
+
+Result<void> parseUserInfo(const hidl_vec<int32_t>& int32Values, size_t startPos,
+ UserInfo* userInfo) {
+ if (int32Values.size() < startPos + kNumFieldsPerUserInfo) {
+ return Error() << "Int32Values must have at least " << startPos + 2 << " values, received "
+ << int32Values.size();
+ }
+ userInfo->userId = int32Values[startPos];
+ auto userFlags = verifyAndCast<UserFlags>(int32Values[startPos + 1]);
+ if (!userFlags.ok()) {
+ return Error() << "Invalid user flags: " << userFlags.error();
+ }
+ userInfo->flags = *userFlags;
+ return {};
+}
+
+Result<void> parseUsersInfo(const hidl_vec<int32_t>& int32Values, size_t startPos,
+ UsersInfo* usersInfo) {
+ if (int32Values.size() < startPos + 3) {
+ return Error() << "Int32Values must have at least " << startPos + 3 << " values, received "
+ << int32Values.size();
+ }
+ auto ret = parseUserInfo(int32Values, startPos, &usersInfo->currentUser);
+ if (!ret.ok()) {
+ return ret;
+ }
+ usersInfo->numberUsers = int32Values[startPos + 2];
+ usersInfo->existingUsers.resize(usersInfo->numberUsers);
+ for (size_t i = 0; i < static_cast<size_t>(usersInfo->numberUsers); ++i) {
+ ret = parseUserInfo(int32Values, startPos + 3 + (kNumFieldsPerUserInfo * i),
+ &usersInfo->existingUsers[i]);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse existing user '" << i << "' info: " << ret.error();
+ }
+ }
+ return {};
+}
+
+Result<void> parseUserAssociationTypes(
+ const hidl_vec<int32_t>& int32Values, size_t startPos, size_t numberAssociationTypes,
+ hidl_vec<UserIdentificationAssociationType>* associationTypes) {
+ size_t minInt32Values = startPos + numberAssociationTypes;
+ if (int32Values.size() < minInt32Values) {
+ return Error() << "Int32Values must have at least " << minInt32Values
+ << " values, received " << int32Values.size();
+ }
+ associationTypes->resize(numberAssociationTypes);
+ for (size_t i = 0; i < static_cast<size_t>(numberAssociationTypes); ++i) {
+ size_t pos = startPos + i;
+ auto type = verifyAndCast<UserIdentificationAssociationType>(int32Values[pos]);
+ if (!type.ok()) {
+ return Error() << "Invalid association type in query '" << i << "': " << type.error();
+ }
+ (*associationTypes)[i] = *type;
+ }
+ return {};
+}
+
+Result<void> parseUserAssociations(const hidl_vec<int32_t>& int32Values, size_t startPos,
+ size_t numberAssociations,
+ hidl_vec<UserIdentificationSetAssociation>* associations) {
+ size_t minInt32Values = startPos + (numberAssociations * kNumFieldsPerSetAssociation);
+ if (int32Values.size() < minInt32Values) {
+ return Error() << "Int32Values must have at least " << minInt32Values
+ << " values, received " << int32Values.size();
+ }
+ associations->resize(numberAssociations);
+ for (size_t i = 0; i < static_cast<size_t>(numberAssociations); ++i) {
+ size_t pos = startPos + (kNumFieldsPerSetAssociation * i);
+ auto type = verifyAndCast<UserIdentificationAssociationType>(int32Values[pos]);
+ if (!type.ok()) {
+ return Error() << "Invalid association type in request '" << i << "': " << type.error();
+ }
+ (*associations)[i].type = *type;
+ auto value = verifyAndCast<UserIdentificationAssociationSetValue>(int32Values[pos + 1]);
+ if (!value.ok()) {
+ return Error() << "Invalid association set value in request '" << i
+ << "': " << value.error();
+ }
+ (*associations)[i].value = *value;
+ }
+ return {};
+}
+
+} // namespace
+
+Result<InitialUserInfoRequest> toInitialUserInfoRequest(const VehiclePropValue& propValue) {
+ auto ret = verifyPropValue(propValue, VehicleProperty::INITIAL_USER_INFO, 2);
+ if (!ret.ok()) {
+ return ret.error();
+ }
+ InitialUserInfoRequest request;
+ request.requestId = propValue.value.int32Values[0];
+ auto requestType = verifyAndCast<InitialUserInfoRequestType>(propValue.value.int32Values[1]);
+ if (!requestType.ok()) {
+ return Error() << "Invalid InitialUserInfoRequestType: " << requestType.error();
+ }
+ request.requestType = *requestType;
+ ret = parseUsersInfo(propValue.value.int32Values, 2, &request.usersInfo);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse users info: " << ret.error();
+ }
+ return request;
+}
+
+Result<SwitchUserRequest> toSwitchUserRequest(const VehiclePropValue& propValue) {
+ auto ret = verifyPropValue(propValue, VehicleProperty::SWITCH_USER, 2);
+ if (!ret.ok()) {
+ return ret.error();
+ }
+ SwitchUserRequest request;
+ auto messageType = verifyAndCast<SwitchUserMessageType>(propValue.value.int32Values[1]);
+ if (!messageType.ok()) {
+ return Error() << "Invalid SwitchUserMessageType: " << messageType.error();
+ }
+ if (*messageType != SwitchUserMessageType::LEGACY_ANDROID_SWITCH &&
+ *messageType != SwitchUserMessageType::ANDROID_SWITCH &&
+ *messageType != SwitchUserMessageType::ANDROID_POST_SWITCH) {
+ return Error() << "Invalid " << toString(*messageType) << " from Android System";
+ }
+ request.requestId = propValue.value.int32Values[0];
+ request.messageType = *messageType;
+ ret = parseUserInfo(propValue.value.int32Values, 2, &request.targetUser);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse target user info: " << ret.error();
+ }
+ ret = parseUsersInfo(propValue.value.int32Values, 4, &request.usersInfo);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse users info: " << ret.error();
+ }
+ return request;
+}
+
+Result<CreateUserRequest> toCreateUserRequest(const VehiclePropValue& propValue) {
+ auto ret = verifyPropValue(propValue, VehicleProperty::CREATE_USER, 1);
+ if (!ret.ok()) {
+ return ret.error();
+ }
+ CreateUserRequest request;
+ request.requestId = propValue.value.int32Values[0];
+ ret = parseUserInfo(propValue.value.int32Values, 1, &request.newUserInfo);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse new user info: " << ret.error();
+ }
+ request.newUserName = propValue.value.stringValue;
+ ret = parseUsersInfo(propValue.value.int32Values, 3, &request.usersInfo);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse users info: " << ret.error();
+ }
+ return request;
+}
+
+Result<RemoveUserRequest> toRemoveUserRequest(const VehiclePropValue& propValue) {
+ auto ret = verifyPropValue(propValue, VehicleProperty::REMOVE_USER, 1);
+ if (!ret.ok()) {
+ return ret.error();
+ }
+ RemoveUserRequest request;
+ request.requestId = propValue.value.int32Values[0];
+ ret = parseUserInfo(propValue.value.int32Values, 1, &request.removedUserInfo);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse removed user info: " << ret.error();
+ }
+ ret = parseUsersInfo(propValue.value.int32Values, 3, &request.usersInfo);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse users info: " << ret.error();
+ }
+ return request;
+}
+
+Result<UserIdentificationGetRequest> toUserIdentificationGetRequest(
+ const VehiclePropValue& propValue) {
+ auto ret = verifyPropValue(propValue, VehicleProperty::USER_IDENTIFICATION_ASSOCIATION, 4);
+ if (!ret.ok()) {
+ return ret.error();
+ }
+ UserIdentificationGetRequest request;
+ request.requestId = propValue.value.int32Values[0];
+ ret = parseUserInfo(propValue.value.int32Values, 1, &request.userInfo);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse user info: " << ret.error();
+ }
+ request.numberAssociationTypes = propValue.value.int32Values[3];
+ ret = parseUserAssociationTypes(propValue.value.int32Values, 4, request.numberAssociationTypes,
+ &request.associationTypes);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse UserIdentificationAssociationType: " << ret.error();
+ }
+ return request;
+}
+
+Result<UserIdentificationSetRequest> toUserIdentificationSetRequest(
+ const VehiclePropValue& propValue) {
+ auto ret = verifyPropValue(propValue, VehicleProperty::USER_IDENTIFICATION_ASSOCIATION, 4);
+ if (!ret.ok()) {
+ return ret.error();
+ }
+ UserIdentificationSetRequest request;
+ request.requestId = propValue.value.int32Values[0];
+ ret = parseUserInfo(propValue.value.int32Values, 1, &request.userInfo);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse user info: " << ret.error();
+ }
+ request.numberAssociations = propValue.value.int32Values[3];
+ ret = parseUserAssociations(propValue.value.int32Values, 4, request.numberAssociations,
+ &request.associations);
+ if (!ret.ok()) {
+ return Error() << "Failed to parse UserIdentificationSetAssociation: " << ret.error();
+ }
+ return request;
+}
+
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const SwitchUserRequest& request) {
+ if (request.messageType != SwitchUserMessageType::VEHICLE_REQUEST) {
+ ALOGE("Invalid %s message type %s from HAL", toString(VehicleProperty::SWITCH_USER).c_str(),
+ toString(request.messageType).c_str());
+ return nullptr;
+ }
+ auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue());
+ propValue->prop = static_cast<int32_t>(VehicleProperty::SWITCH_USER);
+ propValue->timestamp = elapsedRealtimeNano();
+ propValue->value.int32Values.resize(3);
+ propValue->value.int32Values[0] = static_cast<int32_t>(request.requestId);
+ propValue->value.int32Values[1] = static_cast<int32_t>(request.messageType);
+ propValue->value.int32Values[2] = static_cast<int32_t>(request.targetUser.userId);
+ return propValue;
+}
+
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const InitialUserInfoResponse& response) {
+ auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue());
+ propValue->prop = static_cast<int32_t>(VehicleProperty::INITIAL_USER_INFO);
+ propValue->timestamp = elapsedRealtimeNano();
+ propValue->value.int32Values.resize(4);
+ propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId);
+ propValue->value.int32Values[1] = static_cast<int32_t>(response.action);
+ propValue->value.int32Values[2] = static_cast<int32_t>(response.userToSwitchOrCreate.userId);
+ propValue->value.int32Values[3] = static_cast<int32_t>(response.userToSwitchOrCreate.flags);
+ propValue->value.stringValue = std::string(response.userLocales) + std::string(kSeparator) +
+ std::string(response.userNameToCreate);
+ return propValue;
+}
+
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const SwitchUserResponse& response) {
+ auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue());
+ propValue->prop = static_cast<int32_t>(VehicleProperty::SWITCH_USER);
+ propValue->timestamp = elapsedRealtimeNano();
+ propValue->value.int32Values.resize(3);
+ propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId);
+ propValue->value.int32Values[1] = static_cast<int32_t>(response.messageType);
+ propValue->value.int32Values[2] = static_cast<int32_t>(response.status);
+ if (response.status == SwitchUserStatus::FAILURE) {
+ propValue->value.stringValue = response.errorMessage;
+ }
+ return propValue;
+}
+
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const CreateUserResponse& response) {
+ auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue());
+ propValue->prop = static_cast<int32_t>(VehicleProperty::CREATE_USER);
+ propValue->timestamp = elapsedRealtimeNano();
+ propValue->value.int32Values.resize(2);
+ propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId);
+ propValue->value.int32Values[1] = static_cast<int32_t>(response.status);
+ if (response.status == CreateUserStatus::FAILURE) {
+ propValue->value.stringValue = response.errorMessage;
+ }
+ return propValue;
+}
+
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const UserIdentificationResponse& response) {
+ auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue());
+ propValue->prop = static_cast<int32_t>(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
+ propValue->timestamp = elapsedRealtimeNano();
+ propValue->value.int32Values.resize(2 + (response.numberAssociation * 2));
+ propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId);
+ propValue->value.int32Values[1] = static_cast<int32_t>(response.numberAssociation);
+ for (size_t i = 0; i < static_cast<size_t>(response.numberAssociation); ++i) {
+ size_t int32ValuesPos = 2 + (2 * i);
+ propValue->value.int32Values[int32ValuesPos] =
+ static_cast<int32_t>(response.associations[i].type);
+ propValue->value.int32Values[int32ValuesPos + 1] =
+ static_cast<int32_t>(response.associations[i].value);
+ }
+ if (!response.errorMessage.empty()) {
+ propValue->value.stringValue = response.errorMessage;
+ }
+ return propValue;
+}
+
+} // namespace user_hal_helper
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/2.0/utils/UserHalHelper.h b/automotive/vehicle/2.0/utils/UserHalHelper.h
new file mode 100644
index 0000000..bee34cf
--- /dev/null
+++ b/automotive/vehicle/2.0/utils/UserHalHelper.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 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_V2_0_impl_UserHalHelper_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_UserHalHelper_H_
+
+#include <android-base/result.h>
+#include <android/hardware/automotive/vehicle/2.0/types.h>
+
+#include <functional>
+#include <memory>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace user_hal_helper {
+
+// Below functions parse VehiclePropValues to the respective User HAL request structs. On success,
+// these functions return the User HAL struct. Otherwise, they return the error.
+android::base::Result<InitialUserInfoRequest> toInitialUserInfoRequest(
+ const VehiclePropValue& propValue);
+android::base::Result<SwitchUserRequest> toSwitchUserRequest(const VehiclePropValue& propValue);
+android::base::Result<CreateUserRequest> toCreateUserRequest(const VehiclePropValue& propValue);
+android::base::Result<RemoveUserRequest> toRemoveUserRequest(const VehiclePropValue& propValue);
+android::base::Result<UserIdentificationGetRequest> toUserIdentificationGetRequest(
+ const VehiclePropValue& propValue);
+android::base::Result<UserIdentificationSetRequest> toUserIdentificationSetRequest(
+ const VehiclePropValue& propValue);
+
+// Below functions convert the User HAL structs to VehiclePropValues. On success, these functions
+// return the pointer to VehiclePropValue. Otherwise, they return nullptr.
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const SwitchUserRequest& request);
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const InitialUserInfoResponse& response);
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const SwitchUserResponse& response);
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const CreateUserResponse& response);
+std::unique_ptr<VehiclePropValue> toVehiclePropValue(const UserIdentificationResponse& response);
+
+} // namespace user_hal_helper
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_V2_0_impl_UserHalHelper_H_
diff --git a/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp b/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp
new file mode 100644
index 0000000..7da87a2
--- /dev/null
+++ b/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp
@@ -0,0 +1,666 @@
+/*
+ * Copyright (C) 2020 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 "UserHalHelper.h"
+
+#include <gtest/gtest.h>
+
+#include <cstdint>
+
+#include "gmock/gmock.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace user_hal_helper {
+
+namespace {
+
+using testing::Eq;
+using testing::Gt;
+using testing::IsNull;
+using testing::NotNull;
+using testing::Pointee;
+
+constexpr int32_t INITIAL_USER_INFO = static_cast<int32_t>(VehicleProperty::INITIAL_USER_INFO);
+constexpr int32_t SWITCH_USER = static_cast<int32_t>(VehicleProperty::SWITCH_USER);
+constexpr int32_t CREATE_USER = static_cast<int32_t>(VehicleProperty::CREATE_USER);
+constexpr int32_t REMOVE_USER = static_cast<int32_t>(VehicleProperty::REMOVE_USER);
+constexpr int32_t USER_IDENTIFICATION_ASSOCIATION =
+ static_cast<int32_t>(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
+
+constexpr int32_t FIRST_BOOT_AFTER_OTA =
+ static_cast<int32_t>(InitialUserInfoRequestType::FIRST_BOOT_AFTER_OTA);
+constexpr int32_t LEGACY_ANDROID_SWITCH =
+ static_cast<int32_t>(SwitchUserMessageType::LEGACY_ANDROID_SWITCH);
+constexpr int32_t VEHICLE_REQUEST = static_cast<int32_t>(SwitchUserMessageType::VEHICLE_REQUEST);
+
+constexpr int32_t GUEST_USER = static_cast<int32_t>(UserFlags::GUEST);
+constexpr int32_t NONE_USER = static_cast<int32_t>(UserFlags::NONE);
+constexpr int32_t SYSTEM_USER = static_cast<int32_t>(UserFlags::SYSTEM);
+
+constexpr int32_t USER_ID_ASSOC_KEY_FOB =
+ static_cast<int32_t>(UserIdentificationAssociationType::KEY_FOB);
+constexpr int32_t USER_ID_ASSOC_CUSTOM_1 =
+ static_cast<int32_t>(UserIdentificationAssociationType::CUSTOM_1);
+
+constexpr int32_t USER_ID_ASSOC_SET_CURRENT_USER =
+ static_cast<int32_t>(UserIdentificationAssociationSetValue::ASSOCIATE_CURRENT_USER);
+constexpr int32_t USER_ID_ASSOC_UNSET_CURRENT_USER =
+ static_cast<int32_t>(UserIdentificationAssociationSetValue::DISASSOCIATE_CURRENT_USER);
+
+constexpr int32_t USER_ID_ASSOC_CURRENT_USER =
+ static_cast<int32_t>(UserIdentificationAssociationValue::ASSOCIATED_CURRENT_USER);
+constexpr int32_t USER_ID_ASSOC_NO_USER =
+ static_cast<int32_t>(UserIdentificationAssociationValue::NOT_ASSOCIATED_ANY_USER);
+
+} // namespace
+
+TEST(UserHalHelperTest, TestToInitialUserInfoRequest) {
+ VehiclePropValue propValue{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0, SYSTEM_USER,
+ 10, NONE_USER}},
+ };
+ InitialUserInfoRequest expected{
+ .requestId = 23,
+ .requestType = InitialUserInfoRequestType::FIRST_BOOT_AFTER_OTA,
+ .usersInfo = {{10, UserFlags::NONE},
+ 2,
+ {{0, UserFlags::SYSTEM}, {10, UserFlags::NONE}}},
+ };
+
+ auto actual = toInitialUserInfoRequest(propValue);
+
+ ASSERT_TRUE(actual.ok()) << actual.error().message();
+ EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToInitialUserInfoRequestWithMismatchingPropType) {
+ VehiclePropValue propValue{
+ .prop = INT32_MAX,
+ .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0, SYSTEM_USER,
+ 10, NONE_USER}},
+ };
+
+ auto actual = toInitialUserInfoRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToInitialUserInfoRequestWithInvalidRequestType) {
+ VehiclePropValue propValue{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, INT32_MAX, 10, NONE_USER, 2, 0, SYSTEM_USER, 10,
+ NONE_USER}},
+ };
+
+ auto actual = toInitialUserInfoRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on invalid request type";
+}
+
+TEST(UserHalHelperTest, TestFailsToInitialUserInfoRequestWithInvalidUserFlag) {
+ VehiclePropValue propValue{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0, SYSTEM_USER,
+ 10, INT32_MAX}},
+ };
+
+ auto actual = toInitialUserInfoRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on invalid user flags";
+}
+
+TEST(UserHalHelperTest, TestFailsToInitialUserInfoRequestWithIncompleteUsersInfo) {
+ VehiclePropValue propValueMissingSecondUserInfo{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0,
+ SYSTEM_USER /*Missing 2nd UserInfo*/}},
+ };
+
+ auto actual = toInitialUserInfoRequest(propValueMissingSecondUserInfo);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing second user info";
+
+ VehiclePropValue propValueMissingUsersInfo{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, /*Missing UsersInfo*/}},
+ };
+
+ actual = toInitialUserInfoRequest(propValueMissingUsersInfo);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing users info";
+}
+
+TEST(UserHalHelperTest, TestToSwitchUserRequest) {
+ VehiclePropValue propValue{
+ .prop = SWITCH_USER,
+ .value = {.int32Values = {23, LEGACY_ANDROID_SWITCH, 0, SYSTEM_USER, 10, NONE_USER, 2,
+ 0, SYSTEM_USER, 10, NONE_USER}},
+ };
+ SwitchUserRequest expected{
+ .requestId = 23,
+ .messageType = SwitchUserMessageType::LEGACY_ANDROID_SWITCH,
+ .targetUser = {0, UserFlags::SYSTEM},
+ .usersInfo = {{10, UserFlags::NONE},
+ 2,
+ {{0, UserFlags::SYSTEM}, {10, UserFlags::NONE}}},
+ };
+
+ auto actual = toSwitchUserRequest(propValue);
+
+ ASSERT_TRUE(actual.ok()) << actual.error().message();
+ EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToSwitchUserRequestWithMismatchingPropType) {
+ VehiclePropValue propValue{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, LEGACY_ANDROID_SWITCH, 0, SYSTEM_USER, 10, NONE_USER, 2,
+ 0, SYSTEM_USER, 10, NONE_USER}},
+ };
+
+ auto actual = toSwitchUserRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToSwitchUserRequestWithInvalidMessageType) {
+ VehiclePropValue propValueIncompatibleMessageType{
+ .prop = SWITCH_USER,
+ .value = {.int32Values = {23, VEHICLE_REQUEST, 0, SYSTEM_USER, 10, NONE_USER, 2, 0,
+ SYSTEM_USER, 10, NONE_USER}},
+ };
+
+ auto actual = toSwitchUserRequest(propValueIncompatibleMessageType);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on incompatible message type";
+
+ VehiclePropValue propValueInvalidMessageType{
+ .prop = SWITCH_USER,
+ .value = {.int32Values = {23, INT32_MAX, 0, SYSTEM_USER, 10, NONE_USER, 2, 0,
+ SYSTEM_USER, 10, NONE_USER}},
+ };
+
+ actual = toSwitchUserRequest(propValueInvalidMessageType);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on invalid message type";
+}
+
+TEST(UserHalHelperTest, TestFailsToSwitchUserRequestWithIncompleteUsersInfo) {
+ VehiclePropValue propValueMissingSecondUserInfo{
+ .prop = SWITCH_USER,
+ .value = {.int32Values = {23, LEGACY_ANDROID_SWITCH, 0, SYSTEM_USER, 10, NONE_USER, 2,
+ 0, SYSTEM_USER,
+ /*Missing 2nd UserInfo*/}},
+ };
+
+ auto actual = toSwitchUserRequest(propValueMissingSecondUserInfo);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing second user info";
+
+ VehiclePropValue propValueMissingUsersInfo{
+ .prop = SWITCH_USER,
+ .value = {.int32Values = {23, LEGACY_ANDROID_SWITCH, 0, SYSTEM_USER,
+ /*Missing UsersInfo*/}},
+ };
+
+ actual = toSwitchUserRequest(propValueMissingUsersInfo);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing users info";
+
+ VehiclePropValue propValueMissingTargetUser{
+ .prop = SWITCH_USER,
+ .value = {.int32Values = {23, LEGACY_ANDROID_SWITCH, /*Missing target UserInfo*/}},
+ };
+
+ actual = toSwitchUserRequest(propValueMissingTargetUser);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing target user info";
+}
+
+TEST(UserHalHelperTest, TestToCreateUserRequest) {
+ VehiclePropValue propValue{
+ .prop = CREATE_USER,
+ .value = {.int32Values = {23, 11, GUEST_USER, 10, NONE_USER, 2, 0, SYSTEM_USER, 10,
+ NONE_USER},
+ .stringValue = "Guest11"},
+ };
+ CreateUserRequest expected{
+ .requestId = 23,
+ .newUserInfo = {11, UserFlags::GUEST},
+ .newUserName = "Guest11",
+ .usersInfo = {{10, UserFlags::NONE},
+ 2,
+ {{0, UserFlags::SYSTEM}, {10, UserFlags::NONE}}},
+ };
+
+ auto actual = toCreateUserRequest(propValue);
+
+ ASSERT_TRUE(actual.ok()) << actual.error().message();
+ EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToCreateUserRequestWithMismatchingPropType) {
+ VehiclePropValue propValue{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, 11, GUEST_USER, 10, NONE_USER, 2, 0, SYSTEM_USER, 10,
+ NONE_USER},
+ .stringValue = "Guest11"},
+ };
+
+ auto actual = toCreateUserRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToCreateUserRequestWithIncompleteUsersInfo) {
+ VehiclePropValue propValueMissingSecondUserInfo{
+ .prop = CREATE_USER,
+ .value = {.int32Values = {23, 11, GUEST_USER, 10, NONE_USER, 2, 0,
+ SYSTEM_USER /*Missing 2nd UserInfo*/},
+ .stringValue = "Guest11"},
+ };
+
+ auto actual = toCreateUserRequest(propValueMissingSecondUserInfo);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing second user info";
+
+ VehiclePropValue propValueMissingUsersInfo{
+ .prop = CREATE_USER,
+ .value = {.int32Values = {23, 11, GUEST_USER, /*Missing UsersInfo*/},
+ .stringValue = "Guest11"},
+ };
+
+ actual = toCreateUserRequest(propValueMissingUsersInfo);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing users info";
+
+ VehiclePropValue propValueMissingCreateUserInfo{
+ .prop = CREATE_USER,
+ .value = {.int32Values = {23, /*Missing create UserInfo*/}, .stringValue = "Guest11"},
+ };
+
+ actual = toCreateUserRequest(propValueMissingCreateUserInfo);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing create user info";
+}
+
+TEST(UserHalHelperTest, TestToRemoveUserRequest) {
+ VehiclePropValue propValue{
+ .prop = REMOVE_USER,
+ .value = {.int32Values = {23, 10, NONE_USER, 10, NONE_USER, 2, 0, SYSTEM_USER, 10,
+ NONE_USER}},
+ };
+ RemoveUserRequest expected{
+ .requestId = 23,
+ .removedUserInfo = {10, UserFlags::NONE},
+ .usersInfo = {{10, UserFlags::NONE},
+ 2,
+ {{0, UserFlags::SYSTEM}, {10, UserFlags::NONE}}},
+ };
+
+ auto actual = toRemoveUserRequest(propValue);
+
+ ASSERT_TRUE(actual.ok()) << actual.error().message();
+ EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToRemoveUserRequestWithMismatchingPropType) {
+ VehiclePropValue propValue{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, 10, NONE_USER, 10, NONE_USER, 2, 0, SYSTEM_USER, 10,
+ NONE_USER}},
+ };
+
+ auto actual = toRemoveUserRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToRemoveUserRequestWithIncompleteUsersInfo) {
+ VehiclePropValue propValueMissingSecondUserInfo{
+ .prop = REMOVE_USER,
+ .value = {.int32Values = {23, 10, NONE_USER, 10, NONE_USER, 2, 0,
+ SYSTEM_USER /*Missing 2nd UserInfo*/}},
+ };
+
+ auto actual = toRemoveUserRequest(propValueMissingSecondUserInfo);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing second user info";
+
+ VehiclePropValue propValueMissingUsersInfo{
+ .prop = REMOVE_USER,
+ .value = {.int32Values = {23, 10, NONE_USER, /*Missing UsersInfo*/}},
+ };
+
+ actual = toRemoveUserRequest(propValueMissingUsersInfo);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing users info";
+
+ VehiclePropValue propValueMissingRemoveUserInfo{
+ .prop = REMOVE_USER,
+ .value = {.int32Values = {23, /*Missing remove UserInfo*/}},
+ };
+
+ actual = toRemoveUserRequest(propValueMissingRemoveUserInfo);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing remove user info";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationGetRequest) {
+ VehiclePropValue propValue{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, 10, NONE_USER, 2, USER_ID_ASSOC_KEY_FOB,
+ USER_ID_ASSOC_CUSTOM_1}},
+ };
+ UserIdentificationGetRequest expected{
+ .requestId = 23,
+ .userInfo = {10, UserFlags::NONE},
+ .numberAssociationTypes = 2,
+ .associationTypes = {UserIdentificationAssociationType::KEY_FOB,
+ UserIdentificationAssociationType::CUSTOM_1},
+ };
+
+ auto actual = toUserIdentificationGetRequest(propValue);
+
+ ASSERT_TRUE(actual.ok()) << actual.error().message();
+ EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationGetRequestWithMismatchingPropType) {
+ VehiclePropValue propValue{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, 10, NONE_USER, 2, USER_ID_ASSOC_KEY_FOB,
+ USER_ID_ASSOC_CUSTOM_1}},
+ };
+
+ auto actual = toUserIdentificationGetRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationGetRequestWithInvalidAssociationTypes) {
+ VehiclePropValue propValue{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, 10, NONE_USER, 1, INT32_MAX}},
+ };
+
+ auto actual = toUserIdentificationGetRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on invalid association type";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationGetRequestWithIncompleteAssociationTypes) {
+ VehiclePropValue propValueMissingSecondAssociationType{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, 10, NONE_USER, 2,
+ USER_ID_ASSOC_KEY_FOB /*Missing 2nd association type*/}},
+ };
+
+ auto actual = toUserIdentificationGetRequest(propValueMissingSecondAssociationType);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing second association type";
+
+ VehiclePropValue propValueMissingNumberAssociationTypes{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, 10, NONE_USER, /*Missing number association types*/}},
+ };
+
+ actual = toUserIdentificationGetRequest(propValueMissingNumberAssociationTypes);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing number association types";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationGetRequestWithMissingUserInfo) {
+ VehiclePropValue propValue{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, /*Missing user info*/}},
+ };
+
+ auto actual = toUserIdentificationGetRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing UserInfo";
+}
+
+TEST(UserHalHelperTest, TestToUserIdentificationSetRequest) {
+ VehiclePropValue propValue{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, 10, NONE_USER, 2, USER_ID_ASSOC_KEY_FOB,
+ USER_ID_ASSOC_SET_CURRENT_USER, USER_ID_ASSOC_CUSTOM_1,
+ USER_ID_ASSOC_UNSET_CURRENT_USER}},
+ };
+ UserIdentificationSetRequest expected{
+ .requestId = 23,
+ .userInfo = {10, UserFlags::NONE},
+ .numberAssociations = 2,
+ .associations = {{UserIdentificationAssociationType::KEY_FOB,
+ UserIdentificationAssociationSetValue::ASSOCIATE_CURRENT_USER},
+ {UserIdentificationAssociationType::CUSTOM_1,
+ UserIdentificationAssociationSetValue::DISASSOCIATE_CURRENT_USER}},
+ };
+
+ auto actual = toUserIdentificationSetRequest(propValue);
+
+ ASSERT_TRUE(actual.ok()) << actual.error().message();
+ EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationSetRequestWithMismatchingPropType) {
+ VehiclePropValue propValue{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, 10, NONE_USER, 2, USER_ID_ASSOC_KEY_FOB,
+ USER_ID_ASSOC_SET_CURRENT_USER, USER_ID_ASSOC_CUSTOM_1,
+ USER_ID_ASSOC_UNSET_CURRENT_USER}},
+ };
+
+ auto actual = toUserIdentificationSetRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on mismatching property type";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationSetRequestWithInvalidAssociations) {
+ VehiclePropValue propValueInvalidAssociationType{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, 10, NONE_USER, 1, INT32_MAX,
+ USER_ID_ASSOC_SET_CURRENT_USER}},
+ };
+
+ auto actual = toUserIdentificationSetRequest(propValueInvalidAssociationType);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on invalid association type";
+
+ VehiclePropValue propValueInvalidAssociationValue{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, 10, NONE_USER, USER_ID_ASSOC_KEY_FOB, INT32_MAX}},
+ };
+
+ actual = toUserIdentificationSetRequest(propValueInvalidAssociationValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing number association types";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationSetRequestWithIncompleteAssociations) {
+ VehiclePropValue propValueMissingSecondAssociationType{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, 10, NONE_USER, 2, USER_ID_ASSOC_KEY_FOB,
+ USER_ID_ASSOC_SET_CURRENT_USER,
+ /*Missing 2nd association*/}},
+ };
+
+ auto actual = toUserIdentificationSetRequest(propValueMissingSecondAssociationType);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing second association type";
+
+ VehiclePropValue propValueMissingNumberAssociationTypes{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, 10, NONE_USER, /*Missing number associations*/}},
+ };
+
+ actual = toUserIdentificationSetRequest(propValueMissingNumberAssociationTypes);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing number association types";
+}
+
+TEST(UserHalHelperTest, TestFailsToUserIdentificationSetRequestWithMissingUserInfo) {
+ VehiclePropValue propValue{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, /*Missing user info*/}},
+ };
+
+ auto actual = toUserIdentificationSetRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on missing UserInfo";
+}
+
+TEST(UserHalHelperTest, TestSwitchUserRequestToVehiclePropValue) {
+ SwitchUserRequest request{
+ .requestId = 23,
+ .messageType = SwitchUserMessageType::VEHICLE_REQUEST,
+ .targetUser = {11, UserFlags::GUEST},
+ };
+ VehiclePropValue expected{
+ .prop = SWITCH_USER,
+ .value = {.int32Values = {23,
+ static_cast<int32_t>(SwitchUserMessageType::VEHICLE_REQUEST),
+ 11}},
+ };
+
+ auto actual = toVehiclePropValue(request);
+
+ ASSERT_THAT(actual, NotNull());
+ EXPECT_THAT(actual->timestamp, Gt(0));
+ // Don't rely on real timestamp in tests as the expected and actual objects won't have the same
+ // timestamps. Thus remove the timestamps before comparing them.
+ actual->timestamp = 0;
+ EXPECT_THAT(actual, Pointee(Eq(expected)));
+}
+
+TEST(UserHalHelperTest, TestFailsSwitchUserRequestToVehiclePropValueWithIncompatibleMessageType) {
+ SwitchUserRequest request{
+ .requestId = 23,
+ .messageType = SwitchUserMessageType::VEHICLE_RESPONSE,
+ .targetUser = {11, UserFlags::GUEST},
+ };
+
+ auto actual = toVehiclePropValue(request);
+
+ EXPECT_THAT(actual, IsNull());
+}
+
+TEST(UserHalHelperTest, TestInitialUserInfoResponseToVehiclePropValue) {
+ InitialUserInfoResponse response{
+ .requestId = 23,
+ .action = InitialUserInfoResponseAction::CREATE,
+ .userToSwitchOrCreate = {11, UserFlags::GUEST},
+ .userLocales = "en-US,pt-BR",
+ .userNameToCreate = "Owner",
+ };
+ VehiclePropValue expected{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23,
+ static_cast<int32_t>(InitialUserInfoResponseAction::CREATE),
+ 11, GUEST_USER},
+ .stringValue = "en-US,pt-BR||Owner"},
+ };
+
+ auto actual = toVehiclePropValue(response);
+
+ ASSERT_THAT(actual, NotNull());
+ EXPECT_THAT(actual->timestamp, Gt(0));
+ actual->timestamp = 0;
+ EXPECT_THAT(actual, Pointee(Eq(expected)));
+}
+
+TEST(UserHalHelperTest, TestSwitchUserResponseToVehiclePropValue) {
+ SwitchUserResponse response{
+ .requestId = 23,
+ .messageType = SwitchUserMessageType::VEHICLE_RESPONSE,
+ .status = SwitchUserStatus::FAILURE,
+ .errorMessage = "random error",
+ };
+ VehiclePropValue expected{
+ .prop = SWITCH_USER,
+ .value = {.int32Values = {23,
+ static_cast<int32_t>(SwitchUserMessageType::VEHICLE_RESPONSE),
+ static_cast<int32_t>(SwitchUserStatus::FAILURE)},
+ .stringValue = "random error"},
+ };
+
+ auto actual = toVehiclePropValue(response);
+
+ ASSERT_THAT(actual, NotNull());
+ EXPECT_THAT(actual->timestamp, Gt(0));
+ actual->timestamp = 0;
+ EXPECT_THAT(actual, Pointee(Eq(expected)));
+}
+
+TEST(UserHalHelperTest, TestCreateUserResponseToVehiclePropValue) {
+ CreateUserResponse response{
+ .requestId = 23,
+ .status = CreateUserStatus::FAILURE,
+ .errorMessage = "random error",
+ };
+ VehiclePropValue expected{
+ .prop = CREATE_USER,
+ .value = {.int32Values = {23, static_cast<int32_t>(CreateUserStatus::FAILURE)},
+ .stringValue = "random error"},
+ };
+
+ auto actual = toVehiclePropValue(response);
+
+ ASSERT_THAT(actual, NotNull());
+ EXPECT_THAT(actual->timestamp, Gt(0));
+ actual->timestamp = 0;
+ EXPECT_THAT(actual, Pointee(Eq(expected)));
+}
+
+TEST(UserHalHelperTest, TestUserIdentificationResponseToVehiclePropValue) {
+ UserIdentificationResponse response{
+ .requestId = 23,
+ .numberAssociation = 2,
+ .associations = {{UserIdentificationAssociationType::KEY_FOB,
+ UserIdentificationAssociationValue::ASSOCIATED_CURRENT_USER},
+ {UserIdentificationAssociationType::CUSTOM_1,
+ UserIdentificationAssociationValue::NOT_ASSOCIATED_ANY_USER}},
+ .errorMessage = "random error",
+ };
+ VehiclePropValue expected{
+ .prop = USER_IDENTIFICATION_ASSOCIATION,
+ .value = {.int32Values = {23, 2, USER_ID_ASSOC_KEY_FOB, USER_ID_ASSOC_CURRENT_USER,
+ USER_ID_ASSOC_CUSTOM_1, USER_ID_ASSOC_NO_USER},
+ .stringValue = "random error"},
+ };
+
+ auto actual = toVehiclePropValue(response);
+
+ ASSERT_THAT(actual, NotNull());
+ EXPECT_THAT(actual->timestamp, Gt(0));
+ actual->timestamp = 0;
+ EXPECT_THAT(actual, Pointee(Eq(expected)));
+}
+
+} // namespace user_hal_helper
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android