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 17 package com.android.server.display; 18 19 import android.content.res.Resources; 20 import com.android.server.LocalServices; 21 import com.android.server.lights.Light; 22 import com.android.server.lights.LightsManager; 23 24 import android.content.Context; 25 import android.os.Build; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.Looper; 29 import android.os.PowerManager; 30 import android.os.SystemProperties; 31 import android.os.Trace; 32 import android.util.Slog; 33 import android.util.SparseArray; 34 import android.view.Display; 35 import android.view.DisplayEventReceiver; 36 import android.view.Surface; 37 import android.view.SurfaceControl; 38 39 import java.io.PrintWriter; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.Collections; 43 import 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 */ 51 final 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 if (colorModes == null) return false; 289 // Build an updated list of all existing color modes. 290 boolean colorModesAdded = false; 291 for (int colorMode: colorModes) { 292 if (!mSupportedColorModes.contains(colorMode)) { 293 colorModesAdded = true; 294 } 295 pendingColorModes.add(colorMode); 296 } 297 298 boolean colorModesChanged = 299 pendingColorModes.size() != mSupportedColorModes.size() 300 || colorModesAdded; 301 302 // If the supported color modes haven't changed then we're done here. 303 if (!colorModesChanged) { 304 return false; 305 } 306 307 mHavePendingChanges = true; 308 309 mSupportedColorModes.clear(); 310 mSupportedColorModes.addAll(pendingColorModes); 311 Collections.sort(mSupportedColorModes); 312 313 // Determine whether the active color mode is still there. 314 if (!mSupportedColorModes.contains(mActiveColorMode)) { 315 if (mActiveColorMode != 0) { 316 Slog.w(TAG, "Active color mode no longer available, reverting" 317 + " to default mode."); 318 mActiveColorMode = Display.COLOR_MODE_DEFAULT; 319 mActiveColorModeInvalid = true; 320 } else { 321 if (!mSupportedColorModes.isEmpty()) { 322 // This should never happen. 323 Slog.e(TAG, "Default and active color mode is no longer available!" 324 + " Reverting to first available mode."); 325 mActiveColorMode = mSupportedColorModes.get(0); 326 mActiveColorModeInvalid = true; 327 } else { 328 // This should really never happen. 329 Slog.e(TAG, "No color modes available!"); 330 } 331 } 332 } 333 return true; 334 } 335 336 private DisplayModeRecord findDisplayModeRecord(SurfaceControl.PhysicalDisplayInfo info) { 337 for (int i = 0; i < mSupportedModes.size(); i++) { 338 DisplayModeRecord record = mSupportedModes.valueAt(i); 339 if (record.hasMatchingMode(info)) { 340 return record; 341 } 342 } 343 return null; 344 } 345 346 @Override 347 public void applyPendingDisplayDeviceInfoChangesLocked() { 348 if (mHavePendingChanges) { 349 mInfo = null; 350 mHavePendingChanges = false; 351 } 352 } 353 354 @Override 355 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 356 if (mInfo == null) { 357 SurfaceControl.PhysicalDisplayInfo phys = mDisplayInfos[mActivePhysIndex]; 358 mInfo = new DisplayDeviceInfo(); 359 mInfo.width = phys.width; 360 mInfo.height = phys.height; 361 mInfo.modeId = mActiveModeId; 362 mInfo.defaultModeId = mDefaultModeId; 363 mInfo.supportedModes = new Display.Mode[mSupportedModes.size()]; 364 for (int i = 0; i < mSupportedModes.size(); i++) { 365 DisplayModeRecord record = mSupportedModes.valueAt(i); 366 mInfo.supportedModes[i] = record.mMode; 367 } 368 mInfo.colorMode = mActiveColorMode; 369 mInfo.supportedColorModes = 370 new int[mSupportedColorModes.size()]; 371 for (int i = 0; i < mSupportedColorModes.size(); i++) { 372 mInfo.supportedColorModes[i] = mSupportedColorModes.get(i); 373 } 374 mInfo.hdrCapabilities = mHdrCapabilities; 375 mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos; 376 mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos; 377 mInfo.state = mState; 378 mInfo.uniqueId = getUniqueId(); 379 380 // Assume that all built-in displays that have secure output (eg. HDCP) also 381 // support compositing from gralloc protected buffers. 382 if (phys.secure) { 383 mInfo.flags = DisplayDeviceInfo.FLAG_SECURE 384 | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS; 385 } 386 387 final Resources res = getContext().getResources(); 388 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { 389 mInfo.name = res.getString( 390 com.android.internal.R.string.display_manager_built_in_display_name); 391 mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY 392 | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; 393 if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound) 394 || (Build.IS_EMULATOR 395 && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) { 396 mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND; 397 } 398 mInfo.type = Display.TYPE_BUILT_IN; 399 mInfo.densityDpi = (int)(phys.density * 160 + 0.5f); 400 mInfo.xDpi = phys.xDpi; 401 mInfo.yDpi = phys.yDpi; 402 mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL; 403 } else { 404 mInfo.type = Display.TYPE_HDMI; 405 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; 406 mInfo.name = getContext().getResources().getString( 407 com.android.internal.R.string.display_manager_hdmi_display_name); 408 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; 409 mInfo.setAssumedDensityForExternalDisplay(phys.width, phys.height); 410 411 // For demonstration purposes, allow rotation of the external display. 412 // In the future we might allow the user to configure this directly. 413 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { 414 mInfo.rotation = Surface.ROTATION_270; 415 } 416 417 // For demonstration purposes, allow rotation of the external display 418 // to follow the built-in display. 419 if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) { 420 mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; 421 } 422 423 if (!res.getBoolean( 424 com.android.internal.R.bool.config_localDisplaysMirrorContent)) { 425 mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY; 426 } 427 } 428 } 429 return mInfo; 430 } 431 432 @Override 433 public Runnable requestDisplayStateLocked(final int state, final int brightness) { 434 // Assume that the brightness is off if the display is being turned off. 435 assert state != Display.STATE_OFF || brightness == PowerManager.BRIGHTNESS_OFF; 436 437 final boolean stateChanged = (mState != state); 438 final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null; 439 if (stateChanged || brightnessChanged) { 440 final int displayId = mBuiltInDisplayId; 441 final IBinder token = getDisplayTokenLocked(); 442 final int oldState = mState; 443 444 if (stateChanged) { 445 mState = state; 446 updateDeviceInfoLocked(); 447 } 448 449 if (brightnessChanged) { 450 mBrightness = brightness; 451 } 452 453 // Defer actually setting the display state until after we have exited 454 // the critical section since it can take hundreds of milliseconds 455 // to complete. 456 return new Runnable() { 457 @Override 458 public void run() { 459 // Exit a suspended state before making any changes. 460 int currentState = oldState; 461 if (Display.isSuspendedState(oldState) 462 || oldState == Display.STATE_UNKNOWN) { 463 if (!Display.isSuspendedState(state)) { 464 setDisplayState(state); 465 currentState = state; 466 } else if (state == Display.STATE_DOZE_SUSPEND 467 || oldState == Display.STATE_DOZE_SUSPEND) { 468 setDisplayState(Display.STATE_DOZE); 469 currentState = Display.STATE_DOZE; 470 } else { 471 return; // old state and new state is off 472 } 473 } 474 475 // If the state change was from or to VR, then we need to tell the light 476 // so that it can apply appropriate VR brightness settings. This should 477 // happen prior to changing the brightness but also if there is no 478 // brightness change at all. 479 if ((state == Display.STATE_VR || currentState == Display.STATE_VR) && 480 currentState != state) { 481 setVrMode(state == Display.STATE_VR); 482 } 483 484 485 // Apply brightness changes given that we are in a non-suspended state. 486 if (brightnessChanged) { 487 setDisplayBrightness(brightness); 488 } 489 490 // Enter the final desired state, possibly suspended. 491 if (state != currentState) { 492 setDisplayState(state); 493 } 494 } 495 496 private void setVrMode(boolean isVrEnabled) { 497 if (DEBUG) { 498 Slog.d(TAG, "setVrMode(" 499 + "id=" + displayId 500 + ", state=" + Display.stateToString(state) + ")"); 501 } 502 mBacklight.setVrMode(isVrEnabled); 503 } 504 505 private void setDisplayState(int state) { 506 if (DEBUG) { 507 Slog.d(TAG, "setDisplayState(" 508 + "id=" + displayId 509 + ", state=" + Display.stateToString(state) + ")"); 510 } 511 512 Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState(" 513 + "id=" + displayId 514 + ", state=" + Display.stateToString(state) + ")"); 515 try { 516 final int mode = getPowerModeForState(state); 517 SurfaceControl.setDisplayPowerMode(token, mode); 518 } finally { 519 Trace.traceEnd(Trace.TRACE_TAG_POWER); 520 } 521 } 522 523 private void setDisplayBrightness(int brightness) { 524 if (DEBUG) { 525 Slog.d(TAG, "setDisplayBrightness(" 526 + "id=" + displayId + ", brightness=" + brightness + ")"); 527 } 528 529 Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness(" 530 + "id=" + displayId + ", brightness=" + brightness + ")"); 531 try { 532 mBacklight.setBrightness(brightness); 533 } finally { 534 Trace.traceEnd(Trace.TRACE_TAG_POWER); 535 } 536 } 537 }; 538 } 539 return null; 540 } 541 542 @Override 543 public void requestDisplayModesInTransactionLocked( 544 int colorMode, int modeId) { 545 if (requestModeInTransactionLocked(modeId) || 546 requestColorModeInTransactionLocked(colorMode)) { 547 updateDeviceInfoLocked(); 548 } 549 } 550 551 public boolean requestModeInTransactionLocked(int modeId) { 552 if (modeId == 0) { 553 modeId = mDefaultModeId; 554 } else if (mSupportedModes.indexOfKey(modeId) < 0) { 555 Slog.w(TAG, "Requested mode " + modeId + " is not supported by this display," 556 + " reverting to default display mode."); 557 modeId = mDefaultModeId; 558 } 559 560 int physIndex = findDisplayInfoIndexLocked(modeId); 561 if (physIndex < 0) { 562 Slog.w(TAG, "Requested mode ID " + modeId + " not available," 563 + " trying with default mode ID"); 564 modeId = mDefaultModeId; 565 physIndex = findDisplayInfoIndexLocked(modeId); 566 } 567 if (mActivePhysIndex == physIndex) { 568 return false; 569 } 570 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex); 571 mActivePhysIndex = physIndex; 572 mActiveModeId = modeId; 573 mActiveModeInvalid = false; 574 return true; 575 } 576 577 public boolean requestColorModeInTransactionLocked(int colorMode) { 578 if (mActiveColorMode == colorMode) { 579 return false; 580 } 581 if (!mSupportedColorModes.contains(colorMode)) { 582 Slog.w(TAG, "Unable to find color mode " + colorMode 583 + ", ignoring request."); 584 return false; 585 } 586 SurfaceControl.setActiveColorMode(getDisplayTokenLocked(), colorMode); 587 mActiveColorMode = colorMode; 588 mActiveColorModeInvalid = false; 589 return true; 590 } 591 592 @Override 593 public void dumpLocked(PrintWriter pw) { 594 super.dumpLocked(pw); 595 pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); 596 pw.println("mActivePhysIndex=" + mActivePhysIndex); 597 pw.println("mActiveModeId=" + mActiveModeId); 598 pw.println("mActiveColorMode=" + mActiveColorMode); 599 pw.println("mState=" + Display.stateToString(mState)); 600 pw.println("mBrightness=" + mBrightness); 601 pw.println("mBacklight=" + mBacklight); 602 pw.println("mDisplayInfos="); 603 for (int i = 0; i < mDisplayInfos.length; i++) { 604 pw.println(" " + mDisplayInfos[i]); 605 } 606 pw.println("mSupportedModes="); 607 for (int i = 0; i < mSupportedModes.size(); i++) { 608 pw.println(" " + mSupportedModes.valueAt(i)); 609 } 610 pw.print("mSupportedColorModes=["); 611 for (int i = 0; i < mSupportedColorModes.size(); i++) { 612 if (i != 0) { 613 pw.print(", "); 614 } 615 pw.print(mSupportedColorModes.get(i)); 616 } 617 pw.println("]"); 618 } 619 620 private int findDisplayInfoIndexLocked(int modeId) { 621 DisplayModeRecord record = mSupportedModes.get(modeId); 622 if (record != null) { 623 for (int i = 0; i < mDisplayInfos.length; i++) { 624 SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[i]; 625 if (record.hasMatchingMode(info)){ 626 return i; 627 } 628 } 629 } 630 return -1; 631 } 632 633 private void updateDeviceInfoLocked() { 634 mInfo = null; 635 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 636 } 637 } 638 639 /** 640 * Keeps track of a display configuration. 641 */ 642 private static final class DisplayModeRecord { 643 public final Display.Mode mMode; 644 645 public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys) { 646 mMode = createMode(phys.width, phys.height, phys.refreshRate); 647 } 648 649 /** 650 * Returns whether the mode generated by the given PhysicalDisplayInfo matches the mode 651 * contained by the record modulo mode ID. 652 * 653 * Note that this doesn't necessarily mean the the PhysicalDisplayInfos are identical, just 654 * that they generate identical modes. 655 */ 656 public boolean hasMatchingMode(SurfaceControl.PhysicalDisplayInfo info) { 657 int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate()); 658 int displayInfoRefreshRate = Float.floatToIntBits(info.refreshRate); 659 return mMode.getPhysicalWidth() == info.width 660 && mMode.getPhysicalHeight() == info.height 661 && modeRefreshRate == displayInfoRefreshRate; 662 } 663 664 public String toString() { 665 return "DisplayModeRecord{mMode=" + mMode + "}"; 666 } 667 } 668 669 private final class HotplugDisplayEventReceiver extends DisplayEventReceiver { 670 public HotplugDisplayEventReceiver(Looper looper) { 671 super(looper, VSYNC_SOURCE_APP); 672 } 673 674 @Override 675 public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { 676 synchronized (getSyncRoot()) { 677 if (connected) { 678 tryConnectDisplayLocked(builtInDisplayId); 679 } else { 680 tryDisconnectDisplayLocked(builtInDisplayId); 681 } 682 } 683 } 684 } 685 } 686