1/* 2 * Copyright (C) 2012 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 17package com.android.server.display; 18 19import android.content.res.Resources; 20import com.android.server.LocalServices; 21import com.android.server.lights.Light; 22import com.android.server.lights.LightsManager; 23 24import android.content.Context; 25import android.os.Build; 26import android.os.Handler; 27import android.os.IBinder; 28import android.os.Looper; 29import android.os.PowerManager; 30import android.os.SystemProperties; 31import android.os.Trace; 32import android.util.Slog; 33import android.util.SparseArray; 34import android.view.Display; 35import android.view.DisplayEventReceiver; 36import android.view.Surface; 37import android.view.SurfaceControl; 38 39import java.io.PrintWriter; 40import java.util.ArrayList; 41import java.util.Arrays; 42import java.util.Collections; 43import java.util.List; 44 45/** 46 * A display adapter for the local displays managed by Surface Flinger. 47 * <p> 48 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 49 * </p> 50 */ 51final class LocalDisplayAdapter extends DisplayAdapter { 52 private static final String TAG = "LocalDisplayAdapter"; 53 private static final boolean DEBUG = false; 54 55 private static final String UNIQUE_ID_PREFIX = "local:"; 56 57 private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular"; 58 59 private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] { 60 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN, 61 SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI, 62 }; 63 64 private final SparseArray<LocalDisplayDevice> mDevices = 65 new SparseArray<LocalDisplayDevice>(); 66 @SuppressWarnings("unused") // Becomes active at instantiation time. 67 private HotplugDisplayEventReceiver mHotplugReceiver; 68 69 // Called with SyncRoot lock held. 70 public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 71 Context context, Handler handler, Listener listener) { 72 super(syncRoot, context, handler, listener, TAG); 73 } 74 75 @Override 76 public void registerLocked() { 77 super.registerLocked(); 78 79 mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper()); 80 81 for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) { 82 tryConnectDisplayLocked(builtInDisplayId); 83 } 84 } 85 86 private void tryConnectDisplayLocked(int builtInDisplayId) { 87 IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId); 88 if (displayToken != null) { 89 SurfaceControl.PhysicalDisplayInfo[] configs = 90 SurfaceControl.getDisplayConfigs(displayToken); 91 if (configs == null) { 92 // There are no valid configs for this device, so we can't use it 93 Slog.w(TAG, "No valid configs found for display device " + 94 builtInDisplayId); 95 return; 96 } 97 int activeConfig = SurfaceControl.getActiveConfig(displayToken); 98 if (activeConfig < 0) { 99 // There is no active config, and for now we don't have the 100 // policy to set one. 101 Slog.w(TAG, "No active config found for display device " + 102 builtInDisplayId); 103 return; 104 } 105 int activeColorMode = SurfaceControl.getActiveColorMode(displayToken); 106 if (activeColorMode < 0) { 107 // We failed to get the active color mode. We don't bail out here since on the next 108 // configuration pass we'll go ahead and set it to whatever it was set to last (or 109 // COLOR_MODE_NATIVE if this is the first configuration). 110 Slog.w(TAG, "Unable to get active color mode for display device " + 111 builtInDisplayId); 112 activeColorMode = Display.COLOR_MODE_INVALID; 113 } 114 int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken); 115 LocalDisplayDevice device = mDevices.get(builtInDisplayId); 116 if (device == null) { 117 // Display was added. 118 device = new LocalDisplayDevice(displayToken, builtInDisplayId, 119 configs, activeConfig, colorModes, activeColorMode); 120 mDevices.put(builtInDisplayId, device); 121 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); 122 } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig, 123 colorModes, activeColorMode)) { 124 // Display properties changed. 125 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); 126 } 127 } else { 128 // The display is no longer available. Ignore the attempt to add it. 129 // If it was connected but has already been disconnected, we'll get a 130 // disconnect event that will remove it from mDevices. 131 } 132 } 133 134 private void tryDisconnectDisplayLocked(int builtInDisplayId) { 135 LocalDisplayDevice device = mDevices.get(builtInDisplayId); 136 if (device != null) { 137 // Display was removed. 138 mDevices.remove(builtInDisplayId); 139 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED); 140 } 141 } 142 143 static int getPowerModeForState(int state) { 144 switch (state) { 145 case Display.STATE_OFF: 146 return SurfaceControl.POWER_MODE_OFF; 147 case Display.STATE_DOZE: 148 return SurfaceControl.POWER_MODE_DOZE; 149 case Display.STATE_DOZE_SUSPEND: 150 return SurfaceControl.POWER_MODE_DOZE_SUSPEND; 151 default: 152 return SurfaceControl.POWER_MODE_NORMAL; 153 } 154 } 155 156 private final class LocalDisplayDevice extends DisplayDevice { 157 private final int mBuiltInDisplayId; 158 private final Light mBacklight; 159 private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>(); 160 private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>(); 161 162 private DisplayDeviceInfo mInfo; 163 private boolean mHavePendingChanges; 164 private int mState = Display.STATE_UNKNOWN; 165 private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT; 166 private int mActivePhysIndex; 167 private int mDefaultModeId; 168 private int mActiveModeId; 169 private boolean mActiveModeInvalid; 170 private int mActiveColorMode; 171 private boolean mActiveColorModeInvalid; 172 private Display.HdrCapabilities mHdrCapabilities; 173 174 private SurfaceControl.PhysicalDisplayInfo mDisplayInfos[]; 175 176 public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId, 177 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo, 178 int[] colorModes, int activeColorMode) { 179 super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + builtInDisplayId); 180 mBuiltInDisplayId = builtInDisplayId; 181 updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo, 182 colorModes, activeColorMode); 183 updateColorModesLocked(colorModes, activeColorMode); 184 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { 185 LightsManager lights = LocalServices.getService(LightsManager.class); 186 mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT); 187 } else { 188 mBacklight = null; 189 } 190 mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken); 191 } 192 193 @Override 194 public boolean hasStableUniqueId() { 195 return true; 196 } 197 198 public boolean updatePhysicalDisplayInfoLocked( 199 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo, 200 int[] colorModes, int activeColorMode) { 201 mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length); 202 mActivePhysIndex = activeDisplayInfo; 203 // Build an updated list of all existing modes. 204 ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>(); 205 boolean modesAdded = false; 206 for (int i = 0; i < physicalDisplayInfos.length; i++) { 207 SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i]; 208 // First, check to see if we've already added a matching mode. Since not all 209 // configuration options are exposed via Display.Mode, it's possible that we have 210 // multiple PhysicalDisplayInfos that would generate the same Display.Mode. 211 boolean existingMode = false; 212 for (int j = 0; j < records.size(); j++) { 213 if (records.get(j).hasMatchingMode(info)) { 214 existingMode = true; 215 break; 216 } 217 } 218 if (existingMode) { 219 continue; 220 } 221 // If we haven't already added a mode for this configuration to the new set of 222 // supported modes then check to see if we have one in the prior set of supported 223 // modes to reuse. 224 DisplayModeRecord record = findDisplayModeRecord(info); 225 if (record == null) { 226 record = new DisplayModeRecord(info); 227 modesAdded = true; 228 } 229 records.add(record); 230 } 231 232 // Get the currently active mode 233 DisplayModeRecord activeRecord = null; 234 for (int i = 0; i < records.size(); i++) { 235 DisplayModeRecord record = records.get(i); 236 if (record.hasMatchingMode(physicalDisplayInfos[activeDisplayInfo])){ 237 activeRecord = record; 238 break; 239 } 240 } 241 // Check whether surface flinger spontaneously changed modes out from under us. Schedule 242 // traversals to ensure that the correct state is reapplied if necessary. 243 if (mActiveModeId != 0 244 && mActiveModeId != activeRecord.mMode.getModeId()) { 245 mActiveModeInvalid = true; 246 sendTraversalRequestLocked(); 247 } 248 249 boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded; 250 // If the records haven't changed then we're done here. 251 if (!recordsChanged) { 252 return false; 253 } 254 // Update the index of modes. 255 mHavePendingChanges = true; 256 257 mSupportedModes.clear(); 258 for (DisplayModeRecord record : records) { 259 mSupportedModes.put(record.mMode.getModeId(), record); 260 } 261 // Update the default mode, if needed. 262 if (findDisplayInfoIndexLocked(mDefaultModeId) < 0) { 263 if (mDefaultModeId != 0) { 264 Slog.w(TAG, "Default display mode no longer available, using currently" 265 + " active mode as default."); 266 } 267 mDefaultModeId = activeRecord.mMode.getModeId(); 268 } 269 // Determine whether the active mode is still there. 270 if (mSupportedModes.indexOfKey(mActiveModeId) < 0) { 271 if (mActiveModeId != 0) { 272 Slog.w(TAG, "Active display mode no longer available, reverting to default" 273 + " mode."); 274 } 275 mActiveModeId = mDefaultModeId; 276 mActiveModeInvalid = true; 277 } 278 279 // Schedule traversals so that we apply pending changes. 280 sendTraversalRequestLocked(); 281 return true; 282 } 283 284 private boolean updateColorModesLocked(int[] colorModes, 285 int activeColorMode) { 286 List<Integer> pendingColorModes = new ArrayList<>(); 287 288 // Build an updated list of all existing color modes. 289 boolean colorModesAdded = false; 290 for (int colorMode: colorModes) { 291 if (!mSupportedColorModes.contains(colorMode)) { 292 colorModesAdded = true; 293 } 294 pendingColorModes.add(colorMode); 295 } 296 297 boolean colorModesChanged = 298 pendingColorModes.size() != mSupportedColorModes.size() 299 || colorModesAdded; 300 301 // If the supported color modes haven't changed then we're done here. 302 if (!colorModesChanged) { 303 return false; 304 } 305 306 mHavePendingChanges = true; 307 308 mSupportedColorModes.clear(); 309 mSupportedColorModes.addAll(pendingColorModes); 310 Collections.sort(mSupportedColorModes); 311 312 // Determine whether the active color mode is still there. 313 if (!mSupportedColorModes.contains(mActiveColorMode)) { 314 if (mActiveColorMode != 0) { 315 Slog.w(TAG, "Active color mode no longer available, reverting" 316 + " to default mode."); 317 mActiveColorMode = Display.COLOR_MODE_DEFAULT; 318 mActiveColorModeInvalid = true; 319 } else { 320 if (!mSupportedColorModes.isEmpty()) { 321 // This should never happen. 322 Slog.e(TAG, "Default and active color mode is no longer available!" 323 + " Reverting to first available mode."); 324 mActiveColorMode = mSupportedColorModes.get(0); 325 mActiveColorModeInvalid = true; 326 } else { 327 // This should really never happen. 328 Slog.e(TAG, "No color modes available!"); 329 } 330 } 331 } 332 return true; 333 } 334 335 private DisplayModeRecord findDisplayModeRecord(SurfaceControl.PhysicalDisplayInfo info) { 336 for (int i = 0; i < mSupportedModes.size(); i++) { 337 DisplayModeRecord record = mSupportedModes.valueAt(i); 338 if (record.hasMatchingMode(info)) { 339 return record; 340 } 341 } 342 return null; 343 } 344 345 @Override 346 public void applyPendingDisplayDeviceInfoChangesLocked() { 347 if (mHavePendingChanges) { 348 mInfo = null; 349 mHavePendingChanges = false; 350 } 351 } 352 353 @Override 354 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 355 if (mInfo == null) { 356 SurfaceControl.PhysicalDisplayInfo phys = mDisplayInfos[mActivePhysIndex]; 357 mInfo = new DisplayDeviceInfo(); 358 mInfo.width = phys.width; 359 mInfo.height = phys.height; 360 mInfo.modeId = mActiveModeId; 361 mInfo.defaultModeId = mDefaultModeId; 362 mInfo.supportedModes = new Display.Mode[mSupportedModes.size()]; 363 for (int i = 0; i < mSupportedModes.size(); i++) { 364 DisplayModeRecord record = mSupportedModes.valueAt(i); 365 mInfo.supportedModes[i] = record.mMode; 366 } 367 mInfo.colorMode = mActiveColorMode; 368 mInfo.supportedColorModes = 369 new int[mSupportedColorModes.size()]; 370 for (int i = 0; i < mSupportedColorModes.size(); i++) { 371 mInfo.supportedColorModes[i] = mSupportedColorModes.get(i); 372 } 373 mInfo.hdrCapabilities = mHdrCapabilities; 374 mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos; 375 mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos; 376 mInfo.state = mState; 377 mInfo.uniqueId = getUniqueId(); 378 379 // Assume that all built-in displays that have secure output (eg. HDCP) also 380 // support compositing from gralloc protected buffers. 381 if (phys.secure) { 382 mInfo.flags = DisplayDeviceInfo.FLAG_SECURE 383 | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS; 384 } 385 386 final Resources res = getContext().getResources(); 387 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { 388 mInfo.name = res.getString( 389 com.android.internal.R.string.display_manager_built_in_display_name); 390 mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY 391 | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; 392 if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound) 393 || (Build.IS_EMULATOR 394 && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) { 395 mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND; 396 } 397 mInfo.type = Display.TYPE_BUILT_IN; 398 mInfo.densityDpi = (int)(phys.density * 160 + 0.5f); 399 mInfo.xDpi = phys.xDpi; 400 mInfo.yDpi = phys.yDpi; 401 mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL; 402 } else { 403 mInfo.type = Display.TYPE_HDMI; 404 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; 405 mInfo.name = getContext().getResources().getString( 406 com.android.internal.R.string.display_manager_hdmi_display_name); 407 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; 408 mInfo.setAssumedDensityForExternalDisplay(phys.width, phys.height); 409 410 // For demonstration purposes, allow rotation of the external display. 411 // In the future we might allow the user to configure this directly. 412 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { 413 mInfo.rotation = Surface.ROTATION_270; 414 } 415 416 // For demonstration purposes, allow rotation of the external display 417 // to follow the built-in display. 418 if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) { 419 mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; 420 } 421 422 if (!res.getBoolean( 423 com.android.internal.R.bool.config_localDisplaysMirrorContent)) { 424 mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY; 425 } 426 } 427 } 428 return mInfo; 429 } 430 431 @Override 432 public Runnable requestDisplayStateLocked(final int state, final int brightness) { 433 // Assume that the brightness is off if the display is being turned off. 434 assert state != Display.STATE_OFF || brightness == PowerManager.BRIGHTNESS_OFF; 435 436 final boolean stateChanged = (mState != state); 437 final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null; 438 if (stateChanged || brightnessChanged) { 439 final int displayId = mBuiltInDisplayId; 440 final IBinder token = getDisplayTokenLocked(); 441 final int oldState = mState; 442 443 if (stateChanged) { 444 mState = state; 445 updateDeviceInfoLocked(); 446 } 447 448 if (brightnessChanged) { 449 mBrightness = brightness; 450 } 451 452 // Defer actually setting the display state until after we have exited 453 // the critical section since it can take hundreds of milliseconds 454 // to complete. 455 return new Runnable() { 456 @Override 457 public void run() { 458 // Exit a suspended state before making any changes. 459 int currentState = oldState; 460 if (Display.isSuspendedState(oldState) 461 || oldState == Display.STATE_UNKNOWN) { 462 if (!Display.isSuspendedState(state)) { 463 setDisplayState(state); 464 currentState = state; 465 } else if (state == Display.STATE_DOZE_SUSPEND 466 || oldState == Display.STATE_DOZE_SUSPEND) { 467 setDisplayState(Display.STATE_DOZE); 468 currentState = Display.STATE_DOZE; 469 } else { 470 return; // old state and new state is off 471 } 472 } 473 474 // If the state change was from or to VR, then we need to tell the light 475 // so that it can apply appropriate VR brightness settings. This should 476 // happen prior to changing the brightness but also if there is no 477 // brightness change at all. 478 if ((state == Display.STATE_VR || currentState == Display.STATE_VR) && 479 currentState != state) { 480 setVrMode(state == Display.STATE_VR); 481 } 482 483 484 // Apply brightness changes given that we are in a non-suspended state. 485 if (brightnessChanged) { 486 setDisplayBrightness(brightness); 487 } 488 489 // Enter the final desired state, possibly suspended. 490 if (state != currentState) { 491 setDisplayState(state); 492 } 493 } 494 495 private void setVrMode(boolean isVrEnabled) { 496 if (DEBUG) { 497 Slog.d(TAG, "setVrMode(" 498 + "id=" + displayId 499 + ", state=" + Display.stateToString(state) + ")"); 500 } 501 mBacklight.setVrMode(isVrEnabled); 502 } 503 504 private void setDisplayState(int state) { 505 if (DEBUG) { 506 Slog.d(TAG, "setDisplayState(" 507 + "id=" + displayId 508 + ", state=" + Display.stateToString(state) + ")"); 509 } 510 511 Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState(" 512 + "id=" + displayId 513 + ", state=" + Display.stateToString(state) + ")"); 514 try { 515 final int mode = getPowerModeForState(state); 516 SurfaceControl.setDisplayPowerMode(token, mode); 517 } finally { 518 Trace.traceEnd(Trace.TRACE_TAG_POWER); 519 } 520 } 521 522 private void setDisplayBrightness(int brightness) { 523 if (DEBUG) { 524 Slog.d(TAG, "setDisplayBrightness(" 525 + "id=" + displayId + ", brightness=" + brightness + ")"); 526 } 527 528 Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness(" 529 + "id=" + displayId + ", brightness=" + brightness + ")"); 530 try { 531 mBacklight.setBrightness(brightness); 532 } finally { 533 Trace.traceEnd(Trace.TRACE_TAG_POWER); 534 } 535 } 536 }; 537 } 538 return null; 539 } 540 541 @Override 542 public void requestDisplayModesInTransactionLocked( 543 int colorMode, int modeId) { 544 if (requestModeInTransactionLocked(modeId) || 545 requestColorModeInTransactionLocked(colorMode)) { 546 updateDeviceInfoLocked(); 547 } 548 } 549 550 public boolean requestModeInTransactionLocked(int modeId) { 551 if (modeId == 0) { 552 modeId = mDefaultModeId; 553 } else if (mSupportedModes.indexOfKey(modeId) < 0) { 554 Slog.w(TAG, "Requested mode " + modeId + " is not supported by this display," 555 + " reverting to default display mode."); 556 modeId = mDefaultModeId; 557 } 558 559 int physIndex = findDisplayInfoIndexLocked(modeId); 560 if (physIndex < 0) { 561 Slog.w(TAG, "Requested mode ID " + modeId + " not available," 562 + " trying with default mode ID"); 563 modeId = mDefaultModeId; 564 physIndex = findDisplayInfoIndexLocked(modeId); 565 } 566 if (mActivePhysIndex == physIndex) { 567 return false; 568 } 569 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex); 570 mActivePhysIndex = physIndex; 571 mActiveModeId = modeId; 572 mActiveModeInvalid = false; 573 return true; 574 } 575 576 public boolean requestColorModeInTransactionLocked(int colorMode) { 577 if (mActiveColorMode == colorMode) { 578 return false; 579 } 580 if (!mSupportedColorModes.contains(colorMode)) { 581 Slog.w(TAG, "Unable to find color mode " + colorMode 582 + ", ignoring request."); 583 return false; 584 } 585 SurfaceControl.setActiveColorMode(getDisplayTokenLocked(), colorMode); 586 mActiveColorMode = colorMode; 587 mActiveColorModeInvalid = false; 588 return true; 589 } 590 591 @Override 592 public void dumpLocked(PrintWriter pw) { 593 super.dumpLocked(pw); 594 pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); 595 pw.println("mActivePhysIndex=" + mActivePhysIndex); 596 pw.println("mActiveModeId=" + mActiveModeId); 597 pw.println("mActiveColorMode=" + mActiveColorMode); 598 pw.println("mState=" + Display.stateToString(mState)); 599 pw.println("mBrightness=" + mBrightness); 600 pw.println("mBacklight=" + mBacklight); 601 pw.println("mDisplayInfos="); 602 for (int i = 0; i < mDisplayInfos.length; i++) { 603 pw.println(" " + mDisplayInfos[i]); 604 } 605 pw.println("mSupportedModes="); 606 for (int i = 0; i < mSupportedModes.size(); i++) { 607 pw.println(" " + mSupportedModes.valueAt(i)); 608 } 609 pw.print("mSupportedColorModes=["); 610 for (int i = 0; i < mSupportedColorModes.size(); i++) { 611 if (i != 0) { 612 pw.print(", "); 613 } 614 pw.print(mSupportedColorModes.get(i)); 615 } 616 pw.println("]"); 617 } 618 619 private int findDisplayInfoIndexLocked(int modeId) { 620 DisplayModeRecord record = mSupportedModes.get(modeId); 621 if (record != null) { 622 for (int i = 0; i < mDisplayInfos.length; i++) { 623 SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[i]; 624 if (record.hasMatchingMode(info)){ 625 return i; 626 } 627 } 628 } 629 return -1; 630 } 631 632 private void updateDeviceInfoLocked() { 633 mInfo = null; 634 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 635 } 636 } 637 638 /** 639 * Keeps track of a display configuration. 640 */ 641 private static final class DisplayModeRecord { 642 public final Display.Mode mMode; 643 644 public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys) { 645 mMode = createMode(phys.width, phys.height, phys.refreshRate); 646 } 647 648 /** 649 * Returns whether the mode generated by the given PhysicalDisplayInfo matches the mode 650 * contained by the record modulo mode ID. 651 * 652 * Note that this doesn't necessarily mean the the PhysicalDisplayInfos are identical, just 653 * that they generate identical modes. 654 */ 655 public boolean hasMatchingMode(SurfaceControl.PhysicalDisplayInfo info) { 656 int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate()); 657 int displayInfoRefreshRate = Float.floatToIntBits(info.refreshRate); 658 return mMode.getPhysicalWidth() == info.width 659 && mMode.getPhysicalHeight() == info.height 660 && modeRefreshRate == displayInfoRefreshRate; 661 } 662 663 public String toString() { 664 return "DisplayModeRecord{mMode=" + mMode + "}"; 665 } 666 } 667 668 private final class HotplugDisplayEventReceiver extends DisplayEventReceiver { 669 public HotplugDisplayEventReceiver(Looper looper) { 670 super(looper); 671 } 672 673 @Override 674 public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { 675 synchronized (getSyncRoot()) { 676 if (connected) { 677 tryConnectDisplayLocked(builtInDisplayId); 678 } else { 679 tryDisconnectDisplayLocked(builtInDisplayId); 680 } 681 } 682 } 683 } 684} 685