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