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