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