Lakshman Annadorai | b33e2ab | 2020-07-27 10:42:32 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2020 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | #define LOG_TAG "UserHalHelper" |
| 17 | |
| 18 | #include "UserHalHelper.h" |
| 19 | |
| 20 | #include <log/log.h> |
| 21 | #include <utils/SystemClock.h> |
| 22 | |
| 23 | namespace android { |
| 24 | namespace hardware { |
| 25 | namespace automotive { |
| 26 | namespace vehicle { |
| 27 | namespace V2_0 { |
| 28 | |
| 29 | namespace user_hal_helper { |
| 30 | namespace { |
| 31 | |
| 32 | using android::base::Error; |
| 33 | using android::base::Result; |
| 34 | |
| 35 | static constexpr const char* kSeparator = "||"; |
| 36 | static const size_t kNumFieldsPerUserInfo = 2; |
| 37 | static const size_t kNumFieldsPerSetAssociation = 2; |
| 38 | |
Lakshman Annadorai | b33e2ab | 2020-07-27 10:42:32 -0700 | [diff] [blame] | 39 | Result<void> verifyPropValue(const VehiclePropValue& propValue, VehicleProperty vehicleProperty, |
| 40 | size_t minInt32Values) { |
| 41 | auto prop = verifyAndCast<VehicleProperty>(propValue.prop); |
| 42 | if (!prop.ok()) { |
| 43 | return Error() << "Invalid vehicle property: " << prop.error(); |
| 44 | } |
| 45 | if (*prop != vehicleProperty) { |
| 46 | return Error() << "Mismatching " << toString(vehicleProperty) << " request, received " |
| 47 | << toString(*prop) << " property"; |
| 48 | } |
| 49 | if (propValue.value.int32Values.size() < minInt32Values) { |
| 50 | return Error() << "Int32Values must have at least " << minInt32Values |
| 51 | << " values, received " << propValue.value.int32Values.size(); |
| 52 | } |
| 53 | return {}; |
| 54 | } |
| 55 | |
| 56 | Result<void> parseUserInfo(const hidl_vec<int32_t>& int32Values, size_t startPos, |
| 57 | UserInfo* userInfo) { |
| 58 | if (int32Values.size() < startPos + kNumFieldsPerUserInfo) { |
| 59 | return Error() << "Int32Values must have at least " << startPos + 2 << " values, received " |
| 60 | << int32Values.size(); |
| 61 | } |
| 62 | userInfo->userId = int32Values[startPos]; |
Yu Shan | 5c0ec3f | 2021-10-20 14:01:42 -0700 | [diff] [blame^] | 63 | int32_t intUserFlags = int32Values[startPos + 1]; |
| 64 | int32_t expectedUserFlags = 0; |
| 65 | for (const auto& v : hidl_enum_range<UserFlags>()) { |
| 66 | int32_t intEnumUserFlag = static_cast<int32_t>(v); |
| 67 | if ((intUserFlags & intEnumUserFlag) != 0) { |
| 68 | expectedUserFlags |= intEnumUserFlag; |
| 69 | } |
Lakshman Annadorai | b33e2ab | 2020-07-27 10:42:32 -0700 | [diff] [blame] | 70 | } |
Yu Shan | 5c0ec3f | 2021-10-20 14:01:42 -0700 | [diff] [blame^] | 71 | if (intUserFlags != expectedUserFlags) { |
| 72 | return Error() << "Invalid user flags: " << intUserFlags << ", must be '|' of UserFlags"; |
| 73 | } |
| 74 | // intUserFlags is actually not a valid UserFlags enum, instead, it is a 'bit or' of possible |
| 75 | // multiple UserFlags. However, because the HAL interface was defined incorrectly, we have to |
| 76 | // cast it to UserFlags here, which is defined behavior because the underlying type for |
| 77 | // UserFlags is int32_t and our intUserFlags is within the range of int32_t. |
| 78 | userInfo->flags = static_cast<UserFlags>(intUserFlags); |
Lakshman Annadorai | b33e2ab | 2020-07-27 10:42:32 -0700 | [diff] [blame] | 79 | return {}; |
| 80 | } |
| 81 | |
| 82 | Result<void> parseUsersInfo(const hidl_vec<int32_t>& int32Values, size_t startPos, |
| 83 | UsersInfo* usersInfo) { |
| 84 | if (int32Values.size() < startPos + 3) { |
| 85 | return Error() << "Int32Values must have at least " << startPos + 3 << " values, received " |
| 86 | << int32Values.size(); |
| 87 | } |
| 88 | auto ret = parseUserInfo(int32Values, startPos, &usersInfo->currentUser); |
| 89 | if (!ret.ok()) { |
| 90 | return ret; |
| 91 | } |
| 92 | usersInfo->numberUsers = int32Values[startPos + 2]; |
| 93 | usersInfo->existingUsers.resize(usersInfo->numberUsers); |
| 94 | for (size_t i = 0; i < static_cast<size_t>(usersInfo->numberUsers); ++i) { |
| 95 | ret = parseUserInfo(int32Values, startPos + 3 + (kNumFieldsPerUserInfo * i), |
| 96 | &usersInfo->existingUsers[i]); |
| 97 | if (!ret.ok()) { |
| 98 | return Error() << "Failed to parse existing user '" << i << "' info: " << ret.error(); |
| 99 | } |
| 100 | } |
| 101 | return {}; |
| 102 | } |
| 103 | |
| 104 | Result<void> parseUserAssociationTypes( |
| 105 | const hidl_vec<int32_t>& int32Values, size_t startPos, size_t numberAssociationTypes, |
| 106 | hidl_vec<UserIdentificationAssociationType>* associationTypes) { |
| 107 | size_t minInt32Values = startPos + numberAssociationTypes; |
| 108 | if (int32Values.size() < minInt32Values) { |
| 109 | return Error() << "Int32Values must have at least " << minInt32Values |
| 110 | << " values, received " << int32Values.size(); |
| 111 | } |
| 112 | associationTypes->resize(numberAssociationTypes); |
| 113 | for (size_t i = 0; i < static_cast<size_t>(numberAssociationTypes); ++i) { |
| 114 | size_t pos = startPos + i; |
| 115 | auto type = verifyAndCast<UserIdentificationAssociationType>(int32Values[pos]); |
| 116 | if (!type.ok()) { |
| 117 | return Error() << "Invalid association type in query '" << i << "': " << type.error(); |
| 118 | } |
| 119 | (*associationTypes)[i] = *type; |
| 120 | } |
| 121 | return {}; |
| 122 | } |
| 123 | |
| 124 | Result<void> parseUserAssociations(const hidl_vec<int32_t>& int32Values, size_t startPos, |
| 125 | size_t numberAssociations, |
| 126 | hidl_vec<UserIdentificationSetAssociation>* associations) { |
| 127 | size_t minInt32Values = startPos + (numberAssociations * kNumFieldsPerSetAssociation); |
| 128 | if (int32Values.size() < minInt32Values) { |
| 129 | return Error() << "Int32Values must have at least " << minInt32Values |
| 130 | << " values, received " << int32Values.size(); |
| 131 | } |
| 132 | associations->resize(numberAssociations); |
| 133 | for (size_t i = 0; i < static_cast<size_t>(numberAssociations); ++i) { |
| 134 | size_t pos = startPos + (kNumFieldsPerSetAssociation * i); |
| 135 | auto type = verifyAndCast<UserIdentificationAssociationType>(int32Values[pos]); |
| 136 | if (!type.ok()) { |
| 137 | return Error() << "Invalid association type in request '" << i << "': " << type.error(); |
| 138 | } |
| 139 | (*associations)[i].type = *type; |
| 140 | auto value = verifyAndCast<UserIdentificationAssociationSetValue>(int32Values[pos + 1]); |
| 141 | if (!value.ok()) { |
| 142 | return Error() << "Invalid association set value in request '" << i |
| 143 | << "': " << value.error(); |
| 144 | } |
| 145 | (*associations)[i].value = *value; |
| 146 | } |
| 147 | return {}; |
| 148 | } |
| 149 | |
| 150 | } // namespace |
| 151 | |
Lakshman Annadorai | bfdb1ac | 2020-08-06 14:25:03 -0700 | [diff] [blame] | 152 | template <typename T> |
| 153 | Result<T> verifyAndCast(int32_t value) { |
| 154 | T castValue = static_cast<T>(value); |
Lakshman Annadorai | bfdb1ac | 2020-08-06 14:25:03 -0700 | [diff] [blame] | 155 | for (const auto& v : hidl_enum_range<T>()) { |
| 156 | if (castValue == v) { |
| 157 | return castValue; |
| 158 | } |
| 159 | } |
| 160 | return Error() << "Value " << value << " not in enum values"; |
| 161 | } |
| 162 | |
Lakshman Annadorai | b33e2ab | 2020-07-27 10:42:32 -0700 | [diff] [blame] | 163 | Result<InitialUserInfoRequest> toInitialUserInfoRequest(const VehiclePropValue& propValue) { |
| 164 | auto ret = verifyPropValue(propValue, VehicleProperty::INITIAL_USER_INFO, 2); |
| 165 | if (!ret.ok()) { |
| 166 | return ret.error(); |
| 167 | } |
| 168 | InitialUserInfoRequest request; |
| 169 | request.requestId = propValue.value.int32Values[0]; |
| 170 | auto requestType = verifyAndCast<InitialUserInfoRequestType>(propValue.value.int32Values[1]); |
| 171 | if (!requestType.ok()) { |
| 172 | return Error() << "Invalid InitialUserInfoRequestType: " << requestType.error(); |
| 173 | } |
| 174 | request.requestType = *requestType; |
| 175 | ret = parseUsersInfo(propValue.value.int32Values, 2, &request.usersInfo); |
| 176 | if (!ret.ok()) { |
| 177 | return Error() << "Failed to parse users info: " << ret.error(); |
| 178 | } |
| 179 | return request; |
| 180 | } |
| 181 | |
| 182 | Result<SwitchUserRequest> toSwitchUserRequest(const VehiclePropValue& propValue) { |
| 183 | auto ret = verifyPropValue(propValue, VehicleProperty::SWITCH_USER, 2); |
| 184 | if (!ret.ok()) { |
| 185 | return ret.error(); |
| 186 | } |
| 187 | SwitchUserRequest request; |
| 188 | auto messageType = verifyAndCast<SwitchUserMessageType>(propValue.value.int32Values[1]); |
| 189 | if (!messageType.ok()) { |
| 190 | return Error() << "Invalid SwitchUserMessageType: " << messageType.error(); |
| 191 | } |
| 192 | if (*messageType != SwitchUserMessageType::LEGACY_ANDROID_SWITCH && |
| 193 | *messageType != SwitchUserMessageType::ANDROID_SWITCH && |
| 194 | *messageType != SwitchUserMessageType::ANDROID_POST_SWITCH) { |
Lakshman Annadorai | bfdb1ac | 2020-08-06 14:25:03 -0700 | [diff] [blame] | 195 | return Error() << "Invalid " << toString(*messageType) |
| 196 | << " message type from Android System"; |
Lakshman Annadorai | b33e2ab | 2020-07-27 10:42:32 -0700 | [diff] [blame] | 197 | } |
| 198 | request.requestId = propValue.value.int32Values[0]; |
| 199 | request.messageType = *messageType; |
| 200 | ret = parseUserInfo(propValue.value.int32Values, 2, &request.targetUser); |
| 201 | if (!ret.ok()) { |
| 202 | return Error() << "Failed to parse target user info: " << ret.error(); |
| 203 | } |
| 204 | ret = parseUsersInfo(propValue.value.int32Values, 4, &request.usersInfo); |
| 205 | if (!ret.ok()) { |
| 206 | return Error() << "Failed to parse users info: " << ret.error(); |
| 207 | } |
| 208 | return request; |
| 209 | } |
| 210 | |
| 211 | Result<CreateUserRequest> toCreateUserRequest(const VehiclePropValue& propValue) { |
| 212 | auto ret = verifyPropValue(propValue, VehicleProperty::CREATE_USER, 1); |
| 213 | if (!ret.ok()) { |
| 214 | return ret.error(); |
| 215 | } |
| 216 | CreateUserRequest request; |
| 217 | request.requestId = propValue.value.int32Values[0]; |
| 218 | ret = parseUserInfo(propValue.value.int32Values, 1, &request.newUserInfo); |
| 219 | if (!ret.ok()) { |
| 220 | return Error() << "Failed to parse new user info: " << ret.error(); |
| 221 | } |
| 222 | request.newUserName = propValue.value.stringValue; |
| 223 | ret = parseUsersInfo(propValue.value.int32Values, 3, &request.usersInfo); |
| 224 | if (!ret.ok()) { |
| 225 | return Error() << "Failed to parse users info: " << ret.error(); |
| 226 | } |
| 227 | return request; |
| 228 | } |
| 229 | |
| 230 | Result<RemoveUserRequest> toRemoveUserRequest(const VehiclePropValue& propValue) { |
| 231 | auto ret = verifyPropValue(propValue, VehicleProperty::REMOVE_USER, 1); |
| 232 | if (!ret.ok()) { |
| 233 | return ret.error(); |
| 234 | } |
| 235 | RemoveUserRequest request; |
| 236 | request.requestId = propValue.value.int32Values[0]; |
| 237 | ret = parseUserInfo(propValue.value.int32Values, 1, &request.removedUserInfo); |
| 238 | if (!ret.ok()) { |
| 239 | return Error() << "Failed to parse removed user info: " << ret.error(); |
| 240 | } |
| 241 | ret = parseUsersInfo(propValue.value.int32Values, 3, &request.usersInfo); |
| 242 | if (!ret.ok()) { |
| 243 | return Error() << "Failed to parse users info: " << ret.error(); |
| 244 | } |
| 245 | return request; |
| 246 | } |
| 247 | |
| 248 | Result<UserIdentificationGetRequest> toUserIdentificationGetRequest( |
| 249 | const VehiclePropValue& propValue) { |
| 250 | auto ret = verifyPropValue(propValue, VehicleProperty::USER_IDENTIFICATION_ASSOCIATION, 4); |
| 251 | if (!ret.ok()) { |
| 252 | return ret.error(); |
| 253 | } |
| 254 | UserIdentificationGetRequest request; |
| 255 | request.requestId = propValue.value.int32Values[0]; |
| 256 | ret = parseUserInfo(propValue.value.int32Values, 1, &request.userInfo); |
| 257 | if (!ret.ok()) { |
| 258 | return Error() << "Failed to parse user info: " << ret.error(); |
| 259 | } |
| 260 | request.numberAssociationTypes = propValue.value.int32Values[3]; |
| 261 | ret = parseUserAssociationTypes(propValue.value.int32Values, 4, request.numberAssociationTypes, |
| 262 | &request.associationTypes); |
| 263 | if (!ret.ok()) { |
| 264 | return Error() << "Failed to parse UserIdentificationAssociationType: " << ret.error(); |
| 265 | } |
| 266 | return request; |
| 267 | } |
| 268 | |
| 269 | Result<UserIdentificationSetRequest> toUserIdentificationSetRequest( |
| 270 | const VehiclePropValue& propValue) { |
| 271 | auto ret = verifyPropValue(propValue, VehicleProperty::USER_IDENTIFICATION_ASSOCIATION, 4); |
| 272 | if (!ret.ok()) { |
| 273 | return ret.error(); |
| 274 | } |
| 275 | UserIdentificationSetRequest request; |
| 276 | request.requestId = propValue.value.int32Values[0]; |
| 277 | ret = parseUserInfo(propValue.value.int32Values, 1, &request.userInfo); |
| 278 | if (!ret.ok()) { |
| 279 | return Error() << "Failed to parse user info: " << ret.error(); |
| 280 | } |
| 281 | request.numberAssociations = propValue.value.int32Values[3]; |
| 282 | ret = parseUserAssociations(propValue.value.int32Values, 4, request.numberAssociations, |
| 283 | &request.associations); |
| 284 | if (!ret.ok()) { |
| 285 | return Error() << "Failed to parse UserIdentificationSetAssociation: " << ret.error(); |
| 286 | } |
| 287 | return request; |
| 288 | } |
| 289 | |
| 290 | std::unique_ptr<VehiclePropValue> toVehiclePropValue(const SwitchUserRequest& request) { |
| 291 | if (request.messageType != SwitchUserMessageType::VEHICLE_REQUEST) { |
| 292 | ALOGE("Invalid %s message type %s from HAL", toString(VehicleProperty::SWITCH_USER).c_str(), |
| 293 | toString(request.messageType).c_str()); |
| 294 | return nullptr; |
| 295 | } |
| 296 | auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue()); |
| 297 | propValue->prop = static_cast<int32_t>(VehicleProperty::SWITCH_USER); |
| 298 | propValue->timestamp = elapsedRealtimeNano(); |
| 299 | propValue->value.int32Values.resize(3); |
| 300 | propValue->value.int32Values[0] = static_cast<int32_t>(request.requestId); |
| 301 | propValue->value.int32Values[1] = static_cast<int32_t>(request.messageType); |
| 302 | propValue->value.int32Values[2] = static_cast<int32_t>(request.targetUser.userId); |
| 303 | return propValue; |
| 304 | } |
| 305 | |
| 306 | std::unique_ptr<VehiclePropValue> toVehiclePropValue(const InitialUserInfoResponse& response) { |
| 307 | auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue()); |
| 308 | propValue->prop = static_cast<int32_t>(VehicleProperty::INITIAL_USER_INFO); |
| 309 | propValue->timestamp = elapsedRealtimeNano(); |
| 310 | propValue->value.int32Values.resize(4); |
| 311 | propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId); |
| 312 | propValue->value.int32Values[1] = static_cast<int32_t>(response.action); |
| 313 | propValue->value.int32Values[2] = static_cast<int32_t>(response.userToSwitchOrCreate.userId); |
| 314 | propValue->value.int32Values[3] = static_cast<int32_t>(response.userToSwitchOrCreate.flags); |
| 315 | propValue->value.stringValue = std::string(response.userLocales) + std::string(kSeparator) + |
| 316 | std::string(response.userNameToCreate); |
| 317 | return propValue; |
| 318 | } |
| 319 | |
| 320 | std::unique_ptr<VehiclePropValue> toVehiclePropValue(const SwitchUserResponse& response) { |
| 321 | auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue()); |
| 322 | propValue->prop = static_cast<int32_t>(VehicleProperty::SWITCH_USER); |
| 323 | propValue->timestamp = elapsedRealtimeNano(); |
| 324 | propValue->value.int32Values.resize(3); |
| 325 | propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId); |
| 326 | propValue->value.int32Values[1] = static_cast<int32_t>(response.messageType); |
| 327 | propValue->value.int32Values[2] = static_cast<int32_t>(response.status); |
| 328 | if (response.status == SwitchUserStatus::FAILURE) { |
| 329 | propValue->value.stringValue = response.errorMessage; |
| 330 | } |
| 331 | return propValue; |
| 332 | } |
| 333 | |
| 334 | std::unique_ptr<VehiclePropValue> toVehiclePropValue(const CreateUserResponse& response) { |
| 335 | auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue()); |
| 336 | propValue->prop = static_cast<int32_t>(VehicleProperty::CREATE_USER); |
| 337 | propValue->timestamp = elapsedRealtimeNano(); |
| 338 | propValue->value.int32Values.resize(2); |
| 339 | propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId); |
| 340 | propValue->value.int32Values[1] = static_cast<int32_t>(response.status); |
| 341 | if (response.status == CreateUserStatus::FAILURE) { |
| 342 | propValue->value.stringValue = response.errorMessage; |
| 343 | } |
| 344 | return propValue; |
| 345 | } |
| 346 | |
| 347 | std::unique_ptr<VehiclePropValue> toVehiclePropValue(const UserIdentificationResponse& response) { |
| 348 | auto propValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue()); |
| 349 | propValue->prop = static_cast<int32_t>(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION); |
| 350 | propValue->timestamp = elapsedRealtimeNano(); |
| 351 | propValue->value.int32Values.resize(2 + (response.numberAssociation * 2)); |
| 352 | propValue->value.int32Values[0] = static_cast<int32_t>(response.requestId); |
| 353 | propValue->value.int32Values[1] = static_cast<int32_t>(response.numberAssociation); |
| 354 | for (size_t i = 0; i < static_cast<size_t>(response.numberAssociation); ++i) { |
| 355 | size_t int32ValuesPos = 2 + (2 * i); |
| 356 | propValue->value.int32Values[int32ValuesPos] = |
| 357 | static_cast<int32_t>(response.associations[i].type); |
| 358 | propValue->value.int32Values[int32ValuesPos + 1] = |
| 359 | static_cast<int32_t>(response.associations[i].value); |
| 360 | } |
| 361 | if (!response.errorMessage.empty()) { |
| 362 | propValue->value.stringValue = response.errorMessage; |
| 363 | } |
| 364 | return propValue; |
| 365 | } |
| 366 | |
| 367 | } // namespace user_hal_helper |
| 368 | |
| 369 | } // namespace V2_0 |
| 370 | } // namespace vehicle |
| 371 | } // namespace automotive |
| 372 | } // namespace hardware |
| 373 | } // namespace android |