• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.internal.util.VersionUtils.isPlatformVersionAtLeastU;
26 import static com.android.car.user.CarUserService.checkManageUsersPermission;
27 import static com.android.car.user.CarUserService.sendUserSwitchResult;
28 
29 import android.annotation.Nullable;
30 import android.annotation.UserIdInt;
31 import android.app.ActivityManager;
32 import android.car.CarOccupantZoneManager;
33 import android.car.CarOccupantZoneManager.OccupantTypeEnum;
34 import android.car.CarOccupantZoneManager.OccupantZoneInfo;
35 import android.car.IExperimentalCarUserService;
36 import android.car.SyncResultCallback;
37 import android.car.builtin.app.ActivityManagerHelper;
38 import android.car.builtin.os.TraceHelper;
39 import android.car.builtin.os.UserManagerHelper;
40 import android.car.builtin.util.Slogf;
41 import android.car.builtin.util.TimingsTraceLog;
42 import android.car.user.CarUserManager.UserLifecycleListener;
43 import android.car.user.UserCreationResult;
44 import android.car.user.UserLifecycleEventFilter;
45 import android.car.user.UserSwitchResult;
46 import android.car.util.concurrent.AndroidFuture;
47 import android.content.Context;
48 import android.content.res.Resources;
49 import android.os.UserHandle;
50 import android.os.UserManager;
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
createDriver(String name, boolean admin)202     public AndroidFuture<UserCreationResult> createDriver(String name, boolean admin) {
203         checkManageUsersPermission("createDriver");
204         Objects.requireNonNull(name, "name cannot be null");
205 
206         AndroidFuture<UserCreationResult> future = new AndroidFuture<>();
207 
208         ResultCallbackImpl<UserCreationResult> resultResultCallbackImpl = new ResultCallbackImpl<>(
209                 Runnable::run, new SyncResultCallback<>()) {
210             @Override
211             protected void onCompleted(UserCreationResult result) {
212                 if (result == null) {
213                     Slogf.w(TAG, "createDriver(%s, %s) failed", name, admin);
214                 }
215                 future.complete(result);
216                 super.onCompleted(result);
217             }
218         };
219         int flags = 0;
220 
221         if (admin) {
222             if (!(mUserManager.isAdminUser() || mUserManager.isSystemUser())) {
223                 String internalErrorMsg =
224                         "Only admin users and system user can create other admins.";
225                 Slogf.e(TAG, internalErrorMsg);
226                 mCarUserService.sendUserCreationFailure(resultResultCallbackImpl,
227                         UserCreationResult.STATUS_INVALID_REQUEST,
228                         internalErrorMsg);
229                 return future;
230             }
231             flags = UserManagerHelper.FLAG_ADMIN;
232         }
233 
234         mCarUserService.createUser(name,
235                 UserManagerHelper.getDefaultUserTypeForUserInfoFlags(flags), flags, mHalTimeoutMs,
236                 resultResultCallbackImpl, false);
237         return future;
238     }
239 
240     @Override
241     @Nullable
createPassenger(String name, @UserIdInt int driverId)242     public UserHandle createPassenger(String name, @UserIdInt int driverId) {
243         checkManageUsersPermission("createPassenger");
244         Objects.requireNonNull(name, "name cannot be null");
245 
246         UserHandle driver = mUserHandleHelper.getExistingUserHandle(driverId);
247         if (driver == null) {
248             Slogf.w(TAG, "the driver is invalid for driverId: %d", driverId);
249             return null;
250         }
251         if (mUserHandleHelper.isGuestUser(driver)) {
252             Slogf.w(TAG, "a guest driver with id %d cannot create a passenger", driverId);
253             return null;
254         }
255         // createPassenger doesn't use user HAL because user HAL doesn't support profile user yet.
256         UserManager userManager = mContext.createContextAsUser(driver, /* flags= */ 0)
257                 .getSystemService(UserManager.class);
258         UserHandle user = userManager.createProfile(name, UserManager.USER_TYPE_PROFILE_MANAGED,
259                 /* disallowedPackages= */ null);
260 
261         if (user == null) {
262             // Couldn't create user, most likely because there are too many.
263             Slogf.w(TAG, "can't create a profile for user %d", driverId);
264             return null;
265         }
266         return user;
267     }
268 
269     @Override
switchDriver(@serIdInt int driverId, AndroidFuture<UserSwitchResult> receiver)270     public void switchDriver(@UserIdInt int driverId, AndroidFuture<UserSwitchResult> receiver) {
271         checkManageUsersPermission("switchDriver");
272         ResultCallbackImpl<UserSwitchResult> resultCallbackImpl = new ResultCallbackImpl<>(
273                 Runnable::run, new SyncResultCallback<>()) {
274             @Override
275             protected void onCompleted(UserSwitchResult result) {
276                 receiver.complete(result);
277                 super.onCompleted(result);
278             }
279         };
280 
281         if (UserHelperLite.isHeadlessSystemUser(driverId)) {
282             // System user doesn't associate with real person, can not be switched to.
283             Slogf.w(TAG, "switching to system user in headless system user mode is not allowed");
284             sendUserSwitchResult(resultCallbackImpl, /* isLogout= */ false,
285                     UserSwitchResult.STATUS_INVALID_REQUEST);
286             return;
287         }
288         int userSwitchable = mUserManager.getUserSwitchability();
289         if (userSwitchable != UserManager.SWITCHABILITY_STATUS_OK) {
290             Slogf.w(TAG, "current process is not allowed to switch user");
291             sendUserSwitchResult(resultCallbackImpl, /* isLogout= */ false,
292                     UserSwitchResult.STATUS_INVALID_REQUEST);
293             return;
294         }
295         mCarUserService.switchUser(driverId, mHalTimeoutMs, resultCallbackImpl);
296     }
297 
298     /**
299      * Returns all drivers who can occupy the driving zone. Guest users are included in the list.
300      *
301      * @return the list of {@link UserHandle} who can be a driver on the device.
302      */
303     @Override
getAllDrivers()304     public List<UserHandle> getAllDrivers() {
305         checkManageUsersOrDumpPermission("getAllDrivers");
306 
307         return getUsersHandle(
308                 (user) -> !UserHelperLite.isHeadlessSystemUser(user.getIdentifier())
309                         && mUserHandleHelper.isEnabledUser(user)
310                         && !mUserHandleHelper.isManagedProfile(user)
311                         && !mUserHandleHelper.isEphemeralUser(user));
312     }
313 
314     /**
315      * Returns all passengers under the given driver.
316      *
317      * @param driverId User id of a driver.
318      * @return the list of {@link UserHandle} who is a passenger under the given driver.
319      */
320     @Override
getPassengers(@serIdInt int driverId)321     public List<UserHandle> getPassengers(@UserIdInt int driverId) {
322         checkManageUsersOrDumpPermission("getPassengers");
323 
324         return getUsersHandle((user) -> {
325             return !UserHelperLite.isHeadlessSystemUser(user.getIdentifier())
326                     && mUserHandleHelper.isEnabledUser(user)
327                     && mUserHandleHelper.isManagedProfile(user)
328                     && mUserManager.isSameProfileGroup(user, UserHandle.of(driverId));
329         });
330     }
331 
332     @Override
startPassenger(@serIdInt int passengerId, int zoneId)333     public boolean startPassenger(@UserIdInt int passengerId, int zoneId) {
334         checkManageUsersPermission("startPassenger");
335 
336         synchronized (mLock) {
337             if (!ActivityManagerHelper.startUserInBackground(passengerId)) {
338                 Slogf.w(TAG, "could not start passenger");
339                 return false;
340             }
341             if (!assignUserToOccupantZone(passengerId, zoneId)) {
342                 Slogf.w(TAG, "could not assign passenger to zone");
343                 return false;
344             }
345             mLastPassengerId = passengerId;
346         }
347         for (PassengerCallback callback : mPassengerCallbacks) {
348             callback.onPassengerStarted(passengerId, zoneId);
349         }
350         return true;
351     }
352 
353     @Override
stopPassenger(@serIdInt int passengerId)354     public boolean stopPassenger(@UserIdInt int passengerId) {
355         checkManageUsersPermission("stopPassenger");
356 
357         return stopPassengerInternal(passengerId, true);
358     }
359 
360     /** Adds callback to listen to passenger activity events. */
addPassengerCallback(PassengerCallback callback)361     public void addPassengerCallback(PassengerCallback callback) {
362         Objects.requireNonNull(callback, "callback cannot be null");
363         mPassengerCallbacks.add(callback);
364     }
365 
366     /** Removes previously added callback to listen passenger events. */
removePassengerCallback(PassengerCallback callback)367     public void removePassengerCallback(PassengerCallback callback) {
368         Objects.requireNonNull(callback, "callback cannot be null");
369         mPassengerCallbacks.remove(callback);
370     }
371 
372     /** Sets the implementation of ZoneUserBindingHelper. */
setZoneUserBindingHelper(ZoneUserBindingHelper helper)373     public void setZoneUserBindingHelper(ZoneUserBindingHelper helper) {
374         synchronized (mLock) {
375             mZoneUserBindingHelper = helper;
376         }
377     }
378 
stopPassengerInternal(@serIdInt int passengerId, boolean checkCurrentDriver)379     private boolean stopPassengerInternal(@UserIdInt int passengerId, boolean checkCurrentDriver) {
380         synchronized (mLock) {
381             // NULL passengerId means the last passenger.
382             // This is to avoid accessing mPassengerId without obtaining mLock.
383             if (passengerId == UserManagerHelper.USER_NULL) {
384                 passengerId = mLastPassengerId;
385             }
386             UserHandle passenger = mUserHandleHelper.getExistingUserHandle(passengerId);
387             if (passenger == null) {
388                 Slogf.w(TAG, "passenger %d doesn't exist", passengerId);
389                 return false;
390             }
391             if (mLastPassengerId != passengerId) {
392                 Slogf.w(TAG, "passenger %d hasn't been started", passengerId);
393                 return true;
394             }
395             if (checkCurrentDriver) {
396                 int currentUserId = ActivityManager.getCurrentUser();
397                 if (!mUserManager.isSameProfileGroup(passenger, UserHandle.of(currentUserId))) {
398                     Slogf.w(TAG, "passenger %d is not a profile of the current user %d",
399                             passengerId, currentUserId);
400                     return false;
401                 }
402             }
403             // Passenger is a profile, so cannot be stopped through activity manager.
404             // Instead, activities started by the passenger are stopped and the passenger is
405             // unassigned from the zone.
406             ActivityManagerHelper.stopAllTasksForUser(passengerId);
407             if (!unassignUserFromOccupantZone(passengerId)) {
408                 Slogf.w(TAG, "could not unassign user %d from occupant zone", passengerId);
409                 return false;
410             }
411             mLastPassengerId = UserManagerHelper.USER_NULL;
412         }
413         for (PassengerCallback callback : mPassengerCallbacks) {
414             callback.onPassengerStopped(passengerId);
415         }
416         return true;
417     }
418 
onUserSwitching(@serIdInt int fromUserId, @UserIdInt int toUserId)419     private void onUserSwitching(@UserIdInt int fromUserId, @UserIdInt int toUserId) {
420         Slogf.d(TAG, "onUserSwitching() callback from user %d to user %d", fromUserId, toUserId);
421         TimingsTraceLog t = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE);
422         t.traceBegin("onUserSwitching-" + toUserId);
423 
424         stopPassengerInternal(/* passengerId= */ UserManagerHelper.USER_NULL, false);
425 
426         if (mEnablePassengerSupport && isPassengerDisplayAvailable()) {
427             setupPassengerUser();
428             startFirstPassenger(toUserId);
429         }
430         t.traceEnd();
431     }
432 
433     interface UserFilter {
isEligibleUser(UserHandle user)434         boolean isEligibleUser(UserHandle user);
435     }
436 
437     /** Returns all users who are matched by the given filter. */
getUsersHandle(UserFilter filter)438     private List<UserHandle> getUsersHandle(UserFilter filter) {
439         List<UserHandle> users;
440         if (isPlatformVersionAtLeastU()) {
441             users = UserManagerHelper.getUserHandles(mUserManager, /* excludeDying= */ false);
442         } else {
443             users = UserManagerHelper.getUserHandles(mUserManager,
444                     /* excludePartial= */ false, /* excludeDying= */ false,
445                     /* excludePreCreated */ true);
446         }
447         List<UserHandle> usersFiltered = new ArrayList<UserHandle>();
448 
449         for (Iterator<UserHandle> iterator = users.iterator(); iterator.hasNext(); ) {
450             UserHandle user = iterator.next();
451             if (filter.isEligibleUser(user)) {
452                 usersFiltered.add(user);
453             }
454         }
455 
456         return usersFiltered;
457     }
458 
checkManageUsersOrDumpPermission(String message)459     private void checkManageUsersOrDumpPermission(String message) {
460         checkHasAtLeastOnePermissionGranted(mContext, message,
461                 android.Manifest.permission.MANAGE_USERS,
462                 android.Manifest.permission.DUMP);
463     }
464 
getNumberOfManagedProfiles(@serIdInt int userId)465     private int getNumberOfManagedProfiles(@UserIdInt int userId) {
466         List<UserHandle> users;
467         if (isPlatformVersionAtLeastU()) {
468             users = UserManagerHelper.getUserHandles(mUserManager, /* excludeDying= */ false);
469         } else {
470             users = UserManagerHelper.getUserHandles(mUserManager,
471                     /* excludePartial= */ false, /* excludeDying= */ false,
472                     /* excludePreCreated */ true);
473         }
474         // Count all users that are managed profiles of the given user.
475         int managedProfilesCount = 0;
476         for (UserHandle user : users) {
477             if (mUserHandleHelper.isManagedProfile(user)
478                     && mUserManager.isSameProfileGroup(user, UserHandle.of(userId))) {
479                 managedProfilesCount++;
480             }
481         }
482         return managedProfilesCount;
483     }
484 
485     /**
486      * Starts the first passenger of the given driver and assigns the passenger to the front
487      * passenger zone.
488      *
489      * @param driverId User id of the driver.
490      * @return whether it succeeds.
491      */
startFirstPassenger(@serIdInt int driverId)492     private boolean startFirstPassenger(@UserIdInt int driverId) {
493         int zoneId = getAvailablePassengerZone();
494         if (zoneId == CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID) {
495             Slogf.w(TAG, "passenger occupant zone is not found");
496             return false;
497         }
498         List<UserHandle> passengers = getPassengers(driverId);
499         if (passengers.size() < 1) {
500             Slogf.w(TAG, "passenger is not found for driver %d", driverId);
501             return false;
502         }
503         // Only one passenger is supported. If there are two or more passengers, the first passenger
504         // is chosen.
505         int passengerId = passengers.get(0).getIdentifier();
506         if (!startPassenger(passengerId, zoneId)) {
507             Slogf.w(TAG, "cannot start passenger %d", passengerId);
508             return false;
509         }
510         return true;
511     }
512 
getAvailablePassengerZone()513     private int getAvailablePassengerZone() {
514         int[] occupantTypes = new int[] {CarOccupantZoneManager.OCCUPANT_TYPE_FRONT_PASSENGER,
515                 CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER};
516         for (int occupantType : occupantTypes) {
517             int zoneId = getZoneId(occupantType);
518             if (zoneId != CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID) {
519                 return zoneId;
520             }
521         }
522         return CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID;
523     }
524 
525     /**
526      * Creates a new passenger user when there is no passenger user.
527      */
setupPassengerUser()528     private void setupPassengerUser() {
529         int currentUser = ActivityManager.getCurrentUser();
530         int profileCount = getNumberOfManagedProfiles(currentUser);
531         if (profileCount > 0) {
532             Slogf.w(TAG, "max profile of user %d is exceeded: current profile count is %d",
533                     currentUser, profileCount);
534             return;
535         }
536         // TODO(b/140311342): Use resource string for the default passenger name.
537         UserHandle passenger = createPassenger("Passenger", currentUser);
538         if (passenger == null) {
539             // Couldn't create user, most likely because there are too many.
540             Slogf.w(TAG, "cannot create a passenger user");
541             return;
542         }
543     }
544 
getOccupantZones(@ccupantTypeEnum int occupantType)545     private List<OccupantZoneInfo> getOccupantZones(@OccupantTypeEnum int occupantType) {
546         ZoneUserBindingHelper helper = null;
547         synchronized (mLock) {
548             if (mZoneUserBindingHelper == null) {
549                 Slogf.w(TAG, "implementation is not delegated");
550                 return new ArrayList<OccupantZoneInfo>();
551             }
552             helper = mZoneUserBindingHelper;
553         }
554         return helper.getOccupantZones(occupantType);
555     }
556 
assignUserToOccupantZone(@serIdInt int userId, int zoneId)557     private boolean assignUserToOccupantZone(@UserIdInt int userId, int zoneId) {
558         ZoneUserBindingHelper helper = null;
559         synchronized (mLock) {
560             if (mZoneUserBindingHelper == null) {
561                 Slogf.w(TAG, "implementation is not delegated");
562                 return false;
563             }
564             helper = mZoneUserBindingHelper;
565         }
566         return helper.assignUserToOccupantZone(userId, zoneId);
567     }
568 
unassignUserFromOccupantZone(@serIdInt int userId)569     private boolean unassignUserFromOccupantZone(@UserIdInt int userId) {
570         ZoneUserBindingHelper helper = null;
571         synchronized (mLock) {
572             if (mZoneUserBindingHelper == null) {
573                 Slogf.w(TAG, "implementation is not delegated");
574                 return false;
575             }
576             helper = mZoneUserBindingHelper;
577         }
578         return helper.unassignUserFromOccupantZone(userId);
579     }
580 
isPassengerDisplayAvailable()581     private boolean isPassengerDisplayAvailable() {
582         ZoneUserBindingHelper helper = null;
583         synchronized (mLock) {
584             if (mZoneUserBindingHelper == null) {
585                 Slogf.w(TAG, "implementation is not delegated");
586                 return false;
587             }
588             helper = mZoneUserBindingHelper;
589         }
590         return helper.isPassengerDisplayAvailable();
591     }
592 
593     /**
594      * Gets the zone id of the given occupant type.
595      *
596      * @param occupantType The type of an occupant.
597      * @return The zone id of the given occupant type.
598      *         the first found zone, if there are two or more zones.
599      *         {@link CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID}, if not found.
600      */
getZoneId(@arOccupantZoneManager.OccupantTypeEnum int occupantType)601     private int getZoneId(@CarOccupantZoneManager.OccupantTypeEnum int occupantType) {
602         List<CarOccupantZoneManager.OccupantZoneInfo> zoneInfos = getOccupantZones(occupantType);
603         return (zoneInfos.size() > 0)
604                 ? zoneInfos.get(0).zoneId
605                 : CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID;
606     }
607 }
608