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