/* * 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. */ #define LOG_TAG "UserHalHelper" #include "UserHalHelper.h" #include #include #include #include namespace android { namespace hardware { namespace automotive { namespace vehicle { namespace fake { namespace user_hal_helper { namespace { using ::aidl::android::hardware::automotive::vehicle::CreateUserRequest; using ::aidl::android::hardware::automotive::vehicle::CreateUserResponse; using ::aidl::android::hardware::automotive::vehicle::CreateUserStatus; using ::aidl::android::hardware::automotive::vehicle::InitialUserInfoRequest; using ::aidl::android::hardware::automotive::vehicle::InitialUserInfoRequestType; using ::aidl::android::hardware::automotive::vehicle::InitialUserInfoResponse; using ::aidl::android::hardware::automotive::vehicle::RemoveUserRequest; using ::aidl::android::hardware::automotive::vehicle::SwitchUserMessageType; using ::aidl::android::hardware::automotive::vehicle::SwitchUserRequest; using ::aidl::android::hardware::automotive::vehicle::SwitchUserResponse; using ::aidl::android::hardware::automotive::vehicle::SwitchUserStatus; using ::aidl::android::hardware::automotive::vehicle::UserIdentificationAssociationSetValue; using ::aidl::android::hardware::automotive::vehicle::UserIdentificationAssociationType; using ::aidl::android::hardware::automotive::vehicle::UserIdentificationGetRequest; using ::aidl::android::hardware::automotive::vehicle::UserIdentificationResponse; using ::aidl::android::hardware::automotive::vehicle::UserIdentificationSetAssociation; using ::aidl::android::hardware::automotive::vehicle::UserIdentificationSetRequest; using ::aidl::android::hardware::automotive::vehicle::UserInfo; using ::aidl::android::hardware::automotive::vehicle::UsersInfo; using ::aidl::android::hardware::automotive::vehicle::VehicleProperty; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::android::base::Error; using ::android::base::Result; constexpr const char kSeparator[] = "||"; constexpr size_t kNumFieldsPerUserInfo = 2; constexpr size_t kNumFieldsPerSetAssociation = 2; Result verifyPropValue(const VehiclePropValue& propValue, VehicleProperty vehicleProperty, size_t minInt32Values) { auto prop = verifyAndCast(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 parseUserInfo(const std::vector& 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]; int32_t intUserFlags = int32Values[startPos + 1]; const int32_t combinedFlags = UserInfo::USER_FLAG_SYSTEM | UserInfo::USER_FLAG_GUEST | UserInfo::USER_FLAG_EPHEMERAL | UserInfo::USER_FLAG_ADMIN | UserInfo::USER_FLAG_DISABLED | UserInfo::USER_FLAG_PROFILE; if ((intUserFlags & ~combinedFlags) != 0) { return Error() << "Invalid user flags: " << intUserFlags << ", must be '|' of UserFlags"; } userInfo->flags = intUserFlags; return {}; } Result parseUsersInfo(const std::vector& 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(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 parseUserAssociationTypes( const std::vector& int32Values, size_t startPos, size_t numberAssociationTypes, std::vector* 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(numberAssociationTypes); ++i) { size_t pos = startPos + i; auto type = verifyAndCast(int32Values[pos]); if (!type.ok()) { return Error() << "Invalid association type in query '" << i << "': " << type.error(); } (*associationTypes)[i] = *type; } return {}; } Result parseUserAssociations(const std::vector& int32Values, size_t startPos, size_t numberAssociations, std::vector* 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(numberAssociations); ++i) { size_t pos = startPos + (kNumFieldsPerSetAssociation * i); auto type = verifyAndCast(int32Values[pos]); if (!type.ok()) { return Error() << "Invalid association type in request '" << i << "': " << type.error(); } (*associations)[i].type = *type; auto value = verifyAndCast(int32Values[pos + 1]); if (!value.ok()) { return Error() << "Invalid association set value in request '" << i << "': " << value.error(); } (*associations)[i].value = *value; } return {}; } } // namespace template Result verifyAndCast(int32_t value) { T castValue = static_cast(value); for (const auto& v : ::ndk::enum_range()) { if (castValue == v) { return castValue; } } return Error() << "Value " << value << " not in enum values"; } Result toInitialUserInfoRequest(const VehiclePropValue& propValue) { auto ret = verifyPropValue(propValue, VehicleProperty::INITIAL_USER_INFO, /*minInt32Values=*/2); if (!ret.ok()) { return ret.error(); } InitialUserInfoRequest request; request.requestId = propValue.value.int32Values[0]; auto requestType = verifyAndCast(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 toSwitchUserRequest(const VehiclePropValue& propValue) { auto ret = verifyPropValue(propValue, VehicleProperty::SWITCH_USER, /*minInt32Values=*/2); if (!ret.ok()) { return ret.error(); } SwitchUserRequest request; auto messageType = verifyAndCast(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) << " message type from Android System"; } request.requestId = propValue.value.int32Values[0]; request.messageType = *messageType; ret = parseUserInfo(propValue.value.int32Values, /*startPos=*/2, &request.targetUser); if (!ret.ok()) { return Error() << "Failed to parse target user info: " << ret.error(); } ret = parseUsersInfo(propValue.value.int32Values, /*startPos=*/4, &request.usersInfo); if (!ret.ok()) { return Error() << "Failed to parse users info: " << ret.error(); } return request; } Result toCreateUserRequest(const VehiclePropValue& propValue) { auto ret = verifyPropValue(propValue, VehicleProperty::CREATE_USER, /*minInt32Values=*/1); if (!ret.ok()) { return ret.error(); } CreateUserRequest request; request.requestId = propValue.value.int32Values[0]; ret = parseUserInfo(propValue.value.int32Values, /*startPos=*/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, /*startPos=*/3, &request.usersInfo); if (!ret.ok()) { return Error() << "Failed to parse users info: " << ret.error(); } return request; } Result toRemoveUserRequest(const VehiclePropValue& propValue) { auto ret = verifyPropValue(propValue, VehicleProperty::REMOVE_USER, /*minInt32Values=*/1); if (!ret.ok()) { return ret.error(); } RemoveUserRequest request; request.requestId = propValue.value.int32Values[0]; ret = parseUserInfo(propValue.value.int32Values, /*startPos=*/1, &request.removedUserInfo); if (!ret.ok()) { return Error() << "Failed to parse removed user info: " << ret.error(); } ret = parseUsersInfo(propValue.value.int32Values, /*startPos=*/3, &request.usersInfo); if (!ret.ok()) { return Error() << "Failed to parse users info: " << ret.error(); } return request; } Result toUserIdentificationGetRequest( const VehiclePropValue& propValue) { auto ret = verifyPropValue(propValue, VehicleProperty::USER_IDENTIFICATION_ASSOCIATION, /*minInt32Values=*/4); if (!ret.ok()) { return ret.error(); } UserIdentificationGetRequest request; request.requestId = propValue.value.int32Values[0]; ret = parseUserInfo(propValue.value.int32Values, /*startPos=*/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 toUserIdentificationSetRequest( const VehiclePropValue& propValue) { auto ret = verifyPropValue(propValue, VehicleProperty::USER_IDENTIFICATION_ASSOCIATION, /*minInt32Values=*/4); if (!ret.ok()) { return ret.error(); } UserIdentificationSetRequest request; request.requestId = propValue.value.int32Values[0]; ret = parseUserInfo(propValue.value.int32Values, /*startPos=*/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; } Result toVehiclePropValue(VehiclePropValuePool& pool, const SwitchUserRequest& request) { if (request.messageType != SwitchUserMessageType::VEHICLE_REQUEST) { return Errorf("Invalid %s message type %s from HAL", toString(VehicleProperty::SWITCH_USER).c_str(), toString(request.messageType).c_str()); } int32_t switchUserProp = toInt(VehicleProperty::SWITCH_USER); auto propValue = pool.obtain(getPropType(switchUserProp)); propValue->prop = switchUserProp; propValue->timestamp = elapsedRealtimeNano(); propValue->value.int32Values.resize(3); propValue->value.int32Values[0] = static_cast(request.requestId); propValue->value.int32Values[1] = static_cast(request.messageType); propValue->value.int32Values[2] = static_cast(request.targetUser.userId); return propValue; } VehiclePropValuePool::RecyclableType toVehiclePropValue(VehiclePropValuePool& pool, const InitialUserInfoResponse& response) { int32_t initialUserInfoProp = toInt(VehicleProperty::INITIAL_USER_INFO); auto propValue = pool.obtain(getPropType(initialUserInfoProp)); propValue->prop = initialUserInfoProp; propValue->timestamp = elapsedRealtimeNano(); propValue->value.int32Values.resize(4); propValue->value.int32Values[0] = static_cast(response.requestId); propValue->value.int32Values[1] = static_cast(response.action); propValue->value.int32Values[2] = static_cast(response.userToSwitchOrCreate.userId); propValue->value.int32Values[3] = response.userToSwitchOrCreate.flags; propValue->value.stringValue = std::string(response.userLocales) + std::string(kSeparator) + std::string(response.userNameToCreate); return propValue; } VehiclePropValuePool::RecyclableType toVehiclePropValue(VehiclePropValuePool& pool, const SwitchUserResponse& response) { int32_t switchUserProp = toInt(VehicleProperty::SWITCH_USER); auto propValue = pool.obtain(getPropType(switchUserProp)); propValue->prop = switchUserProp; propValue->timestamp = elapsedRealtimeNano(); propValue->value.int32Values.resize(3); propValue->value.int32Values[0] = static_cast(response.requestId); propValue->value.int32Values[1] = static_cast(response.messageType); propValue->value.int32Values[2] = static_cast(response.status); if (response.status == SwitchUserStatus::FAILURE) { propValue->value.stringValue = response.errorMessage; } return propValue; } VehiclePropValuePool::RecyclableType toVehiclePropValue(VehiclePropValuePool& pool, const CreateUserResponse& response) { int32_t createUserProp = toInt(VehicleProperty::CREATE_USER); auto propValue = pool.obtain(getPropType(createUserProp)); propValue->prop = createUserProp; propValue->timestamp = elapsedRealtimeNano(); propValue->value.int32Values.resize(2); propValue->value.int32Values[0] = static_cast(response.requestId); propValue->value.int32Values[1] = static_cast(response.status); if (response.status == CreateUserStatus::FAILURE) { propValue->value.stringValue = response.errorMessage; } return propValue; } VehiclePropValuePool::RecyclableType toVehiclePropValue( VehiclePropValuePool& pool, const UserIdentificationResponse& response) { int32_t userIdAssocProp = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION); auto propValue = pool.obtain(getPropType(userIdAssocProp)); propValue->prop = userIdAssocProp; propValue->timestamp = elapsedRealtimeNano(); propValue->value.int32Values.resize(2 + (response.numberAssociation * 2)); propValue->value.int32Values[0] = static_cast(response.requestId); propValue->value.int32Values[1] = static_cast(response.numberAssociation); for (size_t i = 0; i < static_cast(response.numberAssociation); ++i) { size_t int32ValuesPos = 2 + (2 * i); propValue->value.int32Values[int32ValuesPos] = static_cast(response.associations[i].type); propValue->value.int32Values[int32ValuesPos + 1] = static_cast(response.associations[i].value); } if (!response.errorMessage.empty()) { propValue->value.stringValue = response.errorMessage; } return propValue; } } // namespace user_hal_helper } // namespace fake } // namespace vehicle } // namespace automotive } // namespace hardware } // namespace android