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