Merge "Add a native UserHalHelper library."

This commit is contained in:
Felipe Leme 2020-09-11 17:43:04 +00:00 committed by Gerrit Code Review
commit 82120a8ce4
4 changed files with 1135 additions and 0 deletions

View file

@ -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"],
}

View file

@ -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

View file

@ -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_

View file

@ -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