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