1 /* 2 * Copyright (C) 2019 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; 18 19 import static android.car.builtin.view.DisplayHelper.INVALID_PORT; 20 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPING; 21 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING; 22 import static android.view.Display.STATE_ON; 23 24 import static com.android.car.CarServiceUtils.getHandlerThread; 25 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 26 import static com.android.car.internal.util.VersionUtils.isPlatformVersionAtLeastU; 27 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.annotation.SuppressLint; 31 import android.annotation.UserIdInt; 32 import android.app.ActivityManager; 33 import android.car.Car; 34 import android.car.CarInfoManager; 35 import android.car.CarOccupantZoneManager; 36 import android.car.CarOccupantZoneManager.DisplayTypeEnum; 37 import android.car.CarOccupantZoneManager.OccupantTypeEnum; 38 import android.car.CarOccupantZoneManager.OccupantZoneInfo; 39 import android.car.ICarOccupantZone; 40 import android.car.ICarOccupantZoneCallback; 41 import android.car.PlatformVersion; 42 import android.car.VehicleAreaSeat; 43 import android.car.builtin.util.Slogf; 44 import android.car.builtin.view.DisplayHelper; 45 import android.car.input.CarInputManager; 46 import android.car.media.CarAudioManager; 47 import android.car.user.CarUserManager.UserLifecycleListener; 48 import android.car.user.UserLifecycleEventFilter; 49 import android.content.ComponentName; 50 import android.content.Context; 51 import android.content.pm.PackageManager; 52 import android.content.res.Resources; 53 import android.hardware.display.DisplayManager; 54 import android.os.Binder; 55 import android.os.Handler; 56 import android.os.Looper; 57 import android.os.RemoteCallbackList; 58 import android.os.RemoteException; 59 import android.os.UserHandle; 60 import android.os.UserManager; 61 import android.util.ArrayMap; 62 import android.util.ArraySet; 63 import android.util.Log; 64 import android.util.SparseArray; 65 import android.util.SparseIntArray; 66 import android.view.Display; 67 68 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 69 import com.android.car.internal.util.IndentingPrintWriter; 70 import com.android.car.internal.util.IntArray; 71 import com.android.car.user.CarUserService; 72 import com.android.car.user.ExperimentalCarUserService; 73 import com.android.car.user.ExperimentalCarUserService.ZoneUserBindingHelper; 74 import com.android.car.user.UserHandleHelper; 75 import com.android.internal.annotations.GuardedBy; 76 import com.android.internal.annotations.VisibleForTesting; 77 import com.android.internal.util.Preconditions; 78 79 import java.util.ArrayList; 80 import java.util.Arrays; 81 import java.util.List; 82 import java.util.Objects; 83 84 /** 85 * Service to implement CarOccupantZoneManager API. 86 */ 87 public final class CarOccupantZoneService extends ICarOccupantZone.Stub 88 implements CarServiceBase { 89 90 private static final String TAG = CarLog.tagFor(CarOccupantZoneService.class); 91 private static final String ALL_COMPONENTS = "*"; 92 private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG); 93 94 private static final String HANDLER_THREAD_NAME = "CarOccupantZoneService"; 95 96 private static final int[] EMPTY_INPUT_SUPPORT_TYPES = new int[0]; 97 98 private final Object mLock = new Object(); 99 private final Context mContext; 100 private final DisplayManager mDisplayManager; 101 private final UserManager mUserManager; 102 103 private final boolean mEnableProfileUserAssignmentForMultiDisplay; 104 105 private boolean mEnableSourcePreferred; 106 private ArrayList<ComponentName> mSourcePreferredComponents; 107 108 /** 109 * Stores android user id of profile users for the current user. 110 */ 111 @GuardedBy("mLock") 112 private final ArraySet<Integer> mProfileUsers = new ArraySet<>(); 113 114 /** key: zone id */ 115 @GuardedBy("mLock") 116 private final SparseArray<OccupantZoneInfo> mOccupantsConfig = new SparseArray<>(); 117 118 /** 119 * The config of a display identified by occupant zone id and display type. 120 */ 121 public static final class DisplayConfig { 122 public final int displayType; 123 public final int occupantZoneId; 124 public final int[] inputTypes; 125 DisplayConfig(int displayType, int occupantZoneId, IntArray inputTypes)126 DisplayConfig(int displayType, int occupantZoneId, IntArray inputTypes) { 127 this.displayType = displayType; 128 this.occupantZoneId = occupantZoneId; 129 if (inputTypes == null && Car.getPlatformVersion().isAtLeast( 130 PlatformVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_0)) { 131 Slogf.w(TAG, "No input type was defined for displayType:%d " 132 + " and occupantZoneId:%d", displayType, occupantZoneId); 133 } 134 this.inputTypes = inputTypes == null ? EMPTY_INPUT_SUPPORT_TYPES : inputTypes.toArray(); 135 } 136 137 @Override toString()138 public String toString() { 139 // do not include type as this is only used for dump 140 StringBuilder b = new StringBuilder(64); 141 b.append("{displayType="); 142 b.append(Integer.toHexString(displayType)); 143 b.append(" occupantZoneId="); 144 b.append(occupantZoneId); 145 b.append(" inputTypes="); 146 b.append(Arrays.toString(inputTypes)); 147 b.append("}"); 148 return b.toString(); 149 } 150 } 151 152 /** key: display port address */ 153 @GuardedBy("mLock") 154 private final SparseArray<DisplayConfig> mDisplayPortConfigs = new SparseArray<>(); 155 156 /** key: displayUniqueId */ 157 @GuardedBy("mLock") 158 private final ArrayMap<String, DisplayConfig> mDisplayUniqueIdConfigs = new ArrayMap<>(); 159 160 /** key: audio zone id */ 161 @GuardedBy("mLock") 162 private final SparseIntArray mAudioZoneIdToOccupantZoneIdMapping = new SparseIntArray(); 163 164 @VisibleForTesting 165 static class DisplayInfo { 166 public final Display display; 167 public final int displayType; 168 DisplayInfo(Display display, int displayType)169 DisplayInfo(Display display, int displayType) { 170 this.display = display; 171 this.displayType = displayType; 172 } 173 174 @Override toString()175 public String toString() { 176 // do not include type as this is only used for dump 177 StringBuilder b = new StringBuilder(64); 178 b.append("{displayId="); 179 b.append(display.getDisplayId()); 180 b.append(" displayType="); 181 b.append(displayType); 182 b.append("}"); 183 return b.toString(); 184 } 185 } 186 187 @VisibleForTesting 188 static class OccupantConfig { 189 public int userId = CarOccupantZoneManager.INVALID_USER_ID; 190 public final ArrayList<DisplayInfo> displayInfos = new ArrayList<>(); 191 public int audioZoneId = CarAudioManager.INVALID_AUDIO_ZONE; 192 193 @Override toString()194 public String toString() { 195 // do not include type as this is only used for dump 196 StringBuilder b = new StringBuilder(128); 197 b.append("{userId="); 198 b.append(userId); 199 b.append(" displays="); 200 for (int i = 0; i < displayInfos.size(); i++) { 201 b.append(displayInfos.get(i).toString()); 202 } 203 b.append(" audioZoneId="); 204 if (audioZoneId != CarAudioManager.INVALID_AUDIO_ZONE) { 205 b.append(audioZoneId); 206 } else { 207 b.append("none"); 208 } 209 b.append("}"); 210 return b.toString(); 211 } 212 } 213 214 /** key : zoneId */ 215 @GuardedBy("mLock") 216 private final SparseArray<OccupantConfig> mActiveOccupantConfigs = new SparseArray<>(); 217 218 @GuardedBy("mLock") 219 private int mDriverZoneId = OccupantZoneInfo.INVALID_ZONE_ID; 220 221 @VisibleForTesting 222 final UserLifecycleListener mUserLifecycleListener = event -> { 223 if (DBG) Slogf.d(TAG, "onEvent(%s)", event); 224 handleUserChange(); 225 }; 226 227 final ExperimentalCarUserService.PassengerCallback mPassengerCallback = 228 new ExperimentalCarUserService.PassengerCallback() { 229 @Override 230 public void onPassengerStarted(@UserIdInt int passengerId, int zoneId) { 231 handlePassengerStarted(); 232 } 233 234 @Override 235 public void onPassengerStopped(@UserIdInt int passengerId) { 236 handlePassengerStopped(); 237 } 238 }; 239 240 @VisibleForTesting 241 final DisplayManager.DisplayListener mDisplayListener = 242 new DisplayManager.DisplayListener() { 243 @Override 244 public void onDisplayAdded(int displayId) { 245 handleDisplayChange(); 246 } 247 248 @Override 249 public void onDisplayRemoved(int displayId) { 250 handleDisplayChange(); 251 } 252 253 @Override 254 public void onDisplayChanged(int displayId) { 255 // nothing to do 256 } 257 }; 258 259 private final RemoteCallbackList<ICarOccupantZoneCallback> mClientCallbacks = 260 new RemoteCallbackList<>(); 261 262 @GuardedBy("mLock") 263 private int mDriverSeat = VehicleAreaSeat.SEAT_UNKNOWN; 264 private final UserHandleHelper mUserHandleHelper; 265 266 final Handler mHandler = new Handler(getHandlerThread(HANDLER_THREAD_NAME).getLooper()); 267 CarOccupantZoneService(Context context)268 public CarOccupantZoneService(Context context) { 269 this(context, context.getSystemService(DisplayManager.class), 270 context.getSystemService(UserManager.class), 271 context.getResources().getBoolean( 272 R.bool.enableProfileUserAssignmentForMultiDisplay) 273 && context.getPackageManager().hasSystemFeature( 274 PackageManager.FEATURE_MANAGED_USERS), 275 new UserHandleHelper(context, context.getSystemService(UserManager.class))); 276 } 277 278 @VisibleForTesting CarOccupantZoneService(Context context, DisplayManager displayManager, UserManager userManager, boolean enableProfileUserAssignmentForMultiDisplay, UserHandleHelper userHandleHelper)279 public CarOccupantZoneService(Context context, DisplayManager displayManager, 280 UserManager userManager, boolean enableProfileUserAssignmentForMultiDisplay, 281 UserHandleHelper userHandleHelper) { 282 mContext = context; 283 mDisplayManager = displayManager; 284 mUserManager = userManager; 285 mEnableProfileUserAssignmentForMultiDisplay = enableProfileUserAssignmentForMultiDisplay; 286 mUserHandleHelper = userHandleHelper; 287 } 288 289 @Override init()290 public void init() { 291 // This does not require connection as binder will be passed directly. 292 Car car = new Car(mContext, /* service= */null, /* handler= */ null); 293 CarInfoManager infoManager = new CarInfoManager(car, CarLocalServices.getService( 294 CarPropertyService.class)); 295 int driverSeat = infoManager.getDriverSeat(); 296 synchronized (mLock) { 297 mDriverSeat = driverSeat; 298 parseOccupantZoneConfigsLocked(); 299 parseDisplayConfigsLocked(); 300 handleActiveDisplaysLocked(); 301 handleAudioZoneChangesLocked(); 302 handleUserChangesLocked(); 303 } 304 CarUserService userService = CarLocalServices.getService(CarUserService.class); 305 UserLifecycleEventFilter userEventFilter = new UserLifecycleEventFilter.Builder() 306 .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING).addEventType( 307 USER_LIFECYCLE_EVENT_TYPE_STOPPING).build(); 308 userService.addUserLifecycleListener(userEventFilter, mUserLifecycleListener); 309 ExperimentalCarUserService experimentalUserService = 310 CarLocalServices.getService(ExperimentalCarUserService.class); 311 if (experimentalUserService != null) { 312 experimentalUserService.addPassengerCallback(mPassengerCallback); 313 } 314 mDisplayManager.registerDisplayListener(mDisplayListener, 315 new Handler(Looper.getMainLooper())); 316 ZoneUserBindingHelper helper = new ZoneUserBindingHelper() { 317 @Override 318 @NonNull 319 public List<OccupantZoneInfo> getOccupantZones(@OccupantTypeEnum int occupantType) { 320 List<OccupantZoneInfo> zones = new ArrayList<OccupantZoneInfo>(); 321 for (OccupantZoneInfo ozi : getAllOccupantZones()) { 322 if (ozi.occupantType == occupantType) { 323 zones.add(ozi); 324 } 325 } 326 return zones; 327 } 328 329 @Override 330 public boolean assignUserToOccupantZone(@UserIdInt int userId, int zoneId) { 331 // Check if the user is already assigned to the other zone. 332 synchronized (mLock) { 333 int userZoneId = getZoneIdForUserIdLocked(userId); 334 if (userZoneId != OccupantZoneInfo.INVALID_ZONE_ID 335 && mActiveOccupantConfigs.keyAt(userZoneId) != zoneId) { 336 Slogf.w(TAG, "Cannot assign user to two different zones simultaneously"); 337 return false; 338 } 339 OccupantConfig zoneConfig = mActiveOccupantConfigs.get(zoneId); 340 if (zoneConfig == null) { 341 Slogf.w(TAG, "cannot find the zone(%d)", zoneId); 342 return false; 343 } 344 if (zoneConfig.userId != CarOccupantZoneManager.INVALID_USER_ID 345 && zoneConfig.userId != userId) { 346 Slogf.w(TAG, "other user already occupies the zone(%d)", zoneId); 347 return false; 348 } 349 zoneConfig.userId = userId; 350 return true; 351 } 352 } 353 354 @Override 355 public boolean unassignUserFromOccupantZone(@UserIdInt int userId) { 356 synchronized (mLock) { 357 for (int i = 0; i < mActiveOccupantConfigs.size(); ++i) { 358 OccupantConfig config = mActiveOccupantConfigs.valueAt(i); 359 if (config.userId == userId) { 360 config.userId = CarOccupantZoneManager.INVALID_USER_ID; 361 break; 362 } 363 } 364 return true; 365 } 366 } 367 368 @Override 369 public boolean isPassengerDisplayAvailable() { 370 for (OccupantZoneInfo ozi : getAllOccupantZones()) { 371 if (getDisplayForOccupant(ozi.zoneId, 372 CarOccupantZoneManager.DISPLAY_TYPE_MAIN) != Display.INVALID_DISPLAY 373 && ozi.occupantType != CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { 374 return true; 375 } 376 } 377 return false; 378 } 379 }; 380 if (experimentalUserService != null) { 381 experimentalUserService.setZoneUserBindingHelper(helper); 382 } 383 384 CarServiceHelperWrapper.getInstance().runOnConnection(() -> doSyncWithCarServiceHelper( 385 /* updateDisplay= */ true, /* updateUser= */ true, /* updateConfig= */ true)); 386 } 387 388 @Override release()389 public void release() { 390 mDisplayManager.unregisterDisplayListener(mDisplayListener); 391 CarUserService userService = CarLocalServices.getService(CarUserService.class); 392 userService.removeUserLifecycleListener(mUserLifecycleListener); 393 ExperimentalCarUserService experimentalUserService = 394 CarLocalServices.getService(ExperimentalCarUserService.class); 395 if (experimentalUserService != null) { 396 experimentalUserService.removePassengerCallback(mPassengerCallback); 397 } 398 synchronized (mLock) { 399 mOccupantsConfig.clear(); 400 mDisplayPortConfigs.clear(); 401 mDisplayUniqueIdConfigs.clear(); 402 mAudioZoneIdToOccupantZoneIdMapping.clear(); 403 mActiveOccupantConfigs.clear(); 404 } 405 } 406 407 /** Return cloned mOccupantsConfig for testing */ 408 @VisibleForTesting 409 @NonNull getOccupantsConfig()410 public SparseArray<OccupantZoneInfo> getOccupantsConfig() { 411 synchronized (mLock) { 412 return mOccupantsConfig.clone(); 413 } 414 } 415 416 /** Return cloned mDisplayPortConfigs for testing */ 417 @VisibleForTesting 418 @NonNull getDisplayPortConfigs()419 public SparseArray<DisplayConfig> getDisplayPortConfigs() { 420 synchronized (mLock) { 421 return mDisplayPortConfigs.clone(); 422 } 423 } 424 425 /** Return cloned mDisplayUniqueIdConfigs for testing */ 426 @VisibleForTesting 427 @NonNull getDisplayUniqueIdConfigs()428 ArrayMap<String, DisplayConfig> getDisplayUniqueIdConfigs() { 429 synchronized (mLock) { 430 return new ArrayMap<>(mDisplayUniqueIdConfigs); 431 } 432 } 433 434 /** Return cloned mAudioConfigs for testing */ 435 @VisibleForTesting 436 @NonNull getAudioConfigs()437 SparseIntArray getAudioConfigs() { 438 synchronized (mLock) { 439 return mAudioZoneIdToOccupantZoneIdMapping.clone(); 440 } 441 } 442 443 /** Return cloned mActiveOccupantConfigs for testing */ 444 @VisibleForTesting 445 @NonNull getActiveOccupantConfigs()446 public SparseArray<OccupantConfig> getActiveOccupantConfigs() { 447 synchronized (mLock) { 448 return mActiveOccupantConfigs.clone(); 449 } 450 } 451 452 @Override 453 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)454 public void dump(IndentingPrintWriter writer) { 455 writer.println("*OccupantZoneService*"); 456 synchronized (mLock) { 457 writer.println("**mOccupantsConfig**"); 458 for (int i = 0; i < mOccupantsConfig.size(); ++i) { 459 writer.println(" zoneId=" + mOccupantsConfig.keyAt(i) 460 + " info=" + mOccupantsConfig.valueAt(i)); 461 } 462 writer.println("**mDisplayConfigs**"); 463 for (int i = 0; i < mDisplayPortConfigs.size(); ++i) { 464 writer.println(" port=" + mDisplayPortConfigs.keyAt(i) 465 + " config=" + mDisplayPortConfigs.valueAt(i)); 466 } 467 for (int i = 0; i < mDisplayUniqueIdConfigs.size(); ++i) { 468 writer.println(" uniqueId=" + mDisplayUniqueIdConfigs.keyAt(i) 469 + " config=" + mDisplayUniqueIdConfigs.valueAt(i)); 470 } 471 writer.println("**mAudioZoneIdToOccupantZoneIdMapping**"); 472 for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) { 473 int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index); 474 writer.println(" audioZoneId=" + Integer.toHexString(audioZoneId) 475 + " zoneId=" + mAudioZoneIdToOccupantZoneIdMapping.valueAt(index)); 476 } 477 writer.println("**mActiveOccupantConfigs**"); 478 for (int i = 0; i < mActiveOccupantConfigs.size(); ++i) { 479 writer.println(" zoneId=" + mActiveOccupantConfigs.keyAt(i) 480 + " config=" + mActiveOccupantConfigs.valueAt(i)); 481 } 482 writer.println("mEnableProfileUserAssignmentForMultiDisplay:" 483 + mEnableProfileUserAssignmentForMultiDisplay); 484 writer.println("mEnableSourcePreferred:" 485 + mEnableSourcePreferred); 486 writer.append("mSourcePreferredComponents: ["); 487 if (mSourcePreferredComponents != null) { 488 for (int i = 0; i < mSourcePreferredComponents.size(); ++i) { 489 if (i > 0) writer.append(' '); 490 writer.append(mSourcePreferredComponents.get(i).toString()); 491 } 492 } 493 writer.println(']'); 494 writer.println("hasDriverZone: " + hasDriverZone()); 495 } 496 } 497 498 @Override getAllOccupantZones()499 public List<OccupantZoneInfo> getAllOccupantZones() { 500 synchronized (mLock) { 501 List<OccupantZoneInfo> infos = new ArrayList<>(); 502 for (int i = 0; i < mActiveOccupantConfigs.size(); ++i) { 503 int zoneId = mActiveOccupantConfigs.keyAt(i); 504 // no need for deep copy as OccupantZoneInfo itself is static. 505 infos.add(mOccupantsConfig.get(zoneId)); 506 } 507 return infos; 508 } 509 } 510 511 @Override getAllDisplaysForOccupantZone(int occupantZoneId)512 public int[] getAllDisplaysForOccupantZone(int occupantZoneId) { 513 synchronized (mLock) { 514 OccupantConfig config = mActiveOccupantConfigs.get(occupantZoneId); 515 if (config == null) { 516 return new int[0]; 517 } 518 int[] displayIds = new int[config.displayInfos.size()]; 519 for (int i = 0; i < config.displayInfos.size(); i++) { 520 displayIds[i] = config.displayInfos.get(i).display.getDisplayId(); 521 } 522 return displayIds; 523 } 524 } 525 526 /** 527 * Checks if all displays for a given OccupantZone are on. 528 * 529 * @param occupantZoneId indicates which OccupantZone's displays to check 530 * @return whether all displays for a given OccupantZone are on 531 */ areDisplaysOnForOccupantZone(int occupantZoneId)532 public boolean areDisplaysOnForOccupantZone(int occupantZoneId) { 533 synchronized (mLock) { 534 OccupantConfig config = mActiveOccupantConfigs.get(occupantZoneId); 535 if (config == null) { 536 return false; 537 } 538 for (int i = 0; i < config.displayInfos.size(); i++) { 539 if (config.displayInfos.get(i).display.getState() != STATE_ON) { 540 return false; 541 } 542 } 543 544 return true; 545 } 546 } 547 548 @Override getDisplayForOccupant(int occupantZoneId, int displayType)549 public int getDisplayForOccupant(int occupantZoneId, int displayType) { 550 synchronized (mLock) { 551 OccupantConfig config = mActiveOccupantConfigs.get(occupantZoneId); 552 if (config == null) { 553 return Display.INVALID_DISPLAY; 554 } 555 for (int i = 0; i < config.displayInfos.size(); i++) { 556 if (displayType == config.displayInfos.get(i).displayType) { 557 return config.displayInfos.get(i).display.getDisplayId(); 558 } 559 } 560 } 561 return Display.INVALID_DISPLAY; 562 } 563 getAllDisplayIdsForDriver(int displayType)564 public IntArray getAllDisplayIdsForDriver(int displayType) { 565 synchronized (mLock) { 566 OccupantConfig config = mActiveOccupantConfigs.get(mDriverZoneId); 567 if (config == null) { 568 return new IntArray(0); 569 } 570 IntArray displayIds = new IntArray(config.displayInfos.size()); 571 for (int i = 0; i < config.displayInfos.size(); i++) { 572 DisplayInfo displayInfo = config.displayInfos.get(i); 573 if (displayInfo.displayType == displayType) { 574 displayIds.add(displayInfo.display.getDisplayId()); 575 } 576 } 577 return displayIds; 578 } 579 } 580 581 @Override getDisplayIdForDriver(@isplayTypeEnum int displayType)582 public int getDisplayIdForDriver(@DisplayTypeEnum int displayType) { 583 enforcePermission(Car.ACCESS_PRIVATE_DISPLAY_ID); 584 synchronized (mLock) { 585 int driverUserId = getDriverUserId(); 586 DisplayInfo displayInfo = findDisplayForDriverLocked(driverUserId, displayType); 587 if (displayInfo == null) { 588 return Display.INVALID_DISPLAY; 589 } 590 return displayInfo.display.getDisplayId(); 591 } 592 } 593 594 @GuardedBy("mLock") 595 @Nullable findDisplayForDriverLocked(@serIdInt int driverUserId, @DisplayTypeEnum int displayType)596 private DisplayInfo findDisplayForDriverLocked(@UserIdInt int driverUserId, 597 @DisplayTypeEnum int displayType) { 598 for (OccupantZoneInfo zoneInfo : getAllOccupantZones()) { 599 if (zoneInfo.occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { 600 OccupantConfig config = mActiveOccupantConfigs.get(zoneInfo.zoneId); 601 if (config == null) { 602 //No active display for zone, just continue... 603 continue; 604 } 605 606 if (config.userId == driverUserId) { 607 for (DisplayInfo displayInfo : config.displayInfos) { 608 if (displayInfo.displayType == displayType) { 609 return displayInfo; 610 } 611 } 612 } 613 } 614 } 615 return null; 616 } 617 618 @Override getAudioZoneIdForOccupant(int occupantZoneId)619 public int getAudioZoneIdForOccupant(int occupantZoneId) { 620 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 621 synchronized (mLock) { 622 OccupantConfig config = mActiveOccupantConfigs.get(occupantZoneId); 623 if (config != null) { 624 return config.audioZoneId; 625 } 626 // check if the occupant id exist at all 627 if (!mOccupantsConfig.contains(occupantZoneId)) { 628 return CarAudioManager.INVALID_AUDIO_ZONE; 629 } 630 // Exist but not active 631 return getAudioZoneIdForOccupantLocked(occupantZoneId); 632 } 633 } 634 635 @GuardedBy("mLock") getAudioZoneIdForOccupantLocked(int occupantZoneId)636 private int getAudioZoneIdForOccupantLocked(int occupantZoneId) { 637 for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) { 638 int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index); 639 if (occupantZoneId == mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId)) { 640 return audioZoneId; 641 } 642 } 643 return CarAudioManager.INVALID_AUDIO_ZONE; 644 } 645 646 @Override getOccupantForAudioZoneId(int audioZoneId)647 public OccupantZoneInfo getOccupantForAudioZoneId(int audioZoneId) { 648 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 649 synchronized (mLock) { 650 int occupantZoneId = mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId, 651 OccupantZoneInfo.INVALID_ZONE_ID); 652 if (occupantZoneId == OccupantZoneInfo.INVALID_ZONE_ID) { 653 return null; 654 } 655 // To support headless zones return the occupant configuration. 656 return mOccupantsConfig.get(occupantZoneId); 657 } 658 } 659 660 /** 661 * Finds the DisplayConfig for a logical display id. 662 */ 663 @Nullable findDisplayConfigForDisplayId(int displayId)664 public DisplayConfig findDisplayConfigForDisplayId(int displayId) { 665 synchronized (mLock) { 666 return findDisplayConfigForDisplayIdLocked(displayId); 667 } 668 } 669 670 /** 671 * Finds the DisplayConfig for a physical display port. 672 */ 673 @Nullable findDisplayConfigForPort(int portAddress)674 public DisplayConfig findDisplayConfigForPort(int portAddress) { 675 synchronized (mLock) { 676 return findDisplayConfigForPortLocked(portAddress); 677 } 678 } 679 680 @GuardedBy("mLock") 681 @Nullable findDisplayConfigForDisplayIdLocked(int displayId)682 private DisplayConfig findDisplayConfigForDisplayIdLocked(int displayId) { 683 Display display = mDisplayManager.getDisplay(displayId); 684 if (display == null) { 685 return null; 686 } 687 return findDisplayConfigForDisplayLocked(display); 688 } 689 690 @GuardedBy("mLock") 691 @Nullable findDisplayConfigForDisplayLocked(Display display)692 private DisplayConfig findDisplayConfigForDisplayLocked(Display display) { 693 int portAddress = DisplayHelper.getPhysicalPort(display); 694 if (portAddress != INVALID_PORT) { 695 DisplayConfig config = mDisplayPortConfigs.get(portAddress); 696 if (config != null) { 697 return config; 698 } 699 } 700 return mDisplayUniqueIdConfigs.get(DisplayHelper.getUniqueId(display)); 701 } 702 703 @GuardedBy("mLock") 704 @Nullable findDisplayConfigForPortLocked(int portAddress)705 private DisplayConfig findDisplayConfigForPortLocked(int portAddress) { 706 return portAddress != INVALID_PORT ? mDisplayPortConfigs.get(portAddress) : null; 707 } 708 709 @Override getDisplayType(int displayId)710 public int getDisplayType(int displayId) { 711 synchronized (mLock) { 712 DisplayConfig config = findDisplayConfigForDisplayIdLocked(displayId); 713 if (config != null) { 714 return config.displayType; 715 } 716 } 717 return CarOccupantZoneManager.DISPLAY_TYPE_UNKNOWN; 718 } 719 720 @Override getUserForOccupant(int occupantZoneId)721 public int getUserForOccupant(int occupantZoneId) { 722 synchronized (mLock) { 723 OccupantConfig config = mActiveOccupantConfigs.get(occupantZoneId); 724 if (config == null) { 725 return CarOccupantZoneManager.INVALID_USER_ID; 726 } 727 return config.userId; 728 } 729 } 730 731 @Override getOccupantZoneIdForUserId(@serIdInt int userId)732 public int getOccupantZoneIdForUserId(@UserIdInt int userId) { 733 synchronized (mLock) { 734 for (int i = 0; i < mActiveOccupantConfigs.size(); ++i) { 735 OccupantConfig config = mActiveOccupantConfigs.valueAt(i); 736 if (config.userId == userId) { 737 return mActiveOccupantConfigs.keyAt(i); 738 } 739 } 740 Slogf.w(TAG, "Could not find occupantZoneId for userId%d returning invalid " 741 + "occupant zone id %d", userId, OccupantZoneInfo.INVALID_ZONE_ID); 742 return OccupantZoneInfo.INVALID_ZONE_ID; 743 } 744 } 745 746 @Override getOccupantZoneForDisplayId(int displayId)747 public OccupantZoneInfo getOccupantZoneForDisplayId(int displayId) { 748 synchronized (mLock) { 749 DisplayConfig displayConfig = findDisplayConfigForDisplayIdLocked(displayId); 750 if (displayConfig == null) { 751 Slogf.w(TAG, "getOccupantZoneForDisplayId: Could not find DisplayConfig for " 752 + "display Id %d", displayId); 753 return null; 754 } 755 756 int occupantZoneId = displayConfig.occupantZoneId; 757 if (occupantZoneId == OccupantZoneInfo.INVALID_ZONE_ID) { 758 Slogf.w(TAG, "getOccupantZoneForDisplayId: Got invalid occupant zone id from " 759 + "DisplayConfig: %s", displayConfig); 760 return null; 761 } 762 763 return mOccupantsConfig.get(occupantZoneId); 764 } 765 } 766 767 /** 768 * returns the current driver user id. 769 */ getDriverUserId()770 public @UserIdInt int getDriverUserId() { 771 return getCurrentUser(); 772 } 773 774 /** 775 * Sets the mapping for audio zone id to occupant zone id. 776 * 777 * @param audioZoneIdToOccupantZoneMapping map for audio zone id, where key is the audio zone id 778 * and value is the occupant zone id 779 */ setAudioZoneIdsForOccupantZoneIds( @onNull SparseIntArray audioZoneIdToOccupantZoneMapping)780 public void setAudioZoneIdsForOccupantZoneIds( 781 @NonNull SparseIntArray audioZoneIdToOccupantZoneMapping) { 782 Objects.requireNonNull(audioZoneIdToOccupantZoneMapping, 783 "audioZoneIdToOccupantZoneMapping can not be null"); 784 synchronized (mLock) { 785 validateOccupantZoneIdsLocked(audioZoneIdToOccupantZoneMapping); 786 mAudioZoneIdToOccupantZoneIdMapping.clear(); 787 for (int index = 0; index < audioZoneIdToOccupantZoneMapping.size(); index++) { 788 int audioZoneId = audioZoneIdToOccupantZoneMapping.keyAt(index); 789 mAudioZoneIdToOccupantZoneIdMapping.put(audioZoneId, 790 audioZoneIdToOccupantZoneMapping.get(audioZoneId)); 791 } 792 //If there are any active displays for the zone send change event 793 handleAudioZoneChangesLocked(); 794 } 795 sendConfigChangeEvent(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_AUDIO); 796 } 797 798 @GuardedBy("mLock") validateOccupantZoneIdsLocked(SparseIntArray audioZoneIdToOccupantZoneMapping)799 private void validateOccupantZoneIdsLocked(SparseIntArray audioZoneIdToOccupantZoneMapping) { 800 for (int i = 0; i < audioZoneIdToOccupantZoneMapping.size(); i++) { 801 int occupantZoneId = 802 audioZoneIdToOccupantZoneMapping.get(audioZoneIdToOccupantZoneMapping.keyAt(i)); 803 if (!mOccupantsConfig.contains(occupantZoneId)) { 804 throw new IllegalArgumentException("occupantZoneId " + occupantZoneId 805 + " does not exist."); 806 } 807 } 808 } 809 810 @Override registerCallback(ICarOccupantZoneCallback callback)811 public void registerCallback(ICarOccupantZoneCallback callback) { 812 mClientCallbacks.register(callback); 813 } 814 815 @Override unregisterCallback(ICarOccupantZoneCallback callback)816 public void unregisterCallback(ICarOccupantZoneCallback callback) { 817 mClientCallbacks.unregister(callback); 818 } 819 820 @Override assignProfileUserToOccupantZone(int occupantZoneId, @UserIdInt int userId)821 public boolean assignProfileUserToOccupantZone(int occupantZoneId, @UserIdInt int userId) { 822 CarServiceUtils.assertAnyPermission(mContext, android.Manifest.permission.MANAGE_USERS, 823 Car.PERMISSION_MANAGE_OCCUPANT_ZONE); 824 825 if (!mEnableProfileUserAssignmentForMultiDisplay) { 826 throw new IllegalStateException("feature not enabled"); 827 } 828 829 UserHandle user = null; 830 synchronized (mLock) { 831 if (occupantZoneId == mDriverZoneId) { 832 throw new IllegalArgumentException("Driver zone cannot have profile user"); 833 } 834 updateEnabledProfilesLocked(getCurrentUser()); 835 836 if (!mProfileUsers.contains(userId) 837 && userId != CarOccupantZoneManager.INVALID_USER_ID) { 838 // current user can change while this call is happening, so return false rather 839 // than throwing exception 840 Slogf.w(TAG, "Invalid profile user id: %d", userId); 841 return false; 842 } 843 if (userId != CarOccupantZoneManager.INVALID_USER_ID) { 844 user = UserHandle.of(userId); 845 } 846 } 847 848 long token = Binder.clearCallingIdentity(); 849 try { 850 if (userId == CarOccupantZoneManager.INVALID_USER_ID) { 851 return unassignOccupantZoneUnchecked(occupantZoneId) 852 == CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_OK; 853 } else { 854 return assignVisibleUserToOccupantZoneUnchecked(occupantZoneId, user) 855 == CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_OK; 856 } 857 } finally { 858 Binder.restoreCallingIdentity(token); 859 } 860 } 861 862 @Override assignVisibleUserToOccupantZone(int occupantZoneId, UserHandle user)863 public int assignVisibleUserToOccupantZone(int occupantZoneId, UserHandle user) { 864 CarServiceUtils.assertAnyPermission(mContext, android.Manifest.permission.MANAGE_USERS, 865 Car.PERMISSION_MANAGE_OCCUPANT_ZONE); 866 Preconditions.checkNotNull(user); 867 long token = Binder.clearCallingIdentity(); 868 try { 869 return assignVisibleUserToOccupantZoneUnchecked(occupantZoneId, user); 870 } finally { 871 Binder.restoreCallingIdentity(token); 872 } 873 } 874 875 /** 876 * Precondition: permission check should be done and binder caller identity should be cleared. 877 */ assignVisibleUserToOccupantZoneUnchecked(int occupantZoneId, @NonNull UserHandle user)878 private int assignVisibleUserToOccupantZoneUnchecked(int occupantZoneId, 879 @NonNull UserHandle user) { 880 int userId; 881 if (user.equals(UserHandle.CURRENT)) { 882 userId = getCurrentUser(); 883 } else { 884 userId = user.getIdentifier(); 885 } 886 887 if (!isUserVisible(user)) { 888 Slogf.w(TAG, "Non-visible user %d cannot be allocated to zone %d", userId, 889 occupantZoneId); 890 return CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_FAIL_NON_VISIBLE_USER; 891 } 892 893 synchronized (mLock) { 894 int userZoneId = getZoneIdForUserIdLocked(userId); 895 if (userZoneId != OccupantZoneInfo.INVALID_ZONE_ID 896 && mActiveOccupantConfigs.keyAt(userZoneId) != occupantZoneId) { 897 Slogf.w(TAG, "Cannot assign visible user %d to two different zones simultaneously," 898 + " user is already assigned to %d", 899 userId, userZoneId); 900 return CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED; 901 } 902 OccupantConfig config = mActiveOccupantConfigs.get(occupantZoneId); 903 if (config == null) { 904 Slogf.w(TAG, "Invalid zone:%d", occupantZoneId); 905 throw new IllegalArgumentException("Invalid occupantZoneId:" + occupantZoneId); 906 } 907 if (config.userId == userId && userId != CarOccupantZoneManager.INVALID_USER_ID) { 908 Slogf.w(TAG, "assignVisibleUserToOccupantZone zone:%d already set to user:%d", 909 occupantZoneId, userId); 910 return CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_OK; 911 } 912 if (DBG) Slogf.d(TAG, "Assigned user %d to zone %d", userId, occupantZoneId); 913 config.userId = userId; 914 } 915 916 sendConfigChangeEvent(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER); 917 return CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_OK; 918 } 919 920 @GuardedBy("mLock") getZoneIdForUserIdLocked(@serIdInt int userId)921 private int getZoneIdForUserIdLocked(@UserIdInt int userId) { 922 for (int i = 0; i < mActiveOccupantConfigs.size(); i++) { 923 OccupantConfig config = mActiveOccupantConfigs.valueAt(i); 924 if (config.userId == userId) { 925 return mActiveOccupantConfigs.keyAt(i); 926 } 927 } 928 return OccupantZoneInfo.INVALID_ZONE_ID; 929 } 930 931 @Override unassignOccupantZone(int occupantZoneId)932 public int unassignOccupantZone(int occupantZoneId) { 933 CarServiceUtils.assertAnyPermission(mContext, android.Manifest.permission.MANAGE_USERS, 934 Car.PERMISSION_MANAGE_OCCUPANT_ZONE); 935 936 long token = Binder.clearCallingIdentity(); 937 try { 938 return unassignOccupantZoneUnchecked(occupantZoneId); 939 } finally { 940 Binder.restoreCallingIdentity(token); 941 } 942 } 943 944 /** 945 * Precondition: permission check should be done and binder caller identity should be cleared. 946 */ unassignOccupantZoneUnchecked(int occupantZoneId)947 private int unassignOccupantZoneUnchecked(int occupantZoneId) { 948 synchronized (mLock) { 949 OccupantConfig config = mActiveOccupantConfigs.get(occupantZoneId); 950 if (config == null) { 951 Slogf.w(TAG, "Invalid zone:%d", occupantZoneId); 952 throw new IllegalArgumentException("Invalid occupantZoneId:" + occupantZoneId); 953 } 954 if (config.userId == CarOccupantZoneManager.INVALID_USER_ID) { 955 // already unassigned 956 Slogf.w(TAG, "unassignOccupantZone for already unassigned zone:%d", 957 occupantZoneId); 958 return CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_OK; 959 } 960 OccupantZoneInfo info = mOccupantsConfig.get(occupantZoneId); 961 if (info.occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { 962 Slogf.w(TAG, "Cannot unassign driver zone"); 963 return CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE; 964 } 965 if (DBG) Slogf.d(TAG, "Unassigned zone:%d", occupantZoneId); 966 config.userId = CarOccupantZoneManager.INVALID_USER_ID; 967 } 968 sendConfigChangeEvent(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER); 969 970 return CarOccupantZoneManager.USER_ASSIGNMENT_RESULT_OK; 971 } 972 973 @Override getMyOccupantZone()974 public OccupantZoneInfo getMyOccupantZone() { 975 int uid = Binder.getCallingUid(); 976 // UserHandle.getUserId(uid) can do this in one step but it is hidden API. 977 UserHandle user = UserHandle.getUserHandleForUid(uid); 978 int userId = user.getIdentifier(); 979 synchronized (mLock) { 980 for (int i = 0; i < mActiveOccupantConfigs.size(); i++) { 981 OccupantConfig config = mActiveOccupantConfigs.valueAt(i); 982 if (config.userId == userId) { 983 int zoneId = mActiveOccupantConfigs.keyAt(i); 984 return mOccupantsConfig.get(zoneId); 985 } 986 } 987 } 988 Slogf.w(TAG, "getMyOccupantZone: No assigned zone for uid:%d", uid); 989 return null; 990 } 991 992 @Override getOccupantZoneForUser(UserHandle user)993 public OccupantZoneInfo getOccupantZoneForUser(UserHandle user) { 994 Objects.requireNonNull(user, "User cannot be null"); 995 if (user.getIdentifier() == CarOccupantZoneManager.INVALID_USER_ID) { 996 return null; 997 } 998 int occupantZoneId = getOccupantZoneIdForUserId(user.getIdentifier()); 999 if (DBG) Slogf.d(TAG, "occupantZoneId that was gotten was %d", occupantZoneId); 1000 synchronized (mLock) { 1001 return mOccupantsConfig.get(occupantZoneId); 1002 } 1003 } 1004 1005 @Override getOccupantZone(@ccupantTypeEnum int occupantType, @VehicleAreaSeat.Enum int seat)1006 public OccupantZoneInfo getOccupantZone(@OccupantTypeEnum int occupantType, 1007 @VehicleAreaSeat.Enum int seat) { 1008 synchronized (mLock) { 1009 for (int i = 0; i < mActiveOccupantConfigs.size(); i++) { 1010 int zoneId = mActiveOccupantConfigs.keyAt(i); 1011 OccupantZoneInfo info = mOccupantsConfig.get(zoneId); 1012 if (occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER 1013 || occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_FRONT_PASSENGER) { 1014 if (occupantType == info.occupantType) { 1015 return info; 1016 } 1017 } else { 1018 if (occupantType == info.occupantType && seat == info.seat) { 1019 return info; 1020 } 1021 } 1022 } 1023 return null; 1024 } 1025 } 1026 1027 /** 1028 * Gets the occupant zone id for the seat 1029 * 1030 * @param seat The vehicle area seat to be used 1031 * 1032 * @return The occupant zone id for the given seat 1033 */ getOccupantZoneIdForSeat(@ehicleAreaSeat.Enum int seat)1034 public int getOccupantZoneIdForSeat(@VehicleAreaSeat.Enum int seat) { 1035 synchronized (mLock) { 1036 return getOccupantZoneIdForSeatLocked(seat); 1037 } 1038 } 1039 1040 @GuardedBy("mLock") getOccupantZoneIdForSeatLocked(@ehicleAreaSeat.Enum int seat)1041 private int getOccupantZoneIdForSeatLocked(@VehicleAreaSeat.Enum int seat) { 1042 for (int i = 0; i < mActiveOccupantConfigs.size(); i++) { 1043 int zoneId = mActiveOccupantConfigs.keyAt(i); 1044 OccupantZoneInfo info = mOccupantsConfig.get(zoneId); 1045 if (seat == info.seat) { 1046 return zoneId; 1047 } 1048 } 1049 return OccupantZoneInfo.INVALID_ZONE_ID; 1050 } 1051 1052 @Override hasDriverZone()1053 public boolean hasDriverZone() { 1054 synchronized (mLock) { 1055 return mDriverZoneId != OccupantZoneInfo.INVALID_ZONE_ID; 1056 } 1057 } 1058 1059 @Override hasPassengerZones()1060 public boolean hasPassengerZones() { 1061 synchronized (mLock) { 1062 // There can be only one driver zone. So if there is driver, there should be at least 1063 // two zones to have passenger. If there is no driver zone, having a zone is enough to 1064 // have passenger zone. 1065 boolean hasDriver = mDriverZoneId != OccupantZoneInfo.INVALID_ZONE_ID; 1066 return mActiveOccupantConfigs.size() > (hasDriver ? 1 : 0); 1067 } 1068 } 1069 1070 @Override 1071 @UserIdInt getUserForDisplayId(int displayId)1072 public int getUserForDisplayId(int displayId) { 1073 synchronized (mLock) { 1074 for (int i = 0; i < mActiveOccupantConfigs.size(); i++) { 1075 OccupantConfig config = mActiveOccupantConfigs.valueAt(i); 1076 for (int j = 0; j < config.displayInfos.size(); j++) { 1077 if (config.displayInfos.get(j).display.getDisplayId() == displayId) { 1078 return config.userId; 1079 } 1080 } 1081 } 1082 } 1083 Slogf.w(TAG, "Could not find OccupantZone for display Id %d", displayId); 1084 return CarOccupantZoneManager.INVALID_USER_ID; 1085 } 1086 1087 /** Returns number of passenger zones in the device. */ getNumberOfPassengerZones()1088 public int getNumberOfPassengerZones() { 1089 synchronized (mLock) { 1090 boolean hasDriver = mDriverZoneId != OccupantZoneInfo.INVALID_ZONE_ID; 1091 return mActiveOccupantConfigs.size() - (hasDriver ? 1 : 0); 1092 } 1093 } 1094 doSyncWithCarServiceHelper(boolean updateDisplay, boolean updateUser, boolean updateConfig)1095 private void doSyncWithCarServiceHelper(boolean updateDisplay, boolean updateUser, 1096 boolean updateConfig) { 1097 int[] passengerDisplays = null; 1098 ArrayMap<Integer, IntArray> allowlists = null; 1099 synchronized (mLock) { 1100 if (updateDisplay) { 1101 passengerDisplays = getAllActivePassengerDisplaysLocked(); 1102 } 1103 if (updateUser) { 1104 allowlists = createDisplayAllowlistsLocked(); 1105 } 1106 } 1107 if (updateDisplay) { 1108 updatePassengerDisplays(passengerDisplays); 1109 } 1110 if (updateUser) { 1111 updateUserAssignmentForDisplays(allowlists); 1112 } 1113 if (updateConfig) { 1114 Resources res = mContext.getResources(); 1115 String[] components = res.getStringArray(R.array.config_sourcePreferredComponents); 1116 updateSourcePreferredComponents(components); 1117 } 1118 } 1119 1120 @GuardedBy("mLock") getAllActivePassengerDisplaysLocked()1121 private int[] getAllActivePassengerDisplaysLocked() { 1122 IntArray displays = new IntArray(); 1123 for (int j = 0; j < mActiveOccupantConfigs.size(); ++j) { 1124 int zoneId = mActiveOccupantConfigs.keyAt(j); 1125 if (zoneId == mDriverZoneId) { 1126 continue; 1127 } 1128 OccupantConfig config = mActiveOccupantConfigs.valueAt(j); 1129 for (int i = 0; i < config.displayInfos.size(); i++) { 1130 displays.add(config.displayInfos.get(i).display.getDisplayId()); 1131 } 1132 } 1133 return displays.toArray(); 1134 } 1135 updatePassengerDisplays(int[] passengerDisplayIds)1136 private void updatePassengerDisplays(int[] passengerDisplayIds) { 1137 if (passengerDisplayIds == null) { 1138 return; 1139 } 1140 CarServiceHelperWrapper.getInstance().setPassengerDisplays(passengerDisplayIds); 1141 } 1142 updateSourcePreferredComponents(String[] components)1143 private void updateSourcePreferredComponents(String[] components) { 1144 boolean enableSourcePreferred; 1145 ArrayList<ComponentName> componentNames = null; 1146 if (components == null || components.length == 0) { 1147 enableSourcePreferred = false; 1148 if (DBG) Slogf.d(TAG, "CarLaunchParamsModifier: disable source-preferred"); 1149 } else if (components.length == 1 && Objects.equals(components[0], ALL_COMPONENTS)) { 1150 enableSourcePreferred = true; 1151 if (DBG) { 1152 Slogf.d(TAG, "CarLaunchParamsModifier: enable source-preferred for all Components"); 1153 } 1154 } else { 1155 componentNames = new ArrayList<>((components.length)); 1156 for (String item : components) { 1157 ComponentName name = ComponentName.unflattenFromString(item); 1158 if (name == null) { 1159 Slogf.e(TAG, "CarLaunchParamsModifier: Wrong ComponentName=" + item); 1160 return; 1161 } 1162 componentNames.add(name); 1163 } 1164 enableSourcePreferred = true; 1165 } 1166 CarServiceHelperWrapper.getInstance().setSourcePreferredComponents(enableSourcePreferred, 1167 componentNames); 1168 mEnableSourcePreferred = enableSourcePreferred; 1169 mSourcePreferredComponents = componentNames; 1170 } 1171 1172 @GuardedBy("mLock") createDisplayAllowlistsLocked()1173 private ArrayMap<Integer, IntArray> createDisplayAllowlistsLocked() { 1174 ArrayMap<Integer, IntArray> allowlists = new ArrayMap<>(); 1175 for (int j = 0; j < mActiveOccupantConfigs.size(); ++j) { 1176 int zoneId = mActiveOccupantConfigs.keyAt(j); 1177 if (zoneId == mDriverZoneId) { 1178 continue; 1179 } 1180 OccupantConfig config = mActiveOccupantConfigs.valueAt(j); 1181 if (config.displayInfos.isEmpty()) { 1182 continue; 1183 } 1184 // Do not allow any user if it is unassigned. 1185 if (config.userId == CarOccupantZoneManager.INVALID_USER_ID) { 1186 continue; 1187 } 1188 IntArray displays = allowlists.get(config.userId); 1189 if (displays == null) { 1190 displays = new IntArray(); 1191 allowlists.put(config.userId, displays); 1192 } 1193 for (int i = 0; i < config.displayInfos.size(); i++) { 1194 displays.add(config.displayInfos.get(i).display.getDisplayId()); 1195 } 1196 } 1197 return allowlists; 1198 } 1199 updateUserAssignmentForDisplays(ArrayMap<Integer, IntArray> allowlists)1200 private void updateUserAssignmentForDisplays(ArrayMap<Integer, IntArray> allowlists) { 1201 if (allowlists == null || allowlists.isEmpty()) { 1202 return; 1203 } 1204 for (int i = 0; i < allowlists.size(); i++) { 1205 int userId = allowlists.keyAt(i); 1206 CarServiceHelperWrapper.getInstance().setDisplayAllowlistForUser(userId, 1207 allowlists.valueAt(i).toArray()); 1208 } 1209 } 1210 throwFormatErrorInOccupantZones(String msg)1211 private void throwFormatErrorInOccupantZones(String msg) { 1212 throw new RuntimeException("Format error in config_occupant_zones resource:" + msg); 1213 } 1214 1215 /** Returns the driver seat. */ getDriverSeat()1216 int getDriverSeat() { 1217 synchronized (mLock) { 1218 return mDriverSeat; 1219 } 1220 } 1221 1222 @GuardedBy("mLock") parseOccupantZoneConfigsLocked()1223 private void parseOccupantZoneConfigsLocked() { 1224 final Resources res = mContext.getResources(); 1225 // examples: 1226 // <item>occupantZoneId=0,occupantType=DRIVER,seatRow=1,seatSide=driver</item> 1227 // <item>occupantZoneId=1,occupantType=FRONT_PASSENGER,seatRow=1, 1228 // searSide=oppositeDriver</item> 1229 boolean hasDriver = false; 1230 int driverSeat = getDriverSeat(); 1231 int driverSeatSide = VehicleAreaSeat.SIDE_LEFT; // default LHD : Left Hand Drive 1232 if (driverSeat == VehicleAreaSeat.SEAT_ROW_1_RIGHT) { 1233 driverSeatSide = VehicleAreaSeat.SIDE_RIGHT; 1234 } 1235 int maxZoneId = OccupantZoneInfo.INVALID_ZONE_ID; 1236 for (String config : res.getStringArray(R.array.config_occupant_zones)) { 1237 int zoneId = OccupantZoneInfo.INVALID_ZONE_ID; 1238 int type = CarOccupantZoneManager.OCCUPANT_TYPE_INVALID; 1239 int seatRow = 0; // invalid row 1240 int seatSide = VehicleAreaSeat.SIDE_LEFT; 1241 String[] entries = config.split(","); 1242 for (String entry : entries) { 1243 String[] keyValuePair = entry.split("="); 1244 if (keyValuePair.length != 2) { 1245 throwFormatErrorInOccupantZones("No key/value pair:" + entry); 1246 } 1247 switch (keyValuePair[0]) { 1248 case "occupantZoneId": 1249 zoneId = Integer.parseInt(keyValuePair[1]); 1250 break; 1251 case "occupantType": 1252 switch (keyValuePair[1]) { 1253 case "DRIVER": 1254 type = CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER; 1255 break; 1256 case "FRONT_PASSENGER": 1257 type = CarOccupantZoneManager.OCCUPANT_TYPE_FRONT_PASSENGER; 1258 break; 1259 case "REAR_PASSENGER": 1260 type = CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER; 1261 break; 1262 default: 1263 throwFormatErrorInOccupantZones("Unrecognized type:" + entry); 1264 break; 1265 } 1266 break; 1267 case "seatRow": 1268 seatRow = Integer.parseInt(keyValuePair[1]); 1269 break; 1270 case "seatSide": 1271 switch (keyValuePair[1]) { 1272 case "driver": 1273 seatSide = driverSeatSide; 1274 break; 1275 case "oppositeDriver": 1276 seatSide = -driverSeatSide; 1277 break; 1278 case "left": 1279 seatSide = VehicleAreaSeat.SIDE_LEFT; 1280 break; 1281 case "center": 1282 seatSide = VehicleAreaSeat.SIDE_CENTER; 1283 break; 1284 case "right": 1285 seatSide = VehicleAreaSeat.SIDE_RIGHT; 1286 break; 1287 default: 1288 throwFormatErrorInOccupantZones("Unrecognized seatSide:" + entry); 1289 break; 1290 } 1291 break; 1292 default: 1293 throwFormatErrorInOccupantZones("Unrecognized key:" + entry); 1294 break; 1295 } 1296 } 1297 if (zoneId == OccupantZoneInfo.INVALID_ZONE_ID) { 1298 throwFormatErrorInOccupantZones("Missing zone id:" + config); 1299 } 1300 if (zoneId > maxZoneId) { 1301 maxZoneId = zoneId; 1302 } 1303 if (type == CarOccupantZoneManager.OCCUPANT_TYPE_INVALID) { 1304 throwFormatErrorInOccupantZones("Missing type:" + config); 1305 } 1306 if (type == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { 1307 if (hasDriver) { 1308 throwFormatErrorInOccupantZones("Multiple driver:" + config); 1309 } else { 1310 hasDriver = true; 1311 mDriverZoneId = zoneId; 1312 } 1313 } 1314 int seat = VehicleAreaSeat.fromRowAndSide(seatRow, seatSide); 1315 if (seat == VehicleAreaSeat.SEAT_UNKNOWN) { 1316 throwFormatErrorInOccupantZones("Invalid seat:" + config); 1317 } 1318 OccupantZoneInfo info = new OccupantZoneInfo(zoneId, type, seat); 1319 if (mOccupantsConfig.contains(zoneId)) { 1320 throwFormatErrorInOccupantZones("Duplicate zone id:" + config); 1321 } 1322 mOccupantsConfig.put(zoneId, info); 1323 } 1324 // No zones defined. Then populate driver zone. 1325 if (maxZoneId == OccupantZoneInfo.INVALID_ZONE_ID) { 1326 maxZoneId++; 1327 mDriverZoneId = maxZoneId; 1328 Slogf.w(TAG, "No zones defined, add one as driver:%d", mDriverZoneId); 1329 OccupantZoneInfo info = new OccupantZoneInfo(mDriverZoneId, 1330 CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER, getDriverSeat()); 1331 mOccupantsConfig.put(mDriverZoneId, info); 1332 } 1333 } 1334 throwFormatErrorInDisplayMapping(String msg)1335 private void throwFormatErrorInDisplayMapping(String msg) { 1336 throw new RuntimeException( 1337 "Format error in config_occupant_display_mapping resource:" + msg); 1338 } 1339 1340 @GuardedBy("mLock") parseDisplayConfigsLocked()1341 private void parseDisplayConfigsLocked() { 1342 final Resources res = mContext.getResources(); 1343 final SparseArray<IntArray> inputTypesPerDisplay = new SparseArray<>(); 1344 // examples: 1345 // <item>displayPort=0,displayType=MAIN,occupantZoneId=0,inputTypes=DPAD_KEYS| 1346 // NAVIGATE_KEYS|ROTARY_NAVIGATION</item> 1347 // <item>displayPort=1,displayType=INSTRUMENT_CLUSTER,occupantZoneId=0, 1348 // inputTypes=DPAD_KEYS</item> 1349 for (String config : res.getStringArray(R.array.config_occupant_display_mapping)) { 1350 int port = INVALID_PORT; 1351 String uniqueId = null; 1352 int type = CarOccupantZoneManager.DISPLAY_TYPE_UNKNOWN; 1353 int zoneId = OccupantZoneInfo.INVALID_ZONE_ID; 1354 String[] entries = config.split(","); 1355 for (String entry : entries) { 1356 String[] keyValuePair = entry.split("="); 1357 if (keyValuePair.length != 2) { 1358 throwFormatErrorInDisplayMapping("No key/value pair:" + entry); 1359 } 1360 switch (keyValuePair[0]) { 1361 case "displayPort": 1362 port = Integer.parseInt(keyValuePair[1]); 1363 break; 1364 case "displayUniqueId": 1365 uniqueId = keyValuePair[1]; 1366 break; 1367 case "displayType": 1368 switch (keyValuePair[1]) { 1369 case "MAIN": 1370 type = CarOccupantZoneManager.DISPLAY_TYPE_MAIN; 1371 break; 1372 case "INSTRUMENT_CLUSTER": 1373 type = CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER; 1374 break; 1375 case "HUD": 1376 type = CarOccupantZoneManager.DISPLAY_TYPE_HUD; 1377 break; 1378 case "INPUT": 1379 type = CarOccupantZoneManager.DISPLAY_TYPE_INPUT; 1380 break; 1381 case "AUXILIARY": 1382 type = CarOccupantZoneManager.DISPLAY_TYPE_AUXILIARY; 1383 break; 1384 default: 1385 throwFormatErrorInDisplayMapping( 1386 "Unrecognized display type:" + entry); 1387 break; 1388 } 1389 inputTypesPerDisplay.set(type, new IntArray()); 1390 break; 1391 case "occupantZoneId": 1392 zoneId = Integer.parseInt(keyValuePair[1]); 1393 break; 1394 case "inputTypes": 1395 String[] inputStrings = keyValuePair[1].split("\\|"); 1396 for (int i = 0; i < inputStrings.length; i++) { 1397 switch (inputStrings[i].trim()) { 1398 case "ROTARY_NAVIGATION": 1399 inputTypesPerDisplay.get(type).add( 1400 CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION); 1401 break; 1402 case "ROTARY_VOLUME": 1403 inputTypesPerDisplay.get(type).add( 1404 CarInputManager.INPUT_TYPE_ROTARY_VOLUME); 1405 break; 1406 case "DPAD_KEYS": 1407 inputTypesPerDisplay.get(type).add( 1408 CarInputManager.INPUT_TYPE_DPAD_KEYS); 1409 break; 1410 case "NAVIGATE_KEYS": 1411 inputTypesPerDisplay.get(type).add( 1412 CarInputManager.INPUT_TYPE_NAVIGATE_KEYS); 1413 break; 1414 case "SYSTEM_NAVIGATE_KEYS": 1415 inputTypesPerDisplay.get(type).add( 1416 CarInputManager.INPUT_TYPE_SYSTEM_NAVIGATE_KEYS); 1417 break; 1418 case "CUSTOM_INPUT_EVENT": 1419 inputTypesPerDisplay.get(type).add( 1420 CarInputManager.INPUT_TYPE_CUSTOM_INPUT_EVENT); 1421 break; 1422 case "TOUCH_SCREEN": 1423 inputTypesPerDisplay.get(type).add( 1424 CarInputManager.INPUT_TYPE_TOUCH_SCREEN); 1425 break; 1426 case "NONE": 1427 inputTypesPerDisplay.get(type).add( 1428 CarInputManager.INPUT_TYPE_NONE); 1429 break; 1430 default: 1431 throw new IllegalArgumentException("Invalid input type: " 1432 + inputStrings[i]); 1433 } 1434 } 1435 break; 1436 default: 1437 throwFormatErrorInDisplayMapping("Unrecognized key:" + entry); 1438 break; 1439 } 1440 } 1441 1442 // Now check validity 1443 checkInputTypeNoneLocked(inputTypesPerDisplay); 1444 if (port == INVALID_PORT && uniqueId == null) { 1445 throwFormatErrorInDisplayMapping( 1446 "Missing or invalid displayPort and displayUniqueId:" + config); 1447 } 1448 if (type == CarOccupantZoneManager.DISPLAY_TYPE_UNKNOWN) { 1449 throwFormatErrorInDisplayMapping("Missing or invalid displayType:" + config); 1450 } 1451 if (zoneId == OccupantZoneInfo.INVALID_ZONE_ID) { 1452 throwFormatErrorInDisplayMapping("Missing or invalid occupantZoneId:" + config); 1453 } 1454 if (!mOccupantsConfig.contains(zoneId)) { 1455 throwFormatErrorInDisplayMapping( 1456 "Missing or invalid occupantZoneId:" + config); 1457 } 1458 final DisplayConfig displayConfig = new DisplayConfig(type, zoneId, 1459 inputTypesPerDisplay.get(type)); 1460 if (port != INVALID_PORT) { 1461 if (mDisplayPortConfigs.contains(port)) { 1462 throwFormatErrorInDisplayMapping("Duplicate displayPort:" + config); 1463 } 1464 mDisplayPortConfigs.put(port, displayConfig); 1465 } else { 1466 if (mDisplayUniqueIdConfigs.containsKey(uniqueId)) { 1467 throwFormatErrorInDisplayMapping("Duplicate displayUniqueId:" + config); 1468 } 1469 mDisplayUniqueIdConfigs.put(uniqueId, displayConfig); 1470 } 1471 } 1472 1473 Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); 1474 if (findDisplayConfigForDisplayLocked(defaultDisplay) == null) { 1475 int zoneForDefaultDisplay = mDriverZoneId; 1476 if (zoneForDefaultDisplay == OccupantZoneInfo.INVALID_ZONE_ID) { 1477 // No driver zone but we still need to allocate the default display to the 1st zone, 1478 // zone id 0. 1479 zoneForDefaultDisplay = 0; 1480 } 1481 Slogf.w(TAG, "No default display configuration, will assign to zone:" 1482 + zoneForDefaultDisplay); 1483 mDisplayUniqueIdConfigs.put(DisplayHelper.getUniqueId(defaultDisplay), 1484 new DisplayConfig(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 1485 zoneForDefaultDisplay, inputTypesPerDisplay.get( 1486 CarOccupantZoneManager.DISPLAY_TYPE_MAIN))); 1487 } 1488 } 1489 1490 @GuardedBy("mLock") checkInputTypeNoneLocked(SparseArray<IntArray> inputTypesPerDisplay)1491 private void checkInputTypeNoneLocked(SparseArray<IntArray> inputTypesPerDisplay) { 1492 for (int i = 0; i < inputTypesPerDisplay.size(); ++i) { 1493 IntArray inputTypes = inputTypesPerDisplay.valueAt(i); 1494 for (int j = 0; j < inputTypes.size(); ++j) { 1495 if (inputTypes.get(j) == CarInputManager.INPUT_TYPE_NONE && inputTypes.size() > 1) { 1496 throw new IllegalArgumentException("Display {" + inputTypesPerDisplay.keyAt(i) 1497 + "} has input type NONE defined along with other input types"); 1498 } 1499 } 1500 } 1501 } 1502 1503 @GuardedBy("mLock") addDisplayInfoToOccupantZoneLocked(int zoneId, DisplayInfo info)1504 private void addDisplayInfoToOccupantZoneLocked(int zoneId, DisplayInfo info) { 1505 OccupantConfig occupantConfig = mActiveOccupantConfigs.get(zoneId); 1506 if (occupantConfig == null) { 1507 occupantConfig = new OccupantConfig(); 1508 mActiveOccupantConfigs.put(zoneId, occupantConfig); 1509 } 1510 occupantConfig.displayInfos.add(info); 1511 } 1512 1513 @GuardedBy("mLock") handleActiveDisplaysLocked()1514 private void handleActiveDisplaysLocked() { 1515 // Clear display info for each zone in preparation for re-populating display info. 1516 for (int i = 0; i < mActiveOccupantConfigs.size(); i++) { 1517 OccupantConfig occupantConfig = mActiveOccupantConfigs.valueAt(i); 1518 occupantConfig.displayInfos.clear(); 1519 } 1520 1521 boolean hasDefaultDisplayConfig = false; 1522 boolean hasDriverZone = hasDriverZone(); 1523 for (Display display : mDisplayManager.getDisplays()) { 1524 DisplayConfig displayConfig = findDisplayConfigForDisplayLocked(display); 1525 if (displayConfig == null) { 1526 Slogf.w(TAG, "Display id:%d does not have configurations", 1527 display.getDisplayId()); 1528 continue; 1529 } 1530 if (hasDriverZone && display.getDisplayId() == Display.DEFAULT_DISPLAY) { 1531 if (displayConfig.occupantZoneId != mDriverZoneId) { 1532 throw new IllegalStateException( 1533 "Default display should be only assigned to driver zone"); 1534 } 1535 hasDefaultDisplayConfig = true; 1536 } 1537 addDisplayInfoToOccupantZoneLocked(displayConfig.occupantZoneId, 1538 new DisplayInfo(display, displayConfig.displayType)); 1539 } 1540 1541 // Remove OccupantConfig in zones without displays. 1542 for (int i = 0; i < mActiveOccupantConfigs.size(); i++) { 1543 OccupantConfig occupantConfig = mActiveOccupantConfigs.valueAt(i); 1544 if (occupantConfig.displayInfos.size() == 0) { 1545 if (DBG) { 1546 Slogf.d(TAG, "handleActiveDisplaysLocked: removing zone %d due to no display", 1547 mActiveOccupantConfigs.keyAt(i)); 1548 } 1549 mActiveOccupantConfigs.removeAt(i); 1550 } 1551 } 1552 1553 if (hasDriverZone && !hasDefaultDisplayConfig) { 1554 // This shouldn't happen, since we added the default display config in 1555 // parseDisplayConfigsLocked(). 1556 throw new IllegalStateException("Default display not assigned"); 1557 } 1558 } 1559 1560 @VisibleForTesting getCurrentUser()1561 int getCurrentUser() { 1562 return ActivityManager.getCurrentUser(); 1563 } 1564 1565 @GuardedBy("mLock") updateEnabledProfilesLocked(@serIdInt int userId)1566 private void updateEnabledProfilesLocked(@UserIdInt int userId) { 1567 mProfileUsers.clear(); 1568 List<UserHandle> profileUsers = mUserHandleHelper.getEnabledProfiles(userId); 1569 for (UserHandle profiles : profileUsers) { 1570 if (profiles.getIdentifier() != userId) { 1571 mProfileUsers.add(profiles.getIdentifier()); 1572 } 1573 } 1574 } 1575 1576 /** 1577 * Checks if the given user is visible. This works in pre-U as well. 1578 */ 1579 @VisibleForTesting 1580 @SuppressLint("NewApi") isUserVisible(@onNull UserHandle user)1581 public boolean isUserVisible(@NonNull UserHandle user) { 1582 if (isPlatformVersionAtLeastU()) { 1583 // createContextAsUser throw exception if user does not exist. So it is not a reliable 1584 // way to query it from car service. We need to catch the exception. 1585 // TODO(b/243864134) Plumb to CarServiceHelper to use UserManagerInternal instead. 1586 try { 1587 Context userContext = mContext.createContextAsUser(user, /* flags= */ 0); 1588 UserManager userManager = userContext.getSystemService(UserManager.class); 1589 return userManager.isUserVisible(); 1590 } catch (Exception e) { 1591 Slogf.w(TAG, "Cannot create User Context for user:" + user.getIdentifier(), e); 1592 return false; 1593 } 1594 } 1595 1596 // This is legacy path for T where there is no visible user but we can still support profile 1597 // user as visible as long as it belongs to the current user. 1598 int currentUser = getCurrentUser(); 1599 int userId = user.getIdentifier(); 1600 if (userId == currentUser) { 1601 return true; 1602 } 1603 synchronized (mLock) { 1604 if (mProfileUsers.contains(userId)) { 1605 return true; 1606 } 1607 } 1608 1609 return false; 1610 } 1611 1612 /** Returns {@code true} if user allocation has changed */ 1613 @GuardedBy("mLock") handleUserChangesLocked()1614 private boolean handleUserChangesLocked() { 1615 int currentUserId = getCurrentUser(); 1616 1617 if (mEnableProfileUserAssignmentForMultiDisplay) { 1618 updateEnabledProfilesLocked(currentUserId); 1619 } 1620 1621 boolean changed = false; 1622 for (int i = 0; i < mActiveOccupantConfigs.size(); ++i) { 1623 int zoneId = mActiveOccupantConfigs.keyAt(i); 1624 OccupantConfig config = mActiveOccupantConfigs.valueAt(i); 1625 OccupantZoneInfo info = mOccupantsConfig.get(zoneId); 1626 // Assign the current user to the driver zone if there is a driver zone. 1627 if (info.occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER 1628 && config.userId != currentUserId) { 1629 config.userId = currentUserId; 1630 changed = true; 1631 if (DBG) { 1632 Slogf.d(TAG, "Changed driver, current user change to %d", 1633 currentUserId); 1634 } 1635 continue; 1636 } 1637 // Do not touch if the zone is un-assigned. 1638 if (config.userId == CarOccupantZoneManager.INVALID_USER_ID) { 1639 continue; 1640 } 1641 // Now it will be non-driver valid user id. 1642 if (!isUserVisible(UserHandle.of(config.userId))) { 1643 if (DBG) Slogf.d(TAG, "Unassigned no longer visible user:%d", config.userId); 1644 config.userId = CarOccupantZoneManager.INVALID_USER_ID; 1645 changed = true; 1646 } 1647 } 1648 1649 return changed; 1650 } 1651 1652 @GuardedBy("mLock") handleAudioZoneChangesLocked()1653 private void handleAudioZoneChangesLocked() { 1654 for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) { 1655 int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index); 1656 int occupantZoneId = mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId); 1657 OccupantConfig occupantConfig = mActiveOccupantConfigs.get(occupantZoneId); 1658 if (occupantConfig == null) { 1659 //no active display for zone just continue 1660 continue; 1661 } 1662 // Found an active configuration, add audio to it. 1663 occupantConfig.audioZoneId = audioZoneId; 1664 } 1665 } 1666 sendConfigChangeEvent(int changeFlags)1667 private void sendConfigChangeEvent(int changeFlags) { 1668 boolean updateDisplay = false; 1669 boolean updateUser = false; 1670 if ((changeFlags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY) != 0) { 1671 updateDisplay = true; 1672 updateUser = true; 1673 } else if ((changeFlags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER) != 0) { 1674 updateUser = true; 1675 } 1676 doSyncWithCarServiceHelper(updateDisplay, updateUser, /* updateConfig= */ false); 1677 1678 // Schedule remote callback invocation with the handler attached to the same Looper to 1679 // ensure that only one broadcast can be active at one time. 1680 mHandler.post(() -> { 1681 int n = mClientCallbacks.beginBroadcast(); 1682 for (int i = 0; i < n; i++) { 1683 ICarOccupantZoneCallback callback = mClientCallbacks.getBroadcastItem(i); 1684 try { 1685 callback.onOccupantZoneConfigChanged(changeFlags); 1686 } catch (RemoteException ignores) { 1687 // ignore 1688 } 1689 } 1690 mClientCallbacks.finishBroadcast(); 1691 }); 1692 } 1693 handleUserChange()1694 private void handleUserChange() { 1695 boolean changed; 1696 synchronized (mLock) { 1697 changed = handleUserChangesLocked(); 1698 } 1699 if (changed) { 1700 sendConfigChangeEvent(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER); 1701 } 1702 } 1703 handlePassengerStarted()1704 private void handlePassengerStarted() { 1705 sendConfigChangeEvent(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER); 1706 } 1707 handlePassengerStopped()1708 private void handlePassengerStopped() { 1709 sendConfigChangeEvent(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER); 1710 } 1711 handleDisplayChange()1712 private void handleDisplayChange() { 1713 synchronized (mLock) { 1714 handleActiveDisplaysLocked(); 1715 // Audio zones should be re-checked for changed display 1716 handleAudioZoneChangesLocked(); 1717 // User should be re-checked for changed displays 1718 handleUserChangesLocked(); 1719 } 1720 sendConfigChangeEvent(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY); 1721 } 1722 enforcePermission(String permissionName)1723 private void enforcePermission(String permissionName) { 1724 if (mContext.checkCallingOrSelfPermission(permissionName) 1725 != PackageManager.PERMISSION_GRANTED) { 1726 throw new SecurityException("requires permission " + permissionName); 1727 } 1728 } 1729 1730 /** 1731 * Returns the supported input types for the occupant zone info and display type passed as 1732 * the argument. 1733 * 1734 * @param occupantZoneId the occupant zone id of the supported input types to find 1735 * @param displayType the display type of the supported input types to find 1736 * @return the supported input types for the occupant zone info and display type passed in as 1737 * the argument 1738 */ getSupportedInputTypes(int occupantZoneId, int displayType)1739 public int[] getSupportedInputTypes(int occupantZoneId, int displayType) { 1740 checkOccupantZone(occupantZoneId, displayType); 1741 synchronized (mLock) { 1742 // Search input type in mDisplayPortConfigs 1743 for (int i = 0; i < mDisplayPortConfigs.size(); i++) { 1744 DisplayConfig config = mDisplayPortConfigs.valueAt(i); 1745 if (config.displayType == displayType && config.occupantZoneId == occupantZoneId) { 1746 return config.inputTypes; 1747 } 1748 } 1749 // Search input type in mDisplayUniqueIdConfigs 1750 for (int i = 0; i < mDisplayUniqueIdConfigs.size(); i++) { 1751 DisplayConfig config = mDisplayUniqueIdConfigs.valueAt(i); 1752 if (config.displayType == displayType && config.occupantZoneId == occupantZoneId) { 1753 return config.inputTypes; 1754 } 1755 } 1756 } 1757 return EMPTY_INPUT_SUPPORT_TYPES; 1758 } 1759 checkOccupantZone(int occupantZoneId, int displayType)1760 private void checkOccupantZone(int occupantZoneId, int displayType) { 1761 if (Display.INVALID_DISPLAY == getDisplayForOccupant(occupantZoneId, displayType)) { 1762 throw new IllegalArgumentException("No display is associated with OccupantZoneInfo " 1763 + occupantZoneId); 1764 } 1765 } 1766 } 1767