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 android.car.VehiclePropertyIds.CREATE_USER; 19 import static android.car.VehiclePropertyIds.INITIAL_USER_INFO; 20 import static android.car.VehiclePropertyIds.REMOVE_USER; 21 import static android.car.VehiclePropertyIds.SWITCH_USER; 22 import static android.car.VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION; 23 24 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 25 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; 26 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.app.ActivityManager; 30 import android.car.hardware.property.CarPropertyManager; 31 import android.car.user.CarUserManager; 32 import android.car.userlib.HalCallback; 33 import android.car.userlib.UserHalHelper; 34 import android.hardware.automotive.vehicle.V2_0.CreateUserRequest; 35 import android.hardware.automotive.vehicle.V2_0.CreateUserResponse; 36 import android.hardware.automotive.vehicle.V2_0.CreateUserStatus; 37 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoRequestType; 38 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse; 39 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction; 40 import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest; 41 import android.hardware.automotive.vehicle.V2_0.SwitchUserMessageType; 42 import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest; 43 import android.hardware.automotive.vehicle.V2_0.SwitchUserResponse; 44 import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus; 45 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociation; 46 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationType; 47 import android.hardware.automotive.vehicle.V2_0.UserIdentificationGetRequest; 48 import android.hardware.automotive.vehicle.V2_0.UserIdentificationResponse; 49 import android.hardware.automotive.vehicle.V2_0.UserIdentificationSetAssociation; 50 import android.hardware.automotive.vehicle.V2_0.UserIdentificationSetRequest; 51 import android.hardware.automotive.vehicle.V2_0.UsersInfo; 52 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig; 53 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 54 import android.os.Handler; 55 import android.os.ServiceSpecificException; 56 import android.sysprop.CarProperties; 57 import android.text.TextUtils; 58 import android.util.EventLog; 59 import android.util.Slog; 60 import android.util.SparseArray; 61 import android.util.SparseBooleanArray; 62 63 import com.android.car.CarLocalServices; 64 import com.android.car.CarLog; 65 import com.android.car.CarServiceUtils; 66 import com.android.car.CarStatsLog; 67 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 68 import com.android.car.internal.common.EventLogTags; 69 import com.android.car.internal.common.UserHelperLite; 70 import com.android.car.user.CarUserService; 71 import com.android.internal.annotations.GuardedBy; 72 import com.android.internal.annotations.VisibleForTesting; 73 import com.android.internal.util.FunctionalUtils; 74 import com.android.internal.util.Preconditions; 75 76 import java.io.PrintWriter; 77 import java.io.StringWriter; 78 import java.util.Arrays; 79 import java.util.Collection; 80 import java.util.List; 81 import java.util.Objects; 82 import java.util.Optional; 83 import java.util.concurrent.ThreadLocalRandom; 84 85 /** 86 * Service used to integrate the OEM's custom user management with Android's. 87 */ 88 public final class UserHalService extends HalServiceBase { 89 90 private static final String TAG = CarLog.tagFor(UserHalService.class); 91 92 private static final String UNSUPPORTED_MSG = "Vehicle HAL does not support user management"; 93 private static final String USER_ASSOCIATION_UNSUPPORTED_MSG = 94 "Vehicle HAL does not support user association"; 95 96 private static final int[] SUPPORTED_PROPERTIES = new int[]{ 97 CREATE_USER, 98 INITIAL_USER_INFO, 99 REMOVE_USER, 100 SWITCH_USER, 101 USER_IDENTIFICATION_ASSOCIATION 102 }; 103 104 private static final int[] CORE_PROPERTIES = new int[]{ 105 CREATE_USER, 106 INITIAL_USER_INFO, 107 REMOVE_USER, 108 SWITCH_USER, 109 }; 110 111 private static final boolean DBG = false; 112 113 private final Object mLock = new Object(); 114 115 private final VehicleHal mHal; 116 117 @GuardedBy("mLock") 118 @Nullable 119 private SparseArray<VehiclePropConfig> mProperties; 120 121 // This handler handles 2 types of messages: 122 // - "Anonymous" messages (what=0) containing runnables. 123 // - "Identifiable" messages used to check for timeouts (whose 'what' is the request id). 124 private final Handler mHandler; 125 126 /** 127 * Value used on the next request. 128 */ 129 @GuardedBy("mLock") 130 private int mNextRequestId = 1; 131 132 /** 133 * Base requestID. RequestID logged for west world metrics will be mBaseRequestID + original 134 * requestID 135 */ 136 private final int mBaseRequestId; 137 138 /** 139 * Map of callbacks by request id. 140 */ 141 @GuardedBy("mLock") 142 private final SparseArray<PendingRequest<?, ?>> mPendingRequests = new SparseArray<>(); 143 UserHalService(VehicleHal hal)144 public UserHalService(VehicleHal hal) { 145 this(hal, new Handler(CarServiceUtils.getHandlerThread( 146 CarUserService.HANDLER_THREAD_NAME).getLooper())); 147 } 148 149 @VisibleForTesting UserHalService(VehicleHal hal, Handler handler)150 UserHalService(VehicleHal hal, Handler handler) { 151 mHal = hal; 152 mHandler = handler; 153 mBaseRequestId = ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE); 154 } 155 156 @Override init()157 public void init() { 158 if (DBG) Slog.d(TAG, "init()"); 159 160 if (mProperties == null) { 161 return; 162 } 163 164 int size = mProperties.size(); 165 for (int i = 0; i < size; i++) { 166 VehiclePropConfig config = mProperties.valueAt(i); 167 if (VehicleHal.isPropertySubscribable(config)) { 168 if (DBG) Slog.d(TAG, "subscribing to property " + config.prop); 169 mHal.subscribeProperty(this, config.prop); 170 } 171 } 172 } 173 174 @Override release()175 public void release() { 176 if (DBG) Slog.d(TAG, "release()"); 177 } 178 179 @Override onHalEvents(List<VehiclePropValue> values)180 public void onHalEvents(List<VehiclePropValue> values) { 181 if (DBG) Slog.d(TAG, "handleHalEvents(): " + values); 182 183 for (int i = 0; i < values.size(); i++) { 184 VehiclePropValue value = values.get(i); 185 switch (value.prop) { 186 case INITIAL_USER_INFO: 187 mHandler.sendMessage(obtainMessage( 188 UserHalService::handleOnInitialUserInfoResponse, this, value)); 189 break; 190 case SWITCH_USER: 191 mHandler.sendMessage(obtainMessage( 192 UserHalService::handleOnSwitchUserResponse, this, value)); 193 break; 194 case CREATE_USER: 195 mHandler.sendMessage(obtainMessage( 196 UserHalService::handleOnCreateUserResponse, this, value)); 197 break; 198 case REMOVE_USER: 199 Slog.w(TAG, "Received REMOVE_USER HAL event: " + value); 200 break; 201 case USER_IDENTIFICATION_ASSOCIATION: 202 mHandler.sendMessage(obtainMessage( 203 UserHalService::handleOnUserIdentificationAssociation, this, value)); 204 break; 205 default: 206 Slog.w(TAG, "received unsupported event from HAL: " + value); 207 } 208 } 209 } 210 211 @Override onPropertySetError(int property, int area, @CarPropertyManager.CarSetPropertyErrorCode int errorCode)212 public void onPropertySetError(int property, int area, 213 @CarPropertyManager.CarSetPropertyErrorCode int errorCode) { 214 if (DBG) Slog.d(TAG, "handlePropertySetError(" + property + "/" + area + ")"); 215 } 216 217 @Override getAllSupportedProperties()218 public int[] getAllSupportedProperties() { 219 return SUPPORTED_PROPERTIES; 220 } 221 222 @Override takeProperties(Collection<VehiclePropConfig> properties)223 public void takeProperties(Collection<VehiclePropConfig> properties) { 224 if (properties.isEmpty()) { 225 Slog.w(TAG, UNSUPPORTED_MSG); 226 return; 227 } 228 SparseArray<VehiclePropConfig> supportedProperties = new SparseArray<>(5); 229 for (VehiclePropConfig config : properties) { 230 supportedProperties.put(config.prop, config); 231 } 232 synchronized (mLock) { 233 mProperties = supportedProperties; 234 } 235 } 236 237 /** 238 * Checks if the Vehicle HAL supports core user management actions. 239 */ isSupported()240 public boolean isSupported() { 241 synchronized (mLock) { 242 if (mProperties == null) return false; 243 244 for (int i = 0; i < CORE_PROPERTIES.length; i++) { 245 if (mProperties.get(CORE_PROPERTIES[i]) == null) { 246 return false; 247 } 248 } 249 return true; 250 } 251 } 252 253 /** 254 * Checks if the Vehicle HAL supports core user management actions. 255 */ isUserAssociationSupported()256 public boolean isUserAssociationSupported() { 257 synchronized (mLock) { 258 if (mProperties == null) return false; 259 if (mProperties.get(USER_IDENTIFICATION_ASSOCIATION) == null) return false; 260 return true; 261 } 262 } 263 264 @GuardedBy("mLock") checkSupported()265 private void checkSupported() { 266 Preconditions.checkState(isSupported(), UNSUPPORTED_MSG); 267 } 268 269 @GuardedBy("mLock") checkUserAssociationSupported()270 private void checkUserAssociationSupported() { 271 Preconditions.checkState(isUserAssociationSupported(), USER_ASSOCIATION_UNSUPPORTED_MSG); 272 } 273 274 // Returns mBaseRequestId + originalRequestID. If it overflows, then MOD by Integer.MAX_VALUE 275 // This request Id is used for logging data in statsd for westworld metrics. As original request 276 // id starts with 1 after every restart, a random id is desired for co-relating metrics on the 277 // server side in the west world. mBaseRequestId is generated as a random id on each restart. getRequestIdForStatsLog(int originalRequestId)278 private int getRequestIdForStatsLog(int originalRequestId) { 279 if (Integer.MAX_VALUE - mBaseRequestId < originalRequestId) { 280 // overflow 281 return (mBaseRequestId - Integer.MAX_VALUE) + originalRequestId; 282 } 283 return mBaseRequestId + originalRequestId; 284 } 285 286 /** 287 * Calls HAL to asynchronously get info about the initial user. 288 * 289 * @param requestType type of request (as defined by 290 * {@link android.hardware.automotive.vehicle.V2_0.InitialUserInfoRequestType}). 291 * @param timeoutMs how long to wait (in ms) for the property change event. 292 * @param usersInfo current state of Android users. 293 * @param callback to handle the response. 294 * 295 * @throws IllegalStateException if the HAL does not support user management (callers should 296 * call {@link #isSupported()} first to avoid this exception). 297 */ getInitialUserInfo(int requestType, int timeoutMs, @NonNull UsersInfo usersInfo, @NonNull HalCallback<InitialUserInfoResponse> callback)298 public void getInitialUserInfo(int requestType, int timeoutMs, @NonNull UsersInfo usersInfo, 299 @NonNull HalCallback<InitialUserInfoResponse> callback) { 300 if (DBG) Slog.d(TAG, "getInitialInfo(" + requestType + ")"); 301 Preconditions.checkArgumentPositive(timeoutMs, "timeout must be positive"); 302 Objects.requireNonNull(usersInfo); 303 UserHalHelper.checkValid(usersInfo); 304 Objects.requireNonNull(callback); 305 checkSupported(); 306 307 int requestId = getNextRequestId(); 308 VehiclePropValue propRequest = UserHalHelper.createPropRequest(INITIAL_USER_INFO, requestId, 309 requestType); 310 UserHalHelper.addUsersInfo(propRequest, usersInfo); 311 312 synchronized (mLock) { 313 if (hasPendingRequestLocked(InitialUserInfoResponse.class, callback)) return; 314 addPendingRequestLocked(requestId, InitialUserInfoResponse.class, callback); 315 } 316 317 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_INITIAL_USER_INFO_REQ, requestId, 318 requestType, timeoutMs); 319 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED, 320 getRequestIdForStatsLog(requestId), 321 getInitialUserInfoRequestTypeForStatsd(requestType), timeoutMs); 322 323 sendHalRequest(requestId, timeoutMs, propRequest, callback); 324 } 325 getInitialUserInfoRequestTypeForStatsd(int requestType)326 private static int getInitialUserInfoRequestTypeForStatsd(int requestType) { 327 // CHECKSTYLE:OFF IndentationCheck 328 switch (requestType) { 329 case InitialUserInfoRequestType.FIRST_BOOT: 330 return CarStatsLog 331 .CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED__REQUEST_TYPE__FIRST_BOOT; 332 case InitialUserInfoRequestType.FIRST_BOOT_AFTER_OTA: 333 return CarStatsLog 334 .CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED__REQUEST_TYPE__FIRST_BOOT_AFTER_OTA; 335 case InitialUserInfoRequestType.COLD_BOOT: 336 return CarStatsLog 337 .CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED__REQUEST_TYPE__COLD_BOOT; 338 case InitialUserInfoRequestType.RESUME: 339 return CarStatsLog 340 .CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED__REQUEST_TYPE__RESUME; 341 default: 342 return CarStatsLog 343 .CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED__REQUEST_TYPE__UNKNOWN; 344 } 345 // CHECKSTYLE:ON IndentationCheck 346 } 347 sendHalRequest(int requestId, int timeoutMs, @NonNull VehiclePropValue request, @NonNull HalCallback<?> callback)348 private void sendHalRequest(int requestId, int timeoutMs, @NonNull VehiclePropValue request, 349 @NonNull HalCallback<?> callback) { 350 mHandler.sendMessageDelayed(obtainMessage( 351 UserHalService::handleCheckIfRequestTimedOut, this, requestId).setWhat(requestId), 352 timeoutMs); 353 try { 354 if (DBG) Slog.d(TAG, "Calling hal.set(): " + request); 355 mHal.set(request); 356 } catch (ServiceSpecificException e) { 357 handleRemovePendingRequest(requestId); 358 Slog.w(TAG, "Failed to set " + request, e); 359 callback.onResponse(HalCallback.STATUS_HAL_SET_TIMEOUT, null); 360 } 361 } 362 363 /** 364 * Calls HAL to asynchronously switch user. 365 * 366 * @param request metadata 367 * @param timeoutMs how long to wait (in ms) for the property change event. 368 * @param callback to handle the response. 369 * 370 * @throws IllegalStateException if the HAL does not support user management (callers should 371 * call {@link #isSupported()} first to avoid this exception). 372 */ switchUser(@onNull SwitchUserRequest request, int timeoutMs, @NonNull HalCallback<SwitchUserResponse> callback)373 public void switchUser(@NonNull SwitchUserRequest request, int timeoutMs, 374 @NonNull HalCallback<SwitchUserResponse> callback) { 375 Preconditions.checkArgumentPositive(timeoutMs, "timeout must be positive"); 376 Objects.requireNonNull(callback, "callback cannot be null"); 377 Objects.requireNonNull(request, "request cannot be null"); 378 if (DBG) Slog.d(TAG, "switchUser(" + request + ")"); 379 380 checkSupported(); 381 request.requestId = getNextRequestId(); 382 request.messageType = SwitchUserMessageType.ANDROID_SWITCH; 383 VehiclePropValue propRequest = UserHalHelper.toVehiclePropValue(request); 384 385 synchronized (mLock) { 386 if (hasPendingRequestLocked(SwitchUserResponse.class, callback)) return; 387 addPendingRequestLocked(request.requestId, SwitchUserResponse.class, callback); 388 } 389 390 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_SWITCH_USER_REQ, request.requestId, 391 request.targetUser.userId, timeoutMs); 392 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED, 393 getRequestIdForStatsLog(request.requestId), 394 CarStatsLog 395 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__SWITCH_REQUEST_ANDROID, 396 request.usersInfo.currentUser.userId, request.usersInfo.currentUser.flags, 397 request.targetUser.userId, request.targetUser.flags, timeoutMs); 398 399 sendHalRequest(request.requestId, timeoutMs, propRequest, callback); 400 } 401 402 /** 403 * Calls HAL to remove user. 404 * 405 * @throws IllegalStateException if the HAL does not support user management (callers should 406 * call {@link #isSupported()} first to avoid this exception). 407 */ removeUser(@onNull RemoveUserRequest request)408 public void removeUser(@NonNull RemoveUserRequest request) { 409 Objects.requireNonNull(request, "request cannot be null"); 410 if (DBG) Slog.d(TAG, "removeUser(" + request + ")"); 411 412 checkSupported(); 413 request.requestId = getNextRequestId(); 414 VehiclePropValue propRequest = UserHalHelper.toVehiclePropValue(request); 415 416 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_REMOVE_USER_REQ, 417 request.removedUserInfo.userId, request.usersInfo.currentUser.userId); 418 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED, 419 getRequestIdForStatsLog(request.requestId), 420 CarStatsLog 421 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__REMOVE_REQUEST, 422 request.usersInfo.currentUser.userId, request.usersInfo.currentUser.flags, 423 request.removedUserInfo.userId, request.removedUserInfo.flags, /* timeout */ -1); 424 425 try { 426 if (DBG) Slog.d(TAG, "Calling hal.set(): " + propRequest); 427 mHal.set(propRequest); 428 } catch (ServiceSpecificException e) { 429 Slog.w(TAG, "Failed to set REMOVE USER", e); 430 } 431 } 432 433 /** 434 * Calls HAL to indicate an Android user was created. 435 * 436 * @param request info about the created user. 437 * @param timeoutMs how long to wait (in ms) for the property change event. 438 * @param callback to handle the response. 439 * 440 * @throws IllegalStateException if the HAL does not support user management (callers should 441 * call {@link #isSupported()} first to avoid this exception). 442 */ createUser(@onNull CreateUserRequest request, int timeoutMs, @NonNull HalCallback<CreateUserResponse> callback)443 public void createUser(@NonNull CreateUserRequest request, int timeoutMs, 444 @NonNull HalCallback<CreateUserResponse> callback) { 445 Objects.requireNonNull(request); 446 Preconditions.checkArgumentPositive(timeoutMs, "timeout must be positive"); 447 Objects.requireNonNull(callback); 448 if (DBG) Slog.d(TAG, "createUser(): req=" + request + ", timeout=" + timeoutMs); 449 450 checkSupported(); 451 request.requestId = getNextRequestId(); 452 VehiclePropValue propRequest = UserHalHelper.toVehiclePropValue(request); 453 454 synchronized (mLock) { 455 if (hasPendingRequestLocked(CreateUserResponse.class, callback)) return; 456 addPendingRequestLocked(request.requestId, CreateUserResponse.class, callback); 457 } 458 459 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_CREATE_USER_REQ, request.requestId, 460 UserHelperLite.safeName(request.newUserName), request.newUserInfo.flags, timeoutMs); 461 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED, 462 getRequestIdForStatsLog(request.requestId), 463 CarStatsLog 464 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__CREATE_REQUEST, 465 request.usersInfo.currentUser.userId, request.usersInfo.currentUser.flags, 466 request.newUserInfo.userId, request.newUserInfo.flags, timeoutMs); 467 468 sendHalRequest(request.requestId, timeoutMs, propRequest, callback); 469 } 470 471 /** 472 * Calls HAL after android user switch. 473 */ postSwitchResponse(@onNull SwitchUserRequest request)474 public void postSwitchResponse(@NonNull SwitchUserRequest request) { 475 Objects.requireNonNull(request, "request cannot be null"); 476 if (DBG) Slog.d(TAG, "postSwitchResponse(" + request + ")"); 477 478 checkSupported(); 479 request.messageType = SwitchUserMessageType.ANDROID_POST_SWITCH; 480 VehiclePropValue propRequest = UserHalHelper.toVehiclePropValue(request); 481 482 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_POST_SWITCH_USER_REQ, request.requestId, 483 request.targetUser.userId, request.usersInfo.currentUser.userId); 484 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_POST_SWITCH_RESPONSE_REPORTED, 485 getRequestIdForStatsLog(request.requestId), 486 request.targetUser.userId == request.usersInfo.currentUser.userId 487 ? CarStatsLog.CAR_USER_HAL_POST_SWITCH_RESPONSE_REPORTED__SWITCH_STATUS__SUCCESS 488 : CarStatsLog.CAR_USER_HAL_POST_SWITCH_RESPONSE_REPORTED__SWITCH_STATUS__FAILURE); 489 490 try { 491 if (DBG) Slog.d(TAG, "Calling hal.set(): " + propRequest); 492 mHal.set(propRequest); 493 } catch (ServiceSpecificException e) { 494 Slog.w(TAG, "Failed to set ANDROID POST SWITCH", e); 495 } 496 } 497 498 /** 499 * Calls HAL to switch user after legacy Android user switch. Legacy Android user switch means 500 * user switch is not requested by {@link CarUserManager} or OEM, and user switch is directly 501 * requested by {@link ActivityManager} 502 */ legacyUserSwitch(@onNull SwitchUserRequest request)503 public void legacyUserSwitch(@NonNull SwitchUserRequest request) { 504 Objects.requireNonNull(request, "request cannot be null"); 505 if (DBG) Slog.d(TAG, "userSwitchLegacy(" + request + ")"); 506 507 checkSupported(); 508 request.requestId = getNextRequestId(); 509 request.messageType = SwitchUserMessageType.LEGACY_ANDROID_SWITCH; 510 VehiclePropValue propRequest = UserHalHelper.toVehiclePropValue(request); 511 512 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_LEGACY_SWITCH_USER_REQ, request.requestId, 513 request.targetUser.userId, request.usersInfo.currentUser.userId); 514 //CHECKSTYLE:OFF IndentationCheck 515 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED, 516 getRequestIdForStatsLog(request.requestId), CarStatsLog 517 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__SWITCH_REQUEST_LEGACY, 518 request.usersInfo.currentUser.userId, request.usersInfo.currentUser.flags, 519 request.targetUser.userId, request.targetUser.flags, /* timeout_ms= */ -1); 520 //CHECKSTYLE:ON IndentationCheck 521 522 try { 523 if (DBG) Slog.d(TAG, "Calling hal.set(): " + propRequest); 524 mHal.set(propRequest); 525 } catch (ServiceSpecificException e) { 526 Slog.w(TAG, "Failed to set LEGACY ANDROID SWITCH", e); 527 } 528 } 529 530 /** 531 * Calls HAL to get the value of the user identifications associated with the given user. 532 * 533 * @return HAL response or {@code null} if it was invalid (for example, mismatch on the 534 * requested number of associations). 535 * 536 * @throws IllegalArgumentException if request is invalid (mismatch on number of associations, 537 * duplicated association, invalid association type values, etc). 538 */ 539 @Nullable getUserAssociation( @onNull UserIdentificationGetRequest request)540 public UserIdentificationResponse getUserAssociation( 541 @NonNull UserIdentificationGetRequest request) { 542 Objects.requireNonNull(request, "request cannot be null"); 543 checkUserAssociationSupported(); 544 545 // Check that it doesn't have dupes 546 SparseBooleanArray types = new SparseBooleanArray(request.numberAssociationTypes); 547 for (int i = 0; i < request.numberAssociationTypes; i++) { 548 int type = request.associationTypes.get(i); 549 Preconditions.checkArgument(!types.get(type), "type %s found more than once on %s", 550 UserIdentificationAssociationType.toString(type), request); 551 types.put(type, true); 552 } 553 554 request.requestId = getNextRequestId(); 555 556 if (DBG) Slog.d(TAG, "getUserAssociation(): req=" + request); 557 558 VehiclePropValue requestAsPropValue = UserHalHelper.toVehiclePropValue(request); 559 560 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_GET_USER_AUTH_REQ, 561 requestAsPropValue.value.int32Values.toArray()); 562 563 VehiclePropValue responseAsPropValue = mHal.get(requestAsPropValue); 564 if (responseAsPropValue == null) { 565 Slog.w(TAG, "HAL returned null for request " + requestAsPropValue); 566 return null; 567 } 568 569 logEventWithErrorMessage(EventLogTags.CAR_USER_HAL_GET_USER_AUTH_RESP, responseAsPropValue); 570 if (DBG) Slog.d(TAG, "getUserAssociation(): responseAsPropValue=" + responseAsPropValue); 571 572 UserIdentificationResponse response; 573 try { 574 response = UserHalHelper.toUserIdentificationResponse(responseAsPropValue); 575 } catch (IllegalArgumentException e) { 576 Slog.w(TAG, "invalid response from HAL for " + requestAsPropValue, e); 577 return null; 578 } 579 if (DBG) Slog.d(TAG, "getUserAssociation(): response=" + response); 580 581 // Validate the response according to the request 582 if (response.requestId != request.requestId) { 583 Slog.w(TAG, "invalid request id (should be " + request.requestId + ") on HAL response: " 584 + response); 585 return null; 586 } 587 if (response.numberAssociation != request.numberAssociationTypes) { 588 Slog.w(TAG, "Wrong number of association types on HAL response (expected " 589 + request.numberAssociationTypes + ") for request " + requestAsPropValue 590 + ": " + response); 591 return null; 592 } 593 for (int i = 0; i < request.numberAssociationTypes; i++) { 594 int expectedType = request.associationTypes.get(i); 595 int actualType = response.associations.get(i).type; 596 if (actualType != expectedType) { 597 Slog.w(TAG, "Wrong type on index " + i + " of HAL response (" + response + ") for " 598 + "request " + requestAsPropValue + " : expected " 599 + UserIdentificationAssociationType.toString(expectedType) 600 + ", got " + UserIdentificationAssociationType.toString(actualType)); 601 return null; 602 } 603 } 604 605 // TODO(b/153900032): move this logic to a common helper 606 int[] associationTypes = new int[response.numberAssociation]; 607 int[] associationValues = new int[response.numberAssociation]; 608 for (int i = 0; i < response.numberAssociation; i++) { 609 UserIdentificationAssociation association = response.associations.get(i); 610 associationTypes[i] = association.type; 611 associationValues[i] = association.value; 612 } 613 614 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED, 615 getRequestIdForStatsLog(request.requestId), 616 CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED__REQUEST_TYPE__GET, 617 request.userInfo.userId, 618 request.userInfo.flags, 619 request.numberAssociationTypes, 620 Arrays.toString(associationTypes), Arrays.toString(associationValues)); 621 622 return response; 623 } 624 625 /** 626 * Calls HAL to set the value of the user identifications associated with the given user. 627 * 628 * @throws IllegalArgumentException if request is invalid (mismatch on number of associations, 629 * duplicated association, invalid association type values, etc). 630 */ setUserAssociation(int timeoutMs, @NonNull UserIdentificationSetRequest request, @NonNull HalCallback<UserIdentificationResponse> callback)631 public void setUserAssociation(int timeoutMs, @NonNull UserIdentificationSetRequest request, 632 @NonNull HalCallback<UserIdentificationResponse> callback) { 633 Preconditions.checkArgumentPositive(timeoutMs, "timeout must be positive"); 634 Objects.requireNonNull(request, "request cannot be null"); 635 Objects.requireNonNull(callback, "callback cannot be null"); 636 if (DBG) Slog.d(TAG, "setUserAssociation(" + request + ")"); 637 638 // Check that it doesn't have dupes 639 SparseBooleanArray types = new SparseBooleanArray(request.numberAssociations); 640 for (int i = 0; i < request.numberAssociations; i++) { 641 int type = request.associations.get(i).type; 642 Preconditions.checkArgument(!types.get(type), "type %s found more than once on %s", 643 UserIdentificationAssociationType.toString(type), request); 644 types.put(type, true); 645 } 646 647 checkUserAssociationSupported(); 648 request.requestId = getNextRequestId(); 649 VehiclePropValue propRequest = UserHalHelper.toVehiclePropValue(request); 650 651 synchronized (mLock) { 652 if (hasPendingRequestLocked(UserIdentificationResponse.class, callback)) return; 653 addPendingRequestLocked(request.requestId, UserIdentificationResponse.class, request, 654 callback); 655 } 656 657 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_SET_USER_AUTH_REQ, 658 propRequest.value.int32Values.toArray()); 659 // TODO(b/153900032): move this logic to a common helper 660 int[] associationTypes = new int[request.numberAssociations]; 661 int[] associationValues = new int[request.numberAssociations]; 662 for (int i = 0; i < request.numberAssociations; i++) { 663 UserIdentificationSetAssociation association = request.associations.get(i); 664 associationTypes[i] = association.type; 665 associationValues[i] = association.value; 666 } 667 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED, 668 getRequestIdForStatsLog(request.requestId), 669 CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED__REQUEST_TYPE__SET, 670 request.userInfo.userId, request.userInfo.flags, request.numberAssociations, 671 Arrays.toString(associationTypes), Arrays.toString(associationValues)); 672 sendHalRequest(request.requestId, timeoutMs, propRequest, callback); 673 } 674 handleOnUserIdentificationAssociation(@onNull VehiclePropValue value)675 private void handleOnUserIdentificationAssociation(@NonNull VehiclePropValue value) { 676 logEventWithErrorMessage(EventLogTags.CAR_USER_HAL_SET_USER_AUTH_RESP, value); 677 if (DBG) Slog.d(TAG, "handleOnUserIdentificationAssociation(): " + value); 678 679 int requestId = value.value.int32Values.get(0); 680 HalCallback<UserIdentificationResponse> callback = handleGetPendingCallback(requestId, 681 UserIdentificationResponse.class); 682 if (callback == null) { 683 Slog.w(TAG, "no callback for requestId " + requestId + ": " + value); 684 return; 685 } 686 PendingRequest<?, ?> pendingRequest = handleRemovePendingRequest(requestId); 687 UserIdentificationResponse response; 688 try { 689 response = UserHalHelper.toUserIdentificationResponse(value); 690 } catch (RuntimeException e) { 691 Slog.w(TAG, "error parsing UserIdentificationResponse (" + value + ")", e); 692 callback.onResponse(HalCallback.STATUS_WRONG_HAL_RESPONSE, null); 693 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_SET_USER_ASSOCIATION_RESPONSE_REPORTED, 694 getRequestIdForStatsLog(requestId), 695 getHalCallbackStatusForStatsd(HalCallback.STATUS_WRONG_HAL_RESPONSE), 696 /* number_associations= */ 0, /* user_identification_association_types= */ "", 697 /* user_identification_association_values= */ ""); 698 return; 699 } 700 701 // Validate the response according to the request 702 UserIdentificationSetRequest request = PendingRequest.getRequest(pendingRequest, 703 UserIdentificationSetRequest.class, requestId); 704 705 if (request == null) { 706 // already logged on PendingRequest.getRequest 707 callback.onResponse(HalCallback.STATUS_WRONG_HAL_RESPONSE, null); 708 logSetUserAssociationResponse(requestId, response, 709 HalCallback.STATUS_WRONG_HAL_RESPONSE); 710 return; 711 } 712 713 if (response.numberAssociation != request.numberAssociations) { 714 Slog.w(TAG, "Wrong number of association types on HAL response (expected " 715 + request.numberAssociations + ") for request " + request 716 + ": " + response); 717 callback.onResponse(HalCallback.STATUS_WRONG_HAL_RESPONSE, null); 718 logSetUserAssociationResponse(requestId, response, 719 HalCallback.STATUS_WRONG_HAL_RESPONSE); 720 return; 721 } 722 723 for (int i = 0; i < request.numberAssociations; i++) { 724 int expectedType = request.associations.get(i).type; 725 int actualType = response.associations.get(i).type; 726 if (actualType != expectedType) { 727 Slog.w(TAG, "Wrong type on index " + i + " of HAL response (" + response + ") for " 728 + "request " + value + " : expected " 729 + UserIdentificationAssociationType.toString(expectedType) 730 + ", got " + UserIdentificationAssociationType.toString(actualType)); 731 callback.onResponse(HalCallback.STATUS_WRONG_HAL_RESPONSE, null); 732 logSetUserAssociationResponse(requestId, response, 733 HalCallback.STATUS_WRONG_HAL_RESPONSE); 734 return; 735 } 736 } 737 738 if (DBG) Slog.d(TAG, "replying to request " + requestId + " with " + response); 739 callback.onResponse(HalCallback.STATUS_OK, response); 740 logSetUserAssociationResponse(requestId, response, HalCallback.STATUS_OK); 741 } 742 logSetUserAssociationResponse(int requestId, UserIdentificationResponse response, int halCallbackStatus)743 private void logSetUserAssociationResponse(int requestId, UserIdentificationResponse response, 744 int halCallbackStatus) { 745 // TODO(b/153900032): move this logic to a common helper 746 int[] associationTypes = new int[response.numberAssociation]; 747 int[] associationValues = new int[response.numberAssociation]; 748 for (int i = 0; i < response.numberAssociation; i++) { 749 UserIdentificationAssociation association = response.associations.get(i); 750 associationTypes[i] = association.type; 751 associationValues[i] = association.value; 752 } 753 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_SET_USER_ASSOCIATION_RESPONSE_REPORTED, 754 getRequestIdForStatsLog(requestId), 755 getHalCallbackStatusForStatsd(halCallbackStatus), response.numberAssociation, 756 Arrays.toString(associationTypes), Arrays.toString(associationValues)); 757 } 758 logEventWithErrorMessage(int eventTag, @NonNull VehiclePropValue value)759 private static void logEventWithErrorMessage(int eventTag, @NonNull VehiclePropValue value) { 760 if (TextUtils.isEmpty(value.value.stringValue)) { 761 EventLog.writeEvent(eventTag, value.value.int32Values.toArray()); 762 } else { 763 // Must manually append the error message to the array of values 764 int size = value.value.int32Values.size(); 765 Object[] list = new Object[size + 1]; 766 value.value.int32Values.toArray(list); 767 list[list.length - 1] = value.value.stringValue; 768 EventLog.writeEvent(eventTag, list); 769 } 770 } 771 772 @VisibleForTesting getNextRequestId()773 int getNextRequestId() { 774 synchronized (mLock) { 775 return mNextRequestId++; 776 } 777 } 778 779 @GuardedBy("mLock") addPendingRequestLocked(int requestId, @NonNull Class<RESP> responseClass, @NonNull REQ request, @NonNull HalCallback<RESP> callback)780 private <REQ, RESP> void addPendingRequestLocked(int requestId, 781 @NonNull Class<RESP> responseClass, @NonNull REQ request, 782 @NonNull HalCallback<RESP> callback) { 783 PendingRequest<?, RESP> pendingRequest = new PendingRequest<>(responseClass, request, 784 callback); 785 if (DBG) { 786 Slog.d(TAG, "adding pending request (" + pendingRequest + ") for requestId " 787 + requestId); 788 } 789 mPendingRequests.put(requestId, pendingRequest); 790 } 791 792 @GuardedBy("mLock") addPendingRequestLocked(int requestId, @NonNull Class<RESP> responseClass, @NonNull HalCallback<RESP> callback)793 private <RESP> void addPendingRequestLocked(int requestId, @NonNull Class<RESP> responseClass, 794 @NonNull HalCallback<RESP> callback) { 795 addPendingRequestLocked(requestId, responseClass, /* request= */ null, 796 callback); 797 } 798 799 /** 800 * Checks if there is a pending request of type {@code requestClass}, calling {@code callback} 801 * with {@link HalCallback#STATUS_CONCURRENT_OPERATION} when there is. 802 */ 803 @GuardedBy("mLock") hasPendingRequestLocked(@onNull Class<?> responseClass, @NonNull HalCallback<?> callback)804 private boolean hasPendingRequestLocked(@NonNull Class<?> responseClass, 805 @NonNull HalCallback<?> callback) { 806 for (int i = 0; i < mPendingRequests.size(); i++) { 807 PendingRequest<?, ?> pendingRequest = mPendingRequests.valueAt(i); 808 if (pendingRequest.responseClass == responseClass) { 809 Slog.w(TAG, "Already have pending request of type " + responseClass); 810 callback.onResponse(HalCallback.STATUS_CONCURRENT_OPERATION, null); 811 return true; 812 } 813 } 814 return false; 815 } 816 817 /** 818 * Removes the pending request and its timeout callback. 819 */ 820 @Nullable handleRemovePendingRequest(int requestId)821 private PendingRequest<?, ?> handleRemovePendingRequest(int requestId) { 822 if (DBG) Slog.d(TAG, "Removing pending request #" + requestId); 823 mHandler.removeMessages(requestId); 824 PendingRequest<?, ?> pendingRequest; 825 synchronized (mLock) { 826 pendingRequest = mPendingRequests.get(requestId); 827 mPendingRequests.remove(requestId); 828 } 829 return pendingRequest; 830 } 831 handleCheckIfRequestTimedOut(int requestId)832 private void handleCheckIfRequestTimedOut(int requestId) { 833 PendingRequest<?, ?> pendingRequest = getPendingRequest(requestId); 834 if (pendingRequest == null) return; 835 836 Slog.w(TAG, "Request #" + requestId + " timed out"); 837 handleRemovePendingRequest(requestId); 838 pendingRequest.callback.onResponse(HalCallback.STATUS_HAL_RESPONSE_TIMEOUT, null); 839 } 840 841 @Nullable getPendingRequest(int requestId)842 private PendingRequest<?, ?> getPendingRequest(int requestId) { 843 synchronized (mLock) { 844 return mPendingRequests.get(requestId); 845 } 846 } 847 handleOnInitialUserInfoResponse(VehiclePropValue value)848 private void handleOnInitialUserInfoResponse(VehiclePropValue value) { 849 int requestId = value.value.int32Values.get(0); 850 HalCallback<InitialUserInfoResponse> callback = handleGetPendingCallback(requestId, 851 InitialUserInfoResponse.class); 852 if (callback == null) { 853 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_INITIAL_USER_INFO_RESP, requestId, 854 HalCallback.STATUS_INVALID); 855 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED, 856 getRequestIdForStatsLog(requestId), 857 getHalCallbackStatusForStatsd(HalCallback.STATUS_INVALID), 858 getInitialUserInfoResponseActionForStatsd( 859 InitialUserInfoResponseAction.DEFAULT), 860 /* user id= */ -1, /* flag= */ -1, /* user locales= */ ""); 861 862 Slog.w(TAG, "no callback for requestId " + requestId + ": " + value); 863 return; 864 } 865 handleRemovePendingRequest(requestId); 866 867 InitialUserInfoResponse response; 868 try { 869 response = UserHalHelper.toInitialUserInfoResponse(value); 870 } catch (RuntimeException e) { 871 Slog.e(TAG, "invalid response (" + value + ") from HAL", e); 872 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_INITIAL_USER_INFO_RESP, requestId, 873 HalCallback.STATUS_WRONG_HAL_RESPONSE); 874 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED, 875 getRequestIdForStatsLog(requestId), 876 getHalCallbackStatusForStatsd(HalCallback.STATUS_WRONG_HAL_RESPONSE), 877 getInitialUserInfoResponseActionForStatsd( 878 InitialUserInfoResponseAction.DEFAULT), 879 /* user id= */ -1, /* flag= */ -1, /* user locales= */ ""); 880 881 callback.onResponse(HalCallback.STATUS_WRONG_HAL_RESPONSE, null); 882 return; 883 } 884 885 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_INITIAL_USER_INFO_RESP, requestId, 886 HalCallback.STATUS_OK, response.action, 887 response.userToSwitchOrCreate.userId, response.userToSwitchOrCreate.flags, 888 response.userNameToCreate, response.userLocales); 889 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED, 890 getRequestIdForStatsLog(requestId), 891 getHalCallbackStatusForStatsd(HalCallback.STATUS_OK), 892 getInitialUserInfoResponseActionForStatsd(response.action), 893 response.userToSwitchOrCreate.userId, response.userToSwitchOrCreate.flags, 894 response.userLocales); 895 896 if (DBG) Slog.d(TAG, "replying to request " + requestId + " with " + response); 897 callback.onResponse(HalCallback.STATUS_OK, response); 898 } 899 getInitialUserInfoResponseActionForStatsd(int action)900 private static int getInitialUserInfoResponseActionForStatsd(int action) { 901 switch (action) { 902 case InitialUserInfoResponseAction.CREATE: 903 return CarStatsLog 904 .CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED__RESPONSE_ACTION__CREATE; 905 case InitialUserInfoResponseAction.SWITCH: 906 return CarStatsLog 907 .CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED__RESPONSE_ACTION__SWITCH; 908 default: 909 return CarStatsLog 910 .CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED__RESPONSE_ACTION__DEFAULT; 911 } 912 } 913 handleOnSwitchUserResponse(VehiclePropValue value)914 private void handleOnSwitchUserResponse(VehiclePropValue value) { 915 int requestId = value.value.int32Values.get(0); 916 int messageType = value.value.int32Values.get(1); 917 918 if (messageType == SwitchUserMessageType.VEHICLE_RESPONSE) { 919 handleOnSwitchUserVehicleResponse(value); 920 return; 921 } 922 923 if (messageType == SwitchUserMessageType.VEHICLE_REQUEST) { 924 handleOnSwitchUserVehicleRequest(value); 925 return; 926 } 927 928 Slog.e(TAG, "handleOnSwitchUserResponse invalid message type (" + messageType 929 + ") from HAL: " + value); 930 931 // check if a callback exists for the request ID 932 HalCallback<SwitchUserResponse> callback = 933 handleGetPendingCallback(requestId, SwitchUserResponse.class); 934 if (callback != null) { 935 handleRemovePendingRequest(requestId); 936 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_SWITCH_USER_RESP, requestId, 937 HalCallback.STATUS_WRONG_HAL_RESPONSE); 938 callback.onResponse(HalCallback.STATUS_WRONG_HAL_RESPONSE, null); 939 return; 940 } 941 } 942 handleOnSwitchUserVehicleRequest(VehiclePropValue value)943 private void handleOnSwitchUserVehicleRequest(VehiclePropValue value) { 944 int requestId = value.value.int32Values.get(0); 945 // Index 1 is message type, which is not required in this call. 946 int targetUserId = value.value.int32Values.get(2); 947 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_OEM_SWITCH_USER_REQ, requestId, targetUserId); 948 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED, 949 getRequestIdForStatsLog(requestId), 950 CarStatsLog 951 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__SWITCH_REQUEST_OEM, 952 /* current user id= */ -1, /* current user flag= */ -1, targetUserId, 953 /* target user flag= */ -1, /* timeout_ms= */ -1); 954 955 // HAL vehicle request should have negative request ID 956 if (requestId >= 0) { 957 Slog.e(TAG, "handleVehicleRequest invalid requestId (" + requestId + ") from HAL: " 958 + value); 959 return; 960 } 961 962 CarUserService userService = CarLocalServices.getService(CarUserService.class); 963 userService.switchAndroidUserFromHal(requestId, targetUserId); 964 } 965 handleOnSwitchUserVehicleResponse(VehiclePropValue value)966 private void handleOnSwitchUserVehicleResponse(VehiclePropValue value) { 967 int requestId = value.value.int32Values.get(0); 968 HalCallback<SwitchUserResponse> callback = 969 handleGetPendingCallback(requestId, SwitchUserResponse.class); 970 if (callback == null) { 971 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_SWITCH_USER_RESP, requestId, 972 HalCallback.STATUS_INVALID); 973 Slog.w(TAG, "no callback for requestId " + requestId + ": " + value); 974 logHalSwitchUserResponse(requestId, HalCallback.STATUS_WRONG_HAL_RESPONSE); 975 return; 976 } 977 handleRemovePendingRequest(requestId); 978 SwitchUserResponse response = new SwitchUserResponse(); 979 response.requestId = requestId; 980 response.messageType = value.value.int32Values.get(1); 981 response.status = value.value.int32Values.get(2); 982 response.errorMessage = value.value.stringValue; 983 if (response.status == SwitchUserStatus.SUCCESS 984 || response.status == SwitchUserStatus.FAILURE) { 985 if (DBG) { 986 Slog.d(TAG, "replying to request " + requestId + " with " + response); 987 } 988 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_SWITCH_USER_RESP, requestId, 989 HalCallback.STATUS_OK, response.status, response.errorMessage); 990 callback.onResponse(HalCallback.STATUS_OK, response); 991 logHalSwitchUserResponse(requestId, HalCallback.STATUS_OK, response.status); 992 } else { 993 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_SWITCH_USER_RESP, requestId, 994 HalCallback.STATUS_WRONG_HAL_RESPONSE, response.status, response.errorMessage); 995 Slog.e(TAG, "invalid status (" + response.status + ") from HAL: " + value); 996 callback.onResponse(HalCallback.STATUS_WRONG_HAL_RESPONSE, null); 997 logHalSwitchUserResponse(requestId, HalCallback.STATUS_WRONG_HAL_RESPONSE, 998 response.status); 999 } 1000 } 1001 handleOnCreateUserResponse(VehiclePropValue value)1002 private void handleOnCreateUserResponse(VehiclePropValue value) { 1003 int requestId = value.value.int32Values.get(0); 1004 HalCallback<CreateUserResponse> callback = 1005 handleGetPendingCallback(requestId, CreateUserResponse.class); 1006 if (callback == null) { 1007 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_CREATE_USER_RESP, requestId, 1008 HalCallback.STATUS_INVALID); 1009 Slog.w(TAG, "no callback for requestId " + requestId + ": " + value); 1010 return; 1011 } 1012 handleRemovePendingRequest(requestId); 1013 CreateUserResponse response = new CreateUserResponse(); 1014 response.requestId = requestId; 1015 response.status = value.value.int32Values.get(1); 1016 response.errorMessage = value.value.stringValue; 1017 if (response.status == CreateUserStatus.SUCCESS 1018 || response.status == CreateUserStatus.FAILURE) { 1019 if (DBG) { 1020 Slog.d(TAG, "replying to request " + requestId + " with " + response); 1021 } 1022 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_CREATE_USER_RESP, requestId, 1023 HalCallback.STATUS_OK, response.status, response.errorMessage); 1024 callback.onResponse(HalCallback.STATUS_OK, response); 1025 logHalCreateUserResponse(requestId, HalCallback.STATUS_OK, response.status); 1026 } else { 1027 EventLog.writeEvent(EventLogTags.CAR_USER_HAL_CREATE_USER_RESP, requestId, 1028 HalCallback.STATUS_WRONG_HAL_RESPONSE, response.status, response.errorMessage); 1029 Slog.e(TAG, "invalid status (" + response.status + ") from HAL: " + value); 1030 callback.onResponse(HalCallback.STATUS_WRONG_HAL_RESPONSE, null); 1031 logHalCreateUserResponse(requestId, HalCallback.STATUS_WRONG_HAL_RESPONSE); 1032 } 1033 } 1034 logHalSwitchUserResponse(int requestId, int halCallbackStatus)1035 private void logHalSwitchUserResponse(int requestId, int halCallbackStatus) { 1036 //CHECKSTYLE:OFF IndentationCheck 1037 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED, 1038 getRequestIdForStatsLog(requestId), 1039 getHalCallbackStatusForStatsd(halCallbackStatus), 1040 CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__UNSPECIFIED); 1041 //CHECKSTYLE:ON IndentationCheck 1042 } 1043 logHalSwitchUserResponse(int requestId, int halCallbackStatus, int userSwitchstatus)1044 private void logHalSwitchUserResponse(int requestId, int halCallbackStatus, 1045 int userSwitchstatus) { 1046 int userSwitchstatusForStatsd = userSwitchstatus == SwitchUserStatus.SUCCESS 1047 ? CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__SUCCESS 1048 : CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__FAILURE; 1049 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED, 1050 getRequestIdForStatsLog(requestId), 1051 getHalCallbackStatusForStatsd(halCallbackStatus), userSwitchstatusForStatsd); 1052 } 1053 logHalCreateUserResponse(int requestId, int halCallbackStatus)1054 private void logHalCreateUserResponse(int requestId, int halCallbackStatus) { 1055 //CHECKSTYLE:OFF IndentationCheck 1056 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED, 1057 getRequestIdForStatsLog(requestId), 1058 getHalCallbackStatusForStatsd(halCallbackStatus), 1059 CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__UNSPECIFIED); 1060 //CHECKSTYLE:ON IndentationCheck 1061 } 1062 logHalCreateUserResponse(int requestId, int halCallbackStatus, int userCreatestatus)1063 private void logHalCreateUserResponse(int requestId, int halCallbackStatus, 1064 int userCreatestatus) { 1065 int userCreatestatusForStatsd = userCreatestatus == CreateUserStatus.SUCCESS 1066 ? CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__SUCCESS 1067 : CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__FAILURE; 1068 CarStatsLog.write(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED, 1069 getRequestIdForStatsLog(requestId), 1070 getHalCallbackStatusForStatsd(halCallbackStatus), userCreatestatusForStatsd); 1071 } 1072 getHalCallbackStatusForStatsd(int halCallbackStatus)1073 private int getHalCallbackStatusForStatsd(int halCallbackStatus) { 1074 // CHECKSTYLE:OFF IndentationCheck 1075 switch (halCallbackStatus) { 1076 case HalCallback.STATUS_OK: 1077 return CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__OK; 1078 case HalCallback.STATUS_HAL_SET_TIMEOUT: 1079 return CarStatsLog 1080 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__HAL_SET_TIMEOUT; 1081 case HalCallback.STATUS_HAL_RESPONSE_TIMEOUT: 1082 return CarStatsLog 1083 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__HAL_RESPONSE_TIMEOUT; 1084 case HalCallback.STATUS_WRONG_HAL_RESPONSE: 1085 return CarStatsLog 1086 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__WRONG_HAL_RESPONSE; 1087 case HalCallback.STATUS_CONCURRENT_OPERATION: 1088 return CarStatsLog 1089 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__CONCURRENT_OPERATION; 1090 default: 1091 return CarStatsLog 1092 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__INVALID; 1093 } 1094 // CHECKSTYLE:ON IndentationCheck 1095 } 1096 handleGetPendingCallback(int requestId, Class<T> clazz)1097 private <T> HalCallback<T> handleGetPendingCallback(int requestId, Class<T> clazz) { 1098 PendingRequest<?, ?> pendingRequest = getPendingRequest(requestId); 1099 if (pendingRequest == null) return null; 1100 1101 if (pendingRequest.responseClass != clazz) { 1102 Slog.e(TAG, "Invalid callback class for request " + requestId + ": expected" + clazz 1103 + ", but got is " + pendingRequest.responseClass); 1104 // TODO(b/150413515): add unit test for this scenario once it supports other properties 1105 return null; 1106 } 1107 @SuppressWarnings("unchecked") 1108 HalCallback<T> callback = (HalCallback<T>) pendingRequest.callback; 1109 return callback; 1110 } 1111 1112 @Override 1113 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(PrintWriter writer)1114 public void dump(PrintWriter writer) { 1115 String indent = " "; 1116 writer.printf("*User HAL*\n"); 1117 1118 writer.printf("Relevant CarProperties\n"); 1119 dumpSystemProperty(writer, indent, "user_hal_timeout", CarProperties.user_hal_timeout()); 1120 1121 synchronized (mLock) { 1122 if (!isSupported()) { 1123 writer.println(UNSUPPORTED_MSG); 1124 return; 1125 } 1126 int numberProperties = mProperties.size(); 1127 writer.printf("%d supported properties\n", numberProperties); 1128 for (int i = 0; i < numberProperties; i++) { 1129 writer.printf("%s%s\n", indent, mProperties.valueAt(i)); 1130 } 1131 writer.printf("Base request id: %d\n", mBaseRequestId); 1132 writer.printf("next request id: %d\n", mNextRequestId); 1133 1134 int numberPendingCallbacks = mPendingRequests.size(); 1135 if (numberPendingCallbacks == 0) { 1136 writer.println("no pending callbacks"); 1137 } else { 1138 writer.printf("%d pending callbacks: %s\n", numberPendingCallbacks); 1139 for (int i = 0; i < numberPendingCallbacks; i++) { 1140 writer.print(indent); 1141 mPendingRequests.valueAt(i).dump(writer); 1142 writer.println(); 1143 } 1144 } 1145 } 1146 } 1147 dumpSystemProperty(@onNull PrintWriter writer, @NonNull String indent, @NonNull String name, Optional<?> prop)1148 private static void dumpSystemProperty(@NonNull PrintWriter writer, @NonNull String indent, 1149 @NonNull String name, Optional<?> prop) { 1150 String value = prop.isPresent() ? prop.get().toString() : "<NOT SET>"; 1151 writer.printf("%s%s=%s\n", indent, name, value); 1152 } 1153 1154 private static final class PendingRequest<REQ, RESP> { 1155 @NonNull 1156 public final Class<RESP> responseClass; 1157 1158 @Nullable 1159 public final REQ request; 1160 1161 @NonNull 1162 public final HalCallback<RESP> callback; 1163 PendingRequest(@onNull Class<RESP> responseClass, @Nullable REQ request, @NonNull HalCallback<RESP> callback)1164 PendingRequest(@NonNull Class<RESP> responseClass, @Nullable REQ request, 1165 @NonNull HalCallback<RESP> callback) { 1166 this.responseClass = responseClass; 1167 this.request = request; 1168 this.callback = callback; 1169 } 1170 1171 /** 1172 * Gets the safely cast request for a given pending request. 1173 */ 1174 @Nullable getRequest(@ullable PendingRequest<?, ?> pendingRequest, @NonNull Class<T> clazz, int requestId)1175 private static <T> T getRequest(@Nullable PendingRequest<?, ?> pendingRequest, 1176 @NonNull Class<T> clazz, int requestId) { 1177 if (pendingRequest == null) { 1178 Slog.e(TAG, "No pending request for id " + requestId); 1179 return null; 1180 1181 } 1182 Object request = pendingRequest.request; 1183 if (!clazz.isInstance(request)) { 1184 Slog.e(TAG, "Wrong pending request for id " + requestId + ": " + pendingRequest); 1185 return null; 1186 } 1187 return clazz.cast(request); 1188 } 1189 dump(@onNull PrintWriter pw)1190 public void dump(@NonNull PrintWriter pw) { 1191 pw.printf("Class: %s Callback: %s", responseClass.getSimpleName(), 1192 FunctionalUtils.getLambdaName(callback)); 1193 if (request != null) { 1194 pw.printf(" Request: %s", request); 1195 } 1196 } 1197 1198 @Override toString()1199 public String toString() { 1200 StringWriter sw = new StringWriter(); 1201 PrintWriter pw = new PrintWriter(sw); 1202 pw.print("[PendingRequest: "); 1203 dump(pw); 1204 pw.print("]"); 1205 pw.flush(); 1206 return sw.toString(); 1207 } 1208 } 1209 } 1210