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