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