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 package com.android.car.hal; 17 18 import static com.android.car.internal.common.CommonConstants.EMPTY_INT_ARRAY; 19 import static com.android.car.CarServiceUtils.toIntArray; 20 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.PRIVATE_CONSTRUCTOR; 21 import static com.android.internal.util.Preconditions.checkArgument; 22 23 import android.annotation.UserIdInt; 24 import android.app.ActivityManager; 25 import android.car.builtin.os.UserManagerHelper; 26 import android.car.builtin.util.Slogf; 27 import android.hardware.automotive.vehicle.CreateUserRequest; 28 import android.hardware.automotive.vehicle.InitialUserInfoRequestType; 29 import android.hardware.automotive.vehicle.InitialUserInfoResponse; 30 import android.hardware.automotive.vehicle.InitialUserInfoResponseAction; 31 import android.hardware.automotive.vehicle.RemoveUserRequest; 32 import android.hardware.automotive.vehicle.SwitchUserRequest; 33 import android.hardware.automotive.vehicle.UserIdentificationAssociation; 34 import android.hardware.automotive.vehicle.UserIdentificationAssociationSetValue; 35 import android.hardware.automotive.vehicle.UserIdentificationAssociationType; 36 import android.hardware.automotive.vehicle.UserIdentificationAssociationValue; 37 import android.hardware.automotive.vehicle.UserIdentificationGetRequest; 38 import android.hardware.automotive.vehicle.UserIdentificationResponse; 39 import android.hardware.automotive.vehicle.UserIdentificationSetAssociation; 40 import android.hardware.automotive.vehicle.UserIdentificationSetRequest; 41 import android.hardware.automotive.vehicle.UserInfo; 42 import android.hardware.automotive.vehicle.UsersInfo; 43 import android.hardware.automotive.vehicle.VehiclePropertyStatus; 44 import android.os.SystemClock; 45 import android.os.UserHandle; 46 import android.os.UserManager; 47 import android.text.TextUtils; 48 49 import com.android.car.hal.HalCallback.HalCallbackStatus; 50 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 51 import com.android.car.internal.util.DebugUtils; 52 import com.android.car.user.UserHandleHelper; 53 import com.android.internal.annotations.VisibleForTesting; 54 import com.android.internal.util.Preconditions; 55 56 import java.util.ArrayList; 57 import java.util.Arrays; 58 import java.util.List; 59 import java.util.Objects; 60 61 /** 62 * Provides utility methods for User HAL related functionalities. 63 */ 64 public final class UserHalHelper { 65 66 @VisibleForTesting 67 static final String TAG = UserHalHelper.class.getSimpleName(); 68 69 public static final int INITIAL_USER_INFO_PROPERTY = 299896583; 70 public static final int SWITCH_USER_PROPERTY = 299896584; 71 public static final int CREATE_USER_PROPERTY = 299896585; 72 public static final int REMOVE_USER_PROPERTY = 299896586; 73 public static final int USER_IDENTIFICATION_ASSOCIATION_PROPERTY = 299896587; 74 75 private static final boolean DEBUG = false; 76 private static final String STRING_SEPARATOR = "\\|\\|"; 77 78 /** 79 * Gets user-friendly representation of the status. 80 */ halCallbackStatusToString(@alCallbackStatus int status)81 public static String halCallbackStatusToString(@HalCallbackStatus int status) { 82 switch (status) { 83 case HalCallback.STATUS_OK: 84 return "OK"; 85 case HalCallback.STATUS_HAL_SET_TIMEOUT: 86 return "HAL_SET_TIMEOUT"; 87 case HalCallback.STATUS_HAL_RESPONSE_TIMEOUT: 88 return "HAL_RESPONSE_TIMEOUT"; 89 case HalCallback.STATUS_WRONG_HAL_RESPONSE: 90 return "WRONG_HAL_RESPONSE"; 91 case HalCallback.STATUS_CONCURRENT_OPERATION: 92 return "CONCURRENT_OPERATION"; 93 default: 94 return "UNKNOWN-" + status; 95 } 96 } 97 98 /** 99 * Converts a string to a {@link InitialUserInfoRequestType}. 100 * 101 * @return valid type or numeric value if passed "as is" 102 * 103 * @throws IllegalArgumentException if type is not valid neither a number 104 */ parseInitialUserInfoRequestType(String type)105 public static int parseInitialUserInfoRequestType(String type) { 106 switch(type) { 107 case "FIRST_BOOT": 108 return InitialUserInfoRequestType.FIRST_BOOT; 109 case "FIRST_BOOT_AFTER_OTA": 110 return InitialUserInfoRequestType.FIRST_BOOT_AFTER_OTA; 111 case "COLD_BOOT": 112 return InitialUserInfoRequestType.COLD_BOOT; 113 case "RESUME": 114 return InitialUserInfoRequestType.RESUME; 115 default: 116 try { 117 return Integer.parseInt(type); 118 } catch (NumberFormatException e) { 119 throw new IllegalArgumentException("invalid type: " + type, e); 120 } 121 } 122 } 123 124 /** 125 * Converts Android user flags to HALs. 126 */ convertFlags(UserHandleHelper userHandleHelper, UserHandle user)127 public static int convertFlags(UserHandleHelper userHandleHelper, UserHandle user) { 128 checkArgument(user != null, "user cannot be null"); 129 130 int flags = 0; 131 if (user.getIdentifier() == UserHandle.SYSTEM.getIdentifier()) { 132 flags |= UserInfo.USER_FLAG_SYSTEM; 133 } 134 if (userHandleHelper.isAdminUser(user)) { 135 flags |= UserInfo.USER_FLAG_ADMIN; 136 } 137 if (userHandleHelper.isGuestUser(user)) { 138 flags |= UserInfo.USER_FLAG_GUEST; 139 } 140 if (userHandleHelper.isEphemeralUser(user)) { 141 flags |= UserInfo.USER_FLAG_EPHEMERAL; 142 } 143 if (!userHandleHelper.isEnabledUser(user)) { 144 flags |= UserInfo.USER_FLAG_DISABLED; 145 } 146 if (userHandleHelper.isProfileUser(user)) { 147 flags |= UserInfo.USER_FLAG_PROFILE; 148 } 149 150 return flags; 151 } 152 153 154 /** 155 * Converts Android user flags to HALs. 156 */ getFlags(UserHandleHelper userHandleHelper, @UserIdInt int userId)157 public static int getFlags(UserHandleHelper userHandleHelper, @UserIdInt int userId) { 158 Preconditions.checkArgument(userHandleHelper != null, "UserManager cannot be null"); 159 UserHandle user = userHandleHelper.getExistingUserHandle(userId); 160 Preconditions.checkArgument(user != null, "No user with id %d", userId); 161 return convertFlags(userHandleHelper, user); 162 } 163 164 /** Checks if a HAL flag contains {@link UserInfo#USER_FLAG_SYSTEM}. */ isSystem(int flags)165 public static boolean isSystem(int flags) { 166 return (flags & UserInfo.USER_FLAG_SYSTEM) != 0; 167 } 168 169 /** Checks if a HAL flag contains {@link UserInfo#USER_FLAG_GUEST}. */ isGuest(int flags)170 public static boolean isGuest(int flags) { 171 return (flags & UserInfo.USER_FLAG_GUEST) != 0; 172 } 173 174 /** Checks if a HAL flag contains {@link UserInfo#USER_FLAG_EPHEMERAL}. */ isEphemeral(int flags)175 public static boolean isEphemeral(int flags) { 176 return (flags & UserInfo.USER_FLAG_EPHEMERAL) != 0; 177 } 178 179 /** Checks if a HAL flag contains {@link UserInfo#USER_FLAG_ADMIN}. */ isAdmin(int flags)180 public static boolean isAdmin(int flags) { 181 return (flags & UserInfo.USER_FLAG_ADMIN) != 0; 182 } 183 184 /** Checks if a HAL flag contains {@link UserInfo#USER_FLAG_DISABLED}. */ isDisabled(int flags)185 public static boolean isDisabled(int flags) { 186 return (flags & UserInfo.USER_FLAG_DISABLED) != 0; 187 } 188 189 /** Checks if a HAL flag contains {@link UserInfo#USER_FLAG_PROFILE}. */ isProfile(int flags)190 public static boolean isProfile(int flags) { 191 return (flags & UserInfo.USER_FLAG_PROFILE) != 0; 192 } 193 194 /** 195 * Converts HAL flags to Android's. 196 */ toUserInfoFlags(int halFlags)197 public static int toUserInfoFlags(int halFlags) { 198 int flags = 0; 199 if (isEphemeral(halFlags)) { 200 flags |= UserManagerHelper.FLAG_EPHEMERAL; 201 } 202 if (isAdmin(halFlags)) { 203 flags |= UserManagerHelper.FLAG_ADMIN; 204 } 205 return flags; 206 } 207 208 /** 209 * Gets a user-friendly representation of the user flags. 210 */ userFlagsToString(int flags)211 public static String userFlagsToString(int flags) { 212 return DebugUtils.flagsToString(UserInfo.class, /* prefix= */ "", flags); 213 } 214 215 /** 216 * Adds users information to the property's integer values. 217 * 218 * <p><b>NOTE: </b>it does not validate the semantics of {@link UsersInfo} content (for example, 219 * if the current user is present in the list of users or if the flags are valid), only the 220 * basic correctness (like number of users matching existing users list size). Use 221 * {@link #checkValid(UsersInfo)} for a full check. 222 */ addUsersInfo(List<Integer> intValues, UsersInfo usersInfo)223 public static void addUsersInfo(List<Integer> intValues, UsersInfo usersInfo) { 224 Objects.requireNonNull(usersInfo.currentUser, "Current user cannot be null"); 225 checkArgument(usersInfo.numberUsers == usersInfo.existingUsers.length, 226 "Number of existing users info does not match numberUsers, got %d, want %d", 227 usersInfo.numberUsers, usersInfo.existingUsers.length); 228 229 addUserInfo(intValues, usersInfo.currentUser); 230 intValues.add(usersInfo.numberUsers); 231 for (int i = 0; i < usersInfo.numberUsers; i++) { 232 UserInfo userInfo = usersInfo.existingUsers[i]; 233 addUserInfo(intValues, userInfo); 234 } 235 } 236 237 /** Adds user information to the property's integer values. */ addUserInfo(List<Integer> intValues, UserInfo userInfo)238 public static void addUserInfo(List<Integer> intValues, UserInfo userInfo) { 239 Objects.requireNonNull(userInfo, "UserInfo cannot be null"); 240 241 intValues.add(userInfo.userId); 242 intValues.add(userInfo.flags); 243 } 244 245 /** 246 * Checks if the given {@code value} is a valid {@link UserIdentificationAssociationType}. 247 */ isValidUserIdentificationAssociationType(int type)248 public static boolean isValidUserIdentificationAssociationType(int type) { 249 switch(type) { 250 case UserIdentificationAssociationType.KEY_FOB: 251 case UserIdentificationAssociationType.CUSTOM_1: 252 case UserIdentificationAssociationType.CUSTOM_2: 253 case UserIdentificationAssociationType.CUSTOM_3: 254 case UserIdentificationAssociationType.CUSTOM_4: 255 return true; 256 default: 257 break; 258 } 259 return false; 260 } 261 262 /** 263 * Checks if the given {@code value} is a valid {@link UserIdentificationAssociationValue}. 264 */ isValidUserIdentificationAssociationValue(int value)265 public static boolean isValidUserIdentificationAssociationValue(int value) { 266 switch(value) { 267 case UserIdentificationAssociationValue.ASSOCIATED_ANOTHER_USER: 268 case UserIdentificationAssociationValue.ASSOCIATED_CURRENT_USER: 269 case UserIdentificationAssociationValue.NOT_ASSOCIATED_ANY_USER: 270 case UserIdentificationAssociationValue.UNKNOWN: 271 return true; 272 default: 273 break; 274 } 275 return false; 276 } 277 278 /** 279 * Checks if the given {@code value} is a valid {@link UserIdentificationAssociationSetValue}. 280 */ isValidUserIdentificationAssociationSetValue(int value)281 public static boolean isValidUserIdentificationAssociationSetValue(int value) { 282 switch(value) { 283 case UserIdentificationAssociationSetValue.ASSOCIATE_CURRENT_USER: 284 case UserIdentificationAssociationSetValue.DISASSOCIATE_CURRENT_USER: 285 case UserIdentificationAssociationSetValue.DISASSOCIATE_ALL_USERS: 286 return true; 287 default: 288 break; 289 } 290 return false; 291 } 292 293 /** 294 * Creates a {@link UserIdentificationResponse} from a generic {@link HalPropValue} sent by 295 * HAL. 296 * 297 * @throws IllegalArgumentException if the HAL property doesn't have the proper format. 298 */ toUserIdentificationResponse(HalPropValue prop)299 public static UserIdentificationResponse toUserIdentificationResponse(HalPropValue prop) { 300 Objects.requireNonNull(prop, "prop cannot be null"); 301 checkArgument(prop.getPropId() == USER_IDENTIFICATION_ASSOCIATION_PROPERTY, 302 "invalid prop on %s", prop); 303 // need at least 4: request_id, number associations, type1, value1 304 assertMinimumSize(prop, 4); 305 306 int requestId = prop.getInt32Value(0); 307 checkArgument(requestId > 0, "invalid request id (%d) on %s", requestId, prop); 308 309 int numberAssociations = prop.getInt32Value(1); 310 checkArgument(numberAssociations >= 1, "invalid number of items on %s", prop); 311 int numberOfNonItems = 2; // requestId and size 312 int numberItems = prop.getInt32ValuesSize() - numberOfNonItems; 313 checkArgument(numberItems == numberAssociations * 2, "number of items mismatch on %s", 314 prop); 315 316 UserIdentificationResponse response = new UserIdentificationResponse(); 317 response.requestId = requestId; 318 response.errorMessage = prop.getStringValue(); 319 320 response.numberAssociation = numberAssociations; 321 int i = numberOfNonItems; 322 ArrayList<UserIdentificationAssociation> associations = new ArrayList<>(numberAssociations); 323 for (int a = 0; a < numberAssociations; a++) { 324 int index; 325 UserIdentificationAssociation association = new UserIdentificationAssociation(); 326 index = i++; 327 association.type = prop.getInt32Value(index); 328 checkArgument(isValidUserIdentificationAssociationType(association.type), 329 "invalid type at index %d on %s", index, prop); 330 index = i++; 331 association.value = prop.getInt32Value(index); 332 checkArgument(isValidUserIdentificationAssociationValue(association.value), 333 "invalid value at index %d on %s", index, prop); 334 associations.add(association); 335 } 336 337 response.associations = associations.toArray( 338 new UserIdentificationAssociation[associations.size()]); 339 340 return response; 341 } 342 343 /** 344 * Creates a {@link InitialUserInfoResponse} from a generic {@link HalPropValue} sent by 345 * HAL. 346 * 347 * @throws IllegalArgumentException if the HAL property doesn't have the proper format. 348 */ toInitialUserInfoResponse(HalPropValue prop)349 public static InitialUserInfoResponse toInitialUserInfoResponse(HalPropValue prop) { 350 if (DEBUG) Slogf.d(TAG, "toInitialUserInfoResponse(): %s", prop); 351 Objects.requireNonNull(prop, "prop cannot be null"); 352 checkArgument(prop.getPropId() == INITIAL_USER_INFO_PROPERTY, "invalid prop on %s", prop); 353 354 // need at least 2: request_id, action_type 355 assertMinimumSize(prop, 2); 356 357 int requestId = prop.getInt32Value(0); 358 checkArgument(requestId > 0, "invalid request id (%d) on %s", requestId, prop); 359 360 InitialUserInfoResponse response = new InitialUserInfoResponse(); 361 response.userToSwitchOrCreate = new UserInfo(); 362 response.userLocales = ""; 363 response.userNameToCreate = ""; 364 response.requestId = requestId; 365 response.action = prop.getInt32Value(1); 366 367 String[] stringValues = null; 368 if (!TextUtils.isEmpty(prop.getStringValue())) { 369 stringValues = TextUtils.split(prop.getStringValue(), STRING_SEPARATOR); 370 if (DEBUG) { 371 Slogf.d(TAG, "toInitialUserInfoResponse(): values=" + Arrays.toString(stringValues) 372 + " length: " + stringValues.length); 373 } 374 } 375 if (stringValues != null && stringValues.length > 0) { 376 response.userLocales = stringValues[0]; 377 } 378 379 switch (response.action) { 380 case InitialUserInfoResponseAction.DEFAULT: 381 response.userToSwitchOrCreate.userId = UserManagerHelper.USER_NULL; 382 break; 383 case InitialUserInfoResponseAction.SWITCH: 384 assertMinimumSize(prop, 3); // request_id, action_type, user_id 385 response.userToSwitchOrCreate.userId = prop.getInt32Value(2); 386 break; 387 case InitialUserInfoResponseAction.CREATE: 388 assertMinimumSize(prop, 4); // request_id, action_type, user_id, user_flags 389 // user id is set at index 2, but it's ignored 390 response.userToSwitchOrCreate.userId = UserManagerHelper.USER_NULL; 391 response.userToSwitchOrCreate.flags = prop.getInt32Value(3); 392 if (stringValues.length > 1) { 393 response.userNameToCreate = stringValues[1]; 394 } 395 break; 396 default: 397 throw new IllegalArgumentException("Invalid response action (" + response.action 398 + " on " + prop); 399 } 400 401 if (DEBUG) Slogf.d(TAG, "returning : " + response); 402 403 return response; 404 } 405 406 /** 407 * Creates a generic {@link HalPropValue} (that can be sent to HAL) from a 408 * {@link UserIdentificationGetRequest}. 409 * 410 * @throws IllegalArgumentException if the request doesn't have the proper format. 411 */ toHalPropValue(HalPropValueBuilder builder, UserIdentificationGetRequest request)412 public static HalPropValue toHalPropValue(HalPropValueBuilder builder, 413 UserIdentificationGetRequest request) { 414 Objects.requireNonNull(request, "request cannot be null"); 415 Objects.requireNonNull(request.associationTypes, "associationTypes must not be null"); 416 checkArgument(request.numberAssociationTypes > 0, 417 "invalid number of association types mismatch on %s", request); 418 checkArgument(request.numberAssociationTypes == request.associationTypes.length, 419 "number of association types mismatch on %s", request); 420 checkArgument(request.requestId > 0, "invalid requestId on %s", request); 421 422 List<Integer> intValues = new ArrayList<>(request.numberAssociationTypes + 2); 423 intValues.add(request.requestId); 424 addUserInfo(intValues, request.userInfo); 425 intValues.add(request.numberAssociationTypes); 426 427 for (int i = 0; i < request.numberAssociationTypes; i++) { 428 int type = request.associationTypes[i]; 429 checkArgument(isValidUserIdentificationAssociationType(type), 430 "invalid type at index %d on %s", i, request); 431 intValues.add(type); 432 } 433 434 HalPropValue propValue = builder.build(USER_IDENTIFICATION_ASSOCIATION_PROPERTY, 435 /* areaId= */ 0, SystemClock.elapsedRealtime(), 436 VehiclePropertyStatus.AVAILABLE, 437 toIntArray(intValues)); 438 439 return propValue; 440 } 441 442 /** 443 * Creates a generic {@link HalPropValue} (that can be sent to HAL) from a 444 * {@link UserIdentificationSetRequest}. 445 * 446 * @throws IllegalArgumentException if the request doesn't have the proper format. 447 */ toHalPropValue(HalPropValueBuilder builder, UserIdentificationSetRequest request)448 public static HalPropValue toHalPropValue(HalPropValueBuilder builder, 449 UserIdentificationSetRequest request) { 450 Objects.requireNonNull(request, "request cannot be null"); 451 Objects.requireNonNull(request.associations, "associations must not be null"); 452 checkArgument(request.numberAssociations > 0, 453 "invalid number of associations mismatch on %s", request); 454 checkArgument(request.numberAssociations == request.associations.length, 455 "number of associations mismatch on %s", request); 456 checkArgument(request.requestId > 0, "invalid requestId on %s", request); 457 458 List<Integer> intValues = new ArrayList<>(2); 459 intValues.add(request.requestId); 460 addUserInfo(intValues, request.userInfo); 461 intValues.add(request.numberAssociations); 462 463 for (int i = 0; i < request.numberAssociations; i++) { 464 UserIdentificationSetAssociation association = request.associations[i]; 465 checkArgument(isValidUserIdentificationAssociationType(association.type), 466 "invalid type at index %d on %s", i, request); 467 intValues.add(association.type); 468 checkArgument(isValidUserIdentificationAssociationSetValue(association.value), 469 "invalid value at index %d on %s", i, request); 470 intValues.add(association.value); 471 } 472 473 HalPropValue propValue = builder.build(USER_IDENTIFICATION_ASSOCIATION_PROPERTY, 474 /* areaId= */ 0, SystemClock.elapsedRealtime(), 475 VehiclePropertyStatus.AVAILABLE, 476 toIntArray(intValues)); 477 478 return propValue; 479 } 480 481 /** 482 * Creates a generic {@link HalPropValue} (that can be sent to HAL) from a 483 * {@link CreateUserRequest}. 484 * 485 * @throws IllegalArgumentException if the request doesn't have the proper format. 486 */ toHalPropValue(HalPropValueBuilder builder, CreateUserRequest request)487 public static HalPropValue toHalPropValue(HalPropValueBuilder builder, 488 CreateUserRequest request) { 489 Objects.requireNonNull(request, "request cannot be null"); 490 Objects.requireNonNull(request.newUserInfo, "NewUserInfo cannot be null"); 491 checkArgument(request.requestId > 0, "invalid requestId on %s", request); 492 checkValid(request.usersInfo); 493 checkArgument(request.newUserName != null, "newUserName cannot be null (should be empty " 494 + "instead) on %s", request); 495 496 boolean hasNewUser = false; 497 int newUserFlags = 0; 498 for (int i = 0; i < request.usersInfo.existingUsers.length; i++) { 499 UserInfo user = request.usersInfo.existingUsers[i]; 500 if (user.userId == request.newUserInfo.userId) { 501 hasNewUser = true; 502 newUserFlags = user.flags; 503 break; 504 } 505 } 506 Preconditions.checkArgument(hasNewUser, 507 "new user's id not present on existing users on request %s", request); 508 Preconditions.checkArgument(request.newUserInfo.flags == newUserFlags, 509 "new user flags mismatch on existing users on %s", request); 510 511 List<Integer> intValues = new ArrayList<>(2); 512 intValues.add(request.requestId); 513 addUserInfo(intValues, request.newUserInfo); 514 addUsersInfo(intValues, request.usersInfo); 515 516 HalPropValue propValue = builder.build(CREATE_USER_PROPERTY, /* areaId= */ 0, 517 SystemClock.elapsedRealtime(), VehiclePropertyStatus.AVAILABLE, 518 toIntArray(intValues), new float[0], new long[0], request.newUserName, new byte[0]); 519 520 return propValue; 521 } 522 523 /** 524 * Creates a generic {@link HalPropValue} (that can be sent to HAL) from a 525 * {@link SwitchUserRequest}. 526 * 527 * @throws IllegalArgumentException if the request doesn't have the proper format. 528 */ toHalPropValue(HalPropValueBuilder builder, SwitchUserRequest request)529 public static HalPropValue toHalPropValue(HalPropValueBuilder builder, 530 SwitchUserRequest request) { 531 Objects.requireNonNull(request, "request cannot be null"); 532 checkArgument(request.messageType > 0, "invalid messageType on %s", request); 533 UserInfo targetInfo = request.targetUser; 534 UsersInfo usersInfo = request.usersInfo; 535 Objects.requireNonNull(targetInfo); 536 checkValid(usersInfo); 537 538 List<Integer> intValues = new ArrayList<>(2); 539 intValues.add(request.requestId); 540 intValues.add(request.messageType); 541 542 addUserInfo(intValues, targetInfo); 543 addUsersInfo(intValues, usersInfo); 544 545 HalPropValue propValue = builder.build(SWITCH_USER_PROPERTY, /* areaId= */ 0, 546 SystemClock.elapsedRealtime(), VehiclePropertyStatus.AVAILABLE, 547 toIntArray(intValues)); 548 549 return propValue; 550 } 551 552 /** 553 * Creates a generic {@link HalPropValue} (that can be sent to HAL) from a 554 * {@link RemoveUserRequest}. 555 * 556 * @throws IllegalArgumentException if the request doesn't have the proper format. 557 */ toHalPropValue(HalPropValueBuilder builder, RemoveUserRequest request)558 public static HalPropValue toHalPropValue(HalPropValueBuilder builder, 559 RemoveUserRequest request) { 560 checkArgument(request.requestId > 0, "invalid requestId on %s", request); 561 UserInfo removedUserInfo = request.removedUserInfo; 562 Objects.requireNonNull(removedUserInfo); 563 UsersInfo usersInfo = request.usersInfo; 564 checkValid(usersInfo); 565 566 List<Integer> intValues = new ArrayList<>(1); 567 intValues.add(request.requestId); 568 569 addUserInfo(intValues, removedUserInfo); 570 addUsersInfo(intValues, usersInfo); 571 572 HalPropValue propValue = builder.build(REMOVE_USER_PROPERTY, /* areaId= */ 0, 573 SystemClock.elapsedRealtime(), VehiclePropertyStatus.AVAILABLE, 574 toIntArray(intValues)); 575 return propValue; 576 } 577 578 /** 579 * Creates a {@link UsersInfo} instance populated with the current users, using 580 * {@link ActivityManager#getCurrentUser()} as the current user. 581 */ newUsersInfo(UserManager um, UserHandleHelper userHandleHelper)582 public static UsersInfo newUsersInfo(UserManager um, UserHandleHelper userHandleHelper) { 583 return newUsersInfo(um, userHandleHelper, ActivityManager.getCurrentUser()); 584 } 585 586 /** 587 * Creates a {@link UsersInfo} instance populated with the current users, using 588 * {@code userId} as the current user. 589 */ newUsersInfo(UserManager um, UserHandleHelper userHandleHelper, @UserIdInt int userId)590 public static UsersInfo newUsersInfo(UserManager um, UserHandleHelper userHandleHelper, 591 @UserIdInt int userId) { 592 Preconditions.checkArgument(um != null, "UserManager cannot be null"); 593 Preconditions.checkArgument(userHandleHelper != null, "UserHandleHelper cannot be null"); 594 595 List<UserHandle> users = UserManagerHelper.getUserHandles(um, /* excludeDying= */ false); 596 597 if (users == null || users.isEmpty()) { 598 Slogf.w(TAG, "newUsersInfo(): no users"); 599 return emptyUsersInfo(); 600 } 601 602 UsersInfo usersInfo = emptyUsersInfo(); 603 usersInfo.currentUser.userId = userId; 604 UserHandle currentUser = null; 605 int allUsersSize = users.size(); 606 ArrayList<UserInfo> halUsers = new ArrayList<>(allUsersSize); 607 for (int i = 0; i < allUsersSize; i++) { 608 UserHandle user = users.get(i); 609 try { 610 if (user.getIdentifier() == usersInfo.currentUser.userId) { 611 currentUser = user; 612 } 613 UserInfo halUser = new UserInfo(); 614 halUser.userId = user.getIdentifier(); 615 halUser.flags = convertFlags(userHandleHelper, user); 616 halUsers.add(halUser); 617 } catch (Exception e) { 618 // Most likely the user was removed 619 Slogf.w(TAG, "newUsersInfo(): ignoring user " + user + " due to exception", e); 620 } 621 } 622 int existingUsersSize = halUsers.size(); 623 usersInfo.numberUsers = existingUsersSize; 624 usersInfo.existingUsers = halUsers.toArray(new UserInfo[existingUsersSize]); 625 626 if (currentUser != null) { 627 usersInfo.currentUser.flags = convertFlags(userHandleHelper, currentUser); 628 } else { 629 // This should not happen. 630 Slogf.wtf(TAG, "Current user is not part of existing users. usersInfo: " + usersInfo); 631 } 632 633 return usersInfo; 634 } 635 636 /** 637 * Checks if the given {@code usersInfo} is valid. 638 * 639 * @throws IllegalArgumentException if it isn't. 640 */ checkValid(UsersInfo usersInfo)641 public static void checkValid(UsersInfo usersInfo) { 642 Preconditions.checkArgument(usersInfo != null); 643 Preconditions.checkArgument(usersInfo.existingUsers != null); 644 Preconditions.checkArgument(usersInfo.currentUser != null); 645 Preconditions.checkArgument(usersInfo.numberUsers == usersInfo.existingUsers.length, 646 "sizes mismatch: numberUsers=%d, existingUsers.size=%d", usersInfo.numberUsers, 647 usersInfo.existingUsers.length); 648 boolean hasCurrentUser = false; 649 int currentUserFlags = 0; 650 for (int i = 0; i < usersInfo.numberUsers; i++) { 651 UserInfo user = usersInfo.existingUsers[i]; 652 if (user.userId == usersInfo.currentUser.userId) { 653 hasCurrentUser = true; 654 currentUserFlags = user.flags; 655 break; 656 } 657 } 658 Preconditions.checkArgument(hasCurrentUser, 659 "current user not found on existing users on %s", usersInfo); 660 Preconditions.checkArgument(usersInfo.currentUser.flags == currentUserFlags, 661 "current user flags mismatch on existing users on %s", usersInfo); 662 } 663 664 /** 665 * Gets an empty CreateUserRequest with fields initialized to valid empty values (not 666 * {@code null}). 667 * 668 * @return An empty {@link CreateUserRequest}. 669 */ emptyCreateUserRequest()670 public static CreateUserRequest emptyCreateUserRequest() { 671 CreateUserRequest request = new CreateUserRequest(); 672 request.newUserInfo = new UserInfo(); 673 request.usersInfo = emptyUsersInfo(); 674 request.newUserName = ""; 675 return request; 676 } 677 678 /** 679 * Gets an empty SwitchUserRequest with fields initialized to valid empty values (not 680 * {@code null} 681 * 682 * @return An empty {@link SwitchUserRequest}. 683 */ emptySwitchUserRequest()684 public static SwitchUserRequest emptySwitchUserRequest() { 685 SwitchUserRequest request = new SwitchUserRequest(); 686 request.targetUser = new UserInfo(); 687 request.usersInfo = emptyUsersInfo(); 688 return request; 689 } 690 691 /** 692 * Gets an empty RemoveUserRequest with fields initialized to valid empty values (not 693 * {@code null} 694 * 695 * @return An empty {@link RemoveUserRequest}. 696 */ emptyRemoveUserRequest()697 public static RemoveUserRequest emptyRemoveUserRequest() { 698 RemoveUserRequest request = new RemoveUserRequest(); 699 request.removedUserInfo = new UserInfo(); 700 request.usersInfo = emptyUsersInfo(); 701 return request; 702 } 703 704 /** 705 * Gets an empty UserIdentificationGetRequest with fields initialized to valid empty values 706 * (not {@code null}). 707 * 708 * @return An empty {@link UserIdentificationGetRequest}. 709 */ emptyUserIdentificationGetRequest()710 public static UserIdentificationGetRequest emptyUserIdentificationGetRequest() { 711 UserIdentificationGetRequest request = new UserIdentificationGetRequest(); 712 request.userInfo = new UserInfo(); 713 request.associationTypes = EMPTY_INT_ARRAY; 714 return request; 715 } 716 717 /** 718 * Gets an empty UserIdentificationSetRequest with fields initialized to valid empty values 719 * (not {@code null}). 720 * 721 * @return An empty {@link UserIdentificationSetRequest}. 722 */ emptyUserIdentificationSetRequest()723 public static UserIdentificationSetRequest emptyUserIdentificationSetRequest() { 724 UserIdentificationSetRequest request = new UserIdentificationSetRequest(); 725 request.userInfo = new UserInfo(); 726 request.associations = new UserIdentificationSetAssociation[0]; 727 return request; 728 } 729 730 /** 731 * Gets an empty UsersInfo with fields initialized to valid empty values (not {@code null}). 732 * 733 * @return An empty {@link UsersInfo}. 734 */ emptyUsersInfo()735 public static UsersInfo emptyUsersInfo() { 736 UsersInfo usersInfo = new UsersInfo(); 737 usersInfo.currentUser = new UserInfo(); 738 usersInfo.existingUsers = new UserInfo[0]; 739 usersInfo.currentUser.userId = UserManagerHelper.USER_NULL; 740 return usersInfo; 741 } 742 assertMinimumSize(HalPropValue prop, int minSize)743 private static void assertMinimumSize(HalPropValue prop, int minSize) { 744 checkArgument(prop.getInt32ValuesSize() >= minSize, 745 "not enough int32Values (minimum is %d) on %s", minSize, prop); 746 } 747 748 @ExcludeFromCodeCoverageGeneratedReport(reason = PRIVATE_CONSTRUCTOR) UserHalHelper()749 private UserHalHelper() { 750 throw new UnsupportedOperationException("contains only static methods"); 751 } 752 } 753