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