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