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