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