blob: 4fd93ae24b2d81b97eb04d7362d67ac468f911f4 [file] [log] [blame]
/*
* 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 <VehicleUtils.h>
#include <log/log.h>
#include <utils/SystemClock.h>
#include <vector>
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<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 std::vector<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];
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<void> parseUsersInfo(const std::vector<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 std::vector<int32_t>& int32Values, size_t startPos, size_t numberAssociationTypes,
std::vector<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 std::vector<int32_t>& int32Values, size_t startPos,
size_t numberAssociations,
std::vector<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
template <typename T>
Result<T> verifyAndCast(int32_t value) {
T castValue = static_cast<T>(value);
for (const auto& v : ::ndk::enum_range<T>()) {
if (castValue == v) {
return castValue;
}
}
return Error() << "Value " << value << " not in enum values";
}
Result<InitialUserInfoRequest> 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<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, /*minInt32Values=*/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)
<< " 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<CreateUserRequest> 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<RemoveUserRequest> 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<UserIdentificationGetRequest> 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<UserIdentificationSetRequest> 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<VehiclePropValuePool::RecyclableType> 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<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;
}
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<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] = 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<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;
}
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<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;
}
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<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 fake
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android