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