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