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