1 /*
2  * Copyright (C) 2021 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 
17 package com.android.car.user;
18 
19 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
20 
21 import static com.android.car.CarServiceUtils.isEventOfType;
22 import static com.android.car.PermissionHelper.checkHasAtLeastOnePermissionGranted;
23 import static com.android.car.PermissionHelper.checkHasDumpPermissionGranted;
24 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
25 import static com.android.car.user.CarUserService.checkManageUsersPermission;
26 import static com.android.car.user.CarUserService.sendUserSwitchResult;
27 
28 import android.annotation.Nullable;
29 import android.annotation.UserIdInt;
30 import android.app.ActivityManager;
31 import android.car.CarOccupantZoneManager;
32 import android.car.CarOccupantZoneManager.OccupantTypeEnum;
33 import android.car.CarOccupantZoneManager.OccupantZoneInfo;
34 import android.car.IExperimentalCarUserService;
35 import android.car.SyncResultCallback;
36 import android.car.builtin.app.ActivityManagerHelper;
37 import android.car.builtin.os.TraceHelper;
38 import android.car.builtin.os.UserManagerHelper;
39 import android.car.builtin.util.Slogf;
40 import android.car.builtin.util.TimingsTraceLog;
41 import android.car.user.CarUserManager.UserLifecycleListener;
42 import android.car.user.UserCreationResult;
43 import android.car.user.UserLifecycleEventFilter;
44 import android.car.user.UserSwitchResult;
45 import android.car.util.concurrent.AndroidFuture;
46 import android.content.Context;
47 import android.content.res.Resources;
48 import android.os.UserHandle;
49 import android.os.UserManager;
50 import android.util.proto.ProtoOutputStream;
51 
52 import com.android.car.CarLog;
53 import com.android.car.CarServiceBase;
54 import com.android.car.R;
55 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
56 import com.android.car.internal.ResultCallbackImpl;
57 import com.android.car.internal.common.UserHelperLite;
58 import com.android.car.internal.os.CarSystemProperties;
59 import com.android.car.internal.util.IndentingPrintWriter;
60 import com.android.internal.annotations.GuardedBy;
61 import com.android.internal.annotations.VisibleForTesting;
62 
63 import java.util.ArrayList;
64 import java.util.Iterator;
65 import java.util.List;
66 import java.util.Objects;
67 import java.util.concurrent.CopyOnWriteArrayList;
68 
69 /**
70  * Experimental User Service for Car. Including:
71  *  <ul>
72  *    <li> Creates a user used as driver.
73  *    <li> Creates a user used as passenger.
74  *    <li> Switch drivers.
75  *  <ul/>
76  */
77 public final class ExperimentalCarUserService extends IExperimentalCarUserService.Stub
78         implements CarServiceBase {
79 
80     @VisibleForTesting
81     static final String TAG = CarLog.tagFor(ExperimentalCarUserService.class);
82 
83     private final int mHalTimeoutMs = CarSystemProperties.getUserHalTimeout().orElse(5_000);
84 
85     private final CopyOnWriteArrayList<PassengerCallback> mPassengerCallbacks =
86             new CopyOnWriteArrayList<>();
87 
88     private final Context mContext;
89     private final CarUserService mCarUserService;
90     private final UserManager mUserManager;
91     private final boolean mEnablePassengerSupport;
92     private final UserHandleHelper mUserHandleHelper;
93 
94     private final Object mLock = new Object();
95     // Only one passenger is supported.
96     @GuardedBy("mLock")
97     private @UserIdInt int mLastPassengerId = UserManagerHelper.USER_NULL;
98 
99     @GuardedBy("mLock")
100     private ZoneUserBindingHelper mZoneUserBindingHelper;
101 
102     private final UserLifecycleListener mUserLifecycleListener = event -> {
103         if (!isEventOfType(TAG, event, USER_LIFECYCLE_EVENT_TYPE_SWITCHING)) {
104             return;
105         }
106         Slogf.d(TAG, "ExperimentalCarUserService.onEvent: %s", event);
107 
108         onUserSwitching(event.getPreviousUserId(), event.getUserId());
109     };
110 
111     /** Interface for callbacks related to passenger activities. */
112     public interface PassengerCallback {
113         /** Called when passenger is started at a certain zone. */
onPassengerStarted(@serIdInt int passengerId, int zoneId)114         void onPassengerStarted(@UserIdInt int passengerId, int zoneId);
115         /** Called when passenger is stopped. */
onPassengerStopped(@serIdInt int passengerId)116         void onPassengerStopped(@UserIdInt int passengerId);
117     }
118 
119     /** Interface for delegating zone-related implementation to CarOccupantZoneService. */
120     public interface ZoneUserBindingHelper {
121         /** Gets occupant zones corresponding to the occupant type. */
getOccupantZones(@ccupantTypeEnum int occupantType)122         List<OccupantZoneInfo> getOccupantZones(@OccupantTypeEnum int occupantType);
123         /** Assigns the user to the occupant zone. */
assignUserToOccupantZone(@serIdInt int userId, int zoneId)124         boolean assignUserToOccupantZone(@UserIdInt int userId, int zoneId);
125         /** Makes the occupant zone unoccupied. */
unassignUserFromOccupantZone(@serIdInt int userId)126         boolean unassignUserFromOccupantZone(@UserIdInt int userId);
127         /** Returns whether there is a passenger display. */
isPassengerDisplayAvailable()128         boolean isPassengerDisplayAvailable();
129     }
130 
ExperimentalCarUserService(Context context, CarUserService carUserService, UserManager userManager)131     public ExperimentalCarUserService(Context context, CarUserService carUserService,
132             UserManager userManager) {
133         this(context, carUserService, userManager, new UserHandleHelper(context, userManager));
134     }
135 
136     @VisibleForTesting
ExperimentalCarUserService(Context context, CarUserService carUserService, UserManager userManager, UserHandleHelper userHandleHelper)137     public ExperimentalCarUserService(Context context, CarUserService carUserService,
138             UserManager userManager, UserHandleHelper userHandleHelper) {
139         mContext = context;
140         mUserManager = userManager;
141         mCarUserService = carUserService;
142         Resources resources = context.getResources();
143         mEnablePassengerSupport = resources.getBoolean(R.bool.enablePassengerSupport);
144         mUserHandleHelper = userHandleHelper;
145     }
146 
147     @Override
init()148     public void init() {
149         Slogf.d(TAG, "init()");
150 
151         UserLifecycleEventFilter userSwitchingEventFilter = new UserLifecycleEventFilter.Builder()
152                 .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING).build();
153         mCarUserService.addUserLifecycleListener(userSwitchingEventFilter, mUserLifecycleListener);
154     }
155 
156     @Override
release()157     public void release() {
158         Slogf.d(TAG, "release()");
159 
160         mCarUserService.removeUserLifecycleListener(mUserLifecycleListener);
161     }
162 
163     @Override
164     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)165     public void dump(IndentingPrintWriter writer) {
166         checkHasDumpPermissionGranted(mContext, "dump()");
167 
168         writer.println("*ExperimentalCarUserService*");
169 
170         List<UserHandle> allDrivers = getAllDrivers();
171         int driversSize = allDrivers.size();
172         writer.println("NumberOfDrivers: " + driversSize);
173         writer.increaseIndent();
174         for (int i = 0; i < driversSize; i++) {
175             int driverId = allDrivers.get(i).getIdentifier();
176             writer.printf("#%d: id=%d", i, driverId);
177             List<UserHandle> passengers = getPassengers(driverId);
178             int passengersSize = passengers.size();
179             writer.print(" NumberPassengers: " + passengersSize);
180             if (passengersSize > 0) {
181                 writer.print(" [");
182                 for (int j = 0; j < passengersSize; j++) {
183                     writer.print(passengers.get(j).getIdentifier());
184                     if (j < passengersSize - 1) {
185                         writer.print(" ");
186                     }
187                 }
188                 writer.print("]");
189             }
190             writer.println();
191         }
192         writer.decreaseIndent();
193         writer.printf("EnablePassengerSupport: %s\n", mEnablePassengerSupport);
194         synchronized (mLock) {
195             writer.printf("LastPassengerId: %d\n", mLastPassengerId);
196         }
197 
198         writer.printf("User HAL timeout: %dms\n",  mHalTimeoutMs);
199     }
200 
201     @Override
202     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dumpProto(ProtoOutputStream proto)203     public void dumpProto(ProtoOutputStream proto) {}
204 
205     @Override
createDriver(String name, boolean admin)206     public AndroidFuture<UserCreationResult> createDriver(String name, boolean admin) {
207         checkManageUsersPermission("createDriver");
208         Objects.requireNonNull(name, "name cannot be null");
209 
210         AndroidFuture<UserCreationResult> future = new AndroidFuture<>();
211 
212         ResultCallbackImpl<UserCreationResult> resultResultCallbackImpl = new ResultCallbackImpl<>(
213                 Runnable::run, new SyncResultCallback<>()) {
214             @Override
215             protected void onCompleted(UserCreationResult result) {
216                 if (result == null) {
217                     Slogf.w(TAG, "createDriver(%s, %s) failed", name, admin);
218                 }
219                 future.complete(result);
220                 super.onCompleted(result);
221             }
222         };
223         int flags = 0;
224 
225         if (admin) {
226             if (!(mUserManager.isAdminUser() || mUserManager.isSystemUser())) {
227                 String internalErrorMsg =
228                         "Only admin users and system user can create other admins.";
229                 Slogf.e(TAG, internalErrorMsg);
230                 mCarUserService.sendUserCreationFailure(resultResultCallbackImpl,
231                         UserCreationResult.STATUS_INVALID_REQUEST,
232                         internalErrorMsg);
233                 return future;
234             }
235             flags = UserManagerHelper.FLAG_ADMIN;
236         }
237 
238         mCarUserService.createUser(name,
239                 UserManagerHelper.getDefaultUserTypeForUserInfoFlags(flags), flags, mHalTimeoutMs,
240                 resultResultCallbackImpl, false);
241         return future;
242     }
243 
244     @Override
245     @Nullable
createPassenger(String name, @UserIdInt int driverId)246     public UserHandle createPassenger(String name, @UserIdInt int driverId) {
247         checkManageUsersPermission("createPassenger");
248         Objects.requireNonNull(name, "name cannot be null");
249 
250         UserHandle driver = mUserHandleHelper.getExistingUserHandle(driverId);
251         if (driver == null) {
252             Slogf.w(TAG, "the driver is invalid for driverId: %d", driverId);
253             return null;
254         }
255         if (mUserHandleHelper.isGuestUser(driver)) {
256             Slogf.w(TAG, "a guest driver with id %d cannot create a passenger", driverId);
257             return null;
258         }
259         // createPassenger doesn't use user HAL because user HAL doesn't support profile user yet.
260         UserManager userManager = mContext.createContextAsUser(driver, /* flags= */ 0)
261                 .getSystemService(UserManager.class);
262         UserHandle user = userManager.createProfile(name, UserManager.USER_TYPE_PROFILE_MANAGED,
263                 /* disallowedPackages= */ null);
264 
265         if (user == null) {
266             // Couldn't create user, most likely because there are too many.
267             Slogf.w(TAG, "can't create a profile for user %d", driverId);
268             return null;
269         }
270         return user;
271     }
272 
273     @Override
switchDriver(@serIdInt int driverId, AndroidFuture<UserSwitchResult> receiver)274     public void switchDriver(@UserIdInt int driverId, AndroidFuture<UserSwitchResult> receiver) {
275         checkManageUsersPermission("switchDriver");
276         ResultCallbackImpl<UserSwitchResult> resultCallbackImpl = new ResultCallbackImpl<>(
277                 Runnable::run, new SyncResultCallback<>()) {
278             @Override
279             protected void onCompleted(UserSwitchResult result) {
280                 receiver.complete(result);
281                 super.onCompleted(result);
282             }
283         };
284 
285         if (UserHelperLite.isHeadlessSystemUser(driverId)) {
286             // System user doesn't associate with real person, can not be switched to.
287             Slogf.w(TAG, "switching to system user in headless system user mode is not allowed");
288             sendUserSwitchResult(resultCallbackImpl, /* isLogout= */ false,
289                     UserSwitchResult.STATUS_INVALID_REQUEST);
290             return;
291         }
292         int userSwitchable = mUserManager.getUserSwitchability();
293         if (userSwitchable != UserManager.SWITCHABILITY_STATUS_OK) {
294             Slogf.w(TAG, "current process is not allowed to switch user");
295             sendUserSwitchResult(resultCallbackImpl, /* isLogout= */ false,
296                     UserSwitchResult.STATUS_INVALID_REQUEST);
297             return;
298         }
299         mCarUserService.switchUser(driverId, mHalTimeoutMs, resultCallbackImpl,
300                 /* ignoreUxRestriction= */ false);
301     }
302 
303     /**
304      * Returns all drivers who can occupy the driving zone. Guest users are included in the list.
305      *
306      * @return the list of {@link UserHandle} who can be a driver on the device.
307      */
308     @Override
getAllDrivers()309     public List<UserHandle> getAllDrivers() {
310         checkManageUsersOrDumpPermission("getAllDrivers");
311 
312         return getUsersHandle(
313                 (user) -> !UserHelperLite.isHeadlessSystemUser(user.getIdentifier())
314                         && mUserHandleHelper.isEnabledUser(user)
315                         && !mUserHandleHelper.isManagedProfile(user)
316                         && !mUserHandleHelper.isEphemeralUser(user));
317     }
318 
319     /**
320      * Returns all passengers under the given driver.
321      *
322      * @param driverId User id of a driver.
323      * @return the list of {@link UserHandle} who is a passenger under the given driver.
324      */
325     @Override
getPassengers(@serIdInt int driverId)326     public List<UserHandle> getPassengers(@UserIdInt int driverId) {
327         checkManageUsersOrDumpPermission("getPassengers");
328 
329         return getUsersHandle((user) -> {
330             return !UserHelperLite.isHeadlessSystemUser(user.getIdentifier())
331                     && mUserHandleHelper.isEnabledUser(user)
332                     && mUserHandleHelper.isManagedProfile(user)
333                     && mUserManager.isSameProfileGroup(user, UserHandle.of(driverId));
334         });
335     }
336 
337     @Override
startPassenger(@serIdInt int passengerId, int zoneId)338     public boolean startPassenger(@UserIdInt int passengerId, int zoneId) {
339         checkManageUsersPermission("startPassenger");
340 
341         synchronized (mLock) {
342             if (!ActivityManagerHelper.startUserInBackground(passengerId)) {
343                 Slogf.w(TAG, "could not start passenger");
344                 return false;
345             }
346             if (!assignUserToOccupantZone(passengerId, zoneId)) {
347                 Slogf.w(TAG, "could not assign passenger to zone");
348                 return false;
349             }
350             mLastPassengerId = passengerId;
351         }
352         for (PassengerCallback callback : mPassengerCallbacks) {
353             callback.onPassengerStarted(passengerId, zoneId);
354         }
355         return true;
356     }
357 
358     @Override
stopPassenger(@serIdInt int passengerId)359     public boolean stopPassenger(@UserIdInt int passengerId) {
360         checkManageUsersPermission("stopPassenger");
361 
362         return stopPassengerInternal(passengerId, true);
363     }
364 
365     /** Adds callback to listen to passenger activity events. */
addPassengerCallback(PassengerCallback callback)366     public void addPassengerCallback(PassengerCallback callback) {
367         Objects.requireNonNull(callback, "callback cannot be null");
368         mPassengerCallbacks.add(callback);
369     }
370 
371     /** Removes previously added callback to listen passenger events. */
removePassengerCallback(PassengerCallback callback)372     public void removePassengerCallback(PassengerCallback callback) {
373         Objects.requireNonNull(callback, "callback cannot be null");
374         mPassengerCallbacks.remove(callback);
375     }
376 
377     /** Sets the implementation of ZoneUserBindingHelper. */
setZoneUserBindingHelper(ZoneUserBindingHelper helper)378     public void setZoneUserBindingHelper(ZoneUserBindingHelper helper) {
379         synchronized (mLock) {
380             mZoneUserBindingHelper = helper;
381         }
382     }
383 
stopPassengerInternal(@serIdInt int passengerId, boolean checkCurrentDriver)384     private boolean stopPassengerInternal(@UserIdInt int passengerId, boolean checkCurrentDriver) {
385         synchronized (mLock) {
386             // NULL passengerId means the last passenger.
387             // This is to avoid accessing mPassengerId without obtaining mLock.
388             if (passengerId == UserManagerHelper.USER_NULL) {
389                 passengerId = mLastPassengerId;
390             }
391             UserHandle passenger = mUserHandleHelper.getExistingUserHandle(passengerId);
392             if (passenger == null) {
393                 Slogf.w(TAG, "passenger %d doesn't exist", passengerId);
394                 return false;
395             }
396             if (mLastPassengerId != passengerId) {
397                 Slogf.w(TAG, "passenger %d hasn't been started", passengerId);
398                 return true;
399             }
400             if (checkCurrentDriver) {
401                 int currentUserId = ActivityManager.getCurrentUser();
402                 if (!mUserManager.isSameProfileGroup(passenger, UserHandle.of(currentUserId))) {
403                     Slogf.w(TAG, "passenger %d is not a profile of the current user %d",
404                             passengerId, currentUserId);
405                     return false;
406                 }
407             }
408             // Passenger is a profile, so cannot be stopped through activity manager.
409             // Instead, activities started by the passenger are stopped and the passenger is
410             // unassigned from the zone.
411             ActivityManagerHelper.stopAllTasksForUser(passengerId);
412             if (!unassignUserFromOccupantZone(passengerId)) {
413                 Slogf.w(TAG, "could not unassign user %d from occupant zone", passengerId);
414                 return false;
415             }
416             mLastPassengerId = UserManagerHelper.USER_NULL;
417         }
418         for (PassengerCallback callback : mPassengerCallbacks) {
419             callback.onPassengerStopped(passengerId);
420         }
421         return true;
422     }
423 
onUserSwitching(@serIdInt int fromUserId, @UserIdInt int toUserId)424     private void onUserSwitching(@UserIdInt int fromUserId, @UserIdInt int toUserId) {
425         Slogf.d(TAG, "onUserSwitching() callback from user %d to user %d", fromUserId, toUserId);
426         TimingsTraceLog t = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE);
427         t.traceBegin("onUserSwitching-" + toUserId);
428 
429         stopPassengerInternal(/* passengerId= */ UserManagerHelper.USER_NULL, false);
430 
431         if (mEnablePassengerSupport && isPassengerDisplayAvailable()) {
432             setupPassengerUser();
433             startFirstPassenger(toUserId);
434         }
435         t.traceEnd();
436     }
437 
438     interface UserFilter {
isEligibleUser(UserHandle user)439         boolean isEligibleUser(UserHandle user);
440     }
441 
442     /** Returns all users who are matched by the given filter. */
getUsersHandle(UserFilter filter)443     private List<UserHandle> getUsersHandle(UserFilter filter) {
444         List<UserHandle> users = UserManagerHelper.getUserHandles(mUserManager,
445                 /* excludeDying= */ false);
446         List<UserHandle> usersFiltered = new ArrayList<UserHandle>();
447 
448         for (Iterator<UserHandle> iterator = users.iterator(); iterator.hasNext(); ) {
449             UserHandle user = iterator.next();
450             if (filter.isEligibleUser(user)) {
451                 usersFiltered.add(user);
452             }
453         }
454 
455         return usersFiltered;
456     }
457 
checkManageUsersOrDumpPermission(String message)458     private void checkManageUsersOrDumpPermission(String message) {
459         checkHasAtLeastOnePermissionGranted(mContext, message,
460                 android.Manifest.permission.MANAGE_USERS,
461                 android.Manifest.permission.DUMP);
462     }
463 
getNumberOfManagedProfiles(@serIdInt int userId)464     private int getNumberOfManagedProfiles(@UserIdInt int userId) {
465         List<UserHandle> users = UserManagerHelper.getUserHandles(mUserManager,
466                 /* excludeDying= */ false);
467         // Count all users that are managed profiles of the given user.
468         int managedProfilesCount = 0;
469         for (UserHandle user : users) {
470             if (mUserHandleHelper.isManagedProfile(user)
471                     && mUserManager.isSameProfileGroup(user, UserHandle.of(userId))) {
472                 managedProfilesCount++;
473             }
474         }
475         return managedProfilesCount;
476     }
477 
478     /**
479      * Starts the first passenger of the given driver and assigns the passenger to the front
480      * passenger zone.
481      *
482      * @param driverId User id of the driver.
483      * @return whether it succeeds.
484      */
startFirstPassenger(@serIdInt int driverId)485     private boolean startFirstPassenger(@UserIdInt int driverId) {
486         int zoneId = getAvailablePassengerZone();
487         if (zoneId == CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID) {
488             Slogf.w(TAG, "passenger occupant zone is not found");
489             return false;
490         }
491         List<UserHandle> passengers = getPassengers(driverId);
492         if (passengers.size() < 1) {
493             Slogf.w(TAG, "passenger is not found for driver %d", driverId);
494             return false;
495         }
496         // Only one passenger is supported. If there are two or more passengers, the first passenger
497         // is chosen.
498         int passengerId = passengers.get(0).getIdentifier();
499         if (!startPassenger(passengerId, zoneId)) {
500             Slogf.w(TAG, "cannot start passenger %d", passengerId);
501             return false;
502         }
503         return true;
504     }
505 
getAvailablePassengerZone()506     private int getAvailablePassengerZone() {
507         int[] occupantTypes = new int[] {CarOccupantZoneManager.OCCUPANT_TYPE_FRONT_PASSENGER,
508                 CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER};
509         for (int occupantType : occupantTypes) {
510             int zoneId = getZoneId(occupantType);
511             if (zoneId != CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID) {
512                 return zoneId;
513             }
514         }
515         return CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID;
516     }
517 
518     /**
519      * Creates a new passenger user when there is no passenger user.
520      */
setupPassengerUser()521     private void setupPassengerUser() {
522         int currentUser = ActivityManager.getCurrentUser();
523         int profileCount = getNumberOfManagedProfiles(currentUser);
524         if (profileCount > 0) {
525             Slogf.w(TAG, "max profile of user %d is exceeded: current profile count is %d",
526                     currentUser, profileCount);
527             return;
528         }
529         // TODO(b/140311342): Use resource string for the default passenger name.
530         UserHandle passenger = createPassenger("Passenger", currentUser);
531         if (passenger == null) {
532             // Couldn't create user, most likely because there are too many.
533             Slogf.w(TAG, "cannot create a passenger user");
534             return;
535         }
536     }
537 
getOccupantZones(@ccupantTypeEnum int occupantType)538     private List<OccupantZoneInfo> getOccupantZones(@OccupantTypeEnum int occupantType) {
539         ZoneUserBindingHelper helper = null;
540         synchronized (mLock) {
541             if (mZoneUserBindingHelper == null) {
542                 Slogf.w(TAG, "implementation is not delegated");
543                 return new ArrayList<OccupantZoneInfo>();
544             }
545             helper = mZoneUserBindingHelper;
546         }
547         return helper.getOccupantZones(occupantType);
548     }
549 
assignUserToOccupantZone(@serIdInt int userId, int zoneId)550     private boolean assignUserToOccupantZone(@UserIdInt int userId, int zoneId) {
551         ZoneUserBindingHelper helper = null;
552         synchronized (mLock) {
553             if (mZoneUserBindingHelper == null) {
554                 Slogf.w(TAG, "implementation is not delegated");
555                 return false;
556             }
557             helper = mZoneUserBindingHelper;
558         }
559         return helper.assignUserToOccupantZone(userId, zoneId);
560     }
561 
unassignUserFromOccupantZone(@serIdInt int userId)562     private boolean unassignUserFromOccupantZone(@UserIdInt int userId) {
563         ZoneUserBindingHelper helper = null;
564         synchronized (mLock) {
565             if (mZoneUserBindingHelper == null) {
566                 Slogf.w(TAG, "implementation is not delegated");
567                 return false;
568             }
569             helper = mZoneUserBindingHelper;
570         }
571         return helper.unassignUserFromOccupantZone(userId);
572     }
573 
isPassengerDisplayAvailable()574     private boolean isPassengerDisplayAvailable() {
575         ZoneUserBindingHelper helper = null;
576         synchronized (mLock) {
577             if (mZoneUserBindingHelper == null) {
578                 Slogf.w(TAG, "implementation is not delegated");
579                 return false;
580             }
581             helper = mZoneUserBindingHelper;
582         }
583         return helper.isPassengerDisplayAvailable();
584     }
585 
586     /**
587      * Gets the zone id of the given occupant type.
588      *
589      * @param occupantType The type of an occupant.
590      * @return The zone id of the given occupant type.
591      *         the first found zone, if there are two or more zones.
592      *         {@link CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID}, if not found.
593      */
getZoneId(@arOccupantZoneManager.OccupantTypeEnum int occupantType)594     private int getZoneId(@CarOccupantZoneManager.OccupantTypeEnum int occupantType) {
595         List<CarOccupantZoneManager.OccupantZoneInfo> zoneInfos = getOccupantZones(occupantType);
596         return (zoneInfos.size() > 0)
597                 ? zoneInfos.get(0).zoneId
598                 : CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID;
599     }
600 }
601