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