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; 42 43/** 44 * A display adapter for the local displays managed by Surface Flinger. 45 * <p> 46 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 47 * </p> 48 */ 49final class LocalDisplayAdapter extends DisplayAdapter { 50 private static final String TAG = "LocalDisplayAdapter"; 51 private static final boolean DEBUG = false; 52 53 private static final String UNIQUE_ID_PREFIX = "local:"; 54 55 private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular"; 56 57 private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] { 58 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN, 59 SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI, 60 }; 61 62 private final SparseArray<LocalDisplayDevice> mDevices = 63 new SparseArray<LocalDisplayDevice>(); 64 @SuppressWarnings("unused") // Becomes active at instantiation time. 65 private HotplugDisplayEventReceiver mHotplugReceiver; 66 67 // Called with SyncRoot lock held. 68 public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 69 Context context, Handler handler, Listener listener) { 70 super(syncRoot, context, handler, listener, TAG); 71 } 72 73 @Override 74 public void registerLocked() { 75 super.registerLocked(); 76 77 mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper()); 78 79 for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) { 80 tryConnectDisplayLocked(builtInDisplayId); 81 } 82 } 83 84 private void tryConnectDisplayLocked(int builtInDisplayId) { 85 IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId); 86 if (displayToken != null) { 87 SurfaceControl.PhysicalDisplayInfo[] configs = 88 SurfaceControl.getDisplayConfigs(displayToken); 89 if (configs == null) { 90 // There are no valid configs for this device, so we can't use it 91 Slog.w(TAG, "No valid configs found for display device " + 92 builtInDisplayId); 93 return; 94 } 95 int activeConfig = SurfaceControl.getActiveConfig(displayToken); 96 if (activeConfig < 0) { 97 // There is no active config, and for now we don't have the 98 // policy to set one. 99 Slog.w(TAG, "No active config found for display device " + 100 builtInDisplayId); 101 return; 102 } 103 LocalDisplayDevice device = mDevices.get(builtInDisplayId); 104 if (device == null) { 105 // Display was added. 106 device = new LocalDisplayDevice(displayToken, builtInDisplayId, 107 configs, activeConfig); 108 mDevices.put(builtInDisplayId, device); 109 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); 110 } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig)) { 111 // Display properties changed. 112 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); 113 } 114 } else { 115 // The display is no longer available. Ignore the attempt to add it. 116 // If it was connected but has already been disconnected, we'll get a 117 // disconnect event that will remove it from mDevices. 118 } 119 } 120 121 private void tryDisconnectDisplayLocked(int builtInDisplayId) { 122 LocalDisplayDevice device = mDevices.get(builtInDisplayId); 123 if (device != null) { 124 // Display was removed. 125 mDevices.remove(builtInDisplayId); 126 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED); 127 } 128 } 129 130 static int getPowerModeForState(int state) { 131 switch (state) { 132 case Display.STATE_OFF: 133 return SurfaceControl.POWER_MODE_OFF; 134 case Display.STATE_DOZE: 135 return SurfaceControl.POWER_MODE_DOZE; 136 case Display.STATE_DOZE_SUSPEND: 137 return SurfaceControl.POWER_MODE_DOZE_SUSPEND; 138 default: 139 return SurfaceControl.POWER_MODE_NORMAL; 140 } 141 } 142 143 private final class LocalDisplayDevice extends DisplayDevice { 144 private final int mBuiltInDisplayId; 145 private final Light mBacklight; 146 private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>(); 147 private final SparseArray<Display.ColorTransform> mSupportedColorTransforms = 148 new SparseArray<>(); 149 150 private DisplayDeviceInfo mInfo; 151 private boolean mHavePendingChanges; 152 private int mState = Display.STATE_UNKNOWN; 153 private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT; 154 private int mActivePhysIndex; 155 private int mDefaultModeId; 156 private int mActiveModeId; 157 private boolean mActiveModeInvalid; 158 private int mDefaultColorTransformId; 159 private int mActiveColorTransformId; 160 private boolean mActiveColorTransformInvalid; 161 private Display.HdrCapabilities mHdrCapabilities; 162 163 private SurfaceControl.PhysicalDisplayInfo mDisplayInfos[]; 164 165 public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId, 166 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) { 167 super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + builtInDisplayId); 168 mBuiltInDisplayId = builtInDisplayId; 169 updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo); 170 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { 171 LightsManager lights = LocalServices.getService(LightsManager.class); 172 mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT); 173 } else { 174 mBacklight = null; 175 } 176 mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken); 177 } 178 179 public boolean updatePhysicalDisplayInfoLocked( 180 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) { 181 mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length); 182 mActivePhysIndex = activeDisplayInfo; 183 ArrayList<Display.ColorTransform> colorTransforms = new ArrayList<>(); 184 185 // Build an updated list of all existing color transforms. 186 boolean colorTransformsAdded = false; 187 Display.ColorTransform activeColorTransform = null; 188 for (int i = 0; i < physicalDisplayInfos.length; i++) { 189 SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i]; 190 // First check to see if we've already added this color transform 191 boolean existingMode = false; 192 for (int j = 0; j < colorTransforms.size(); j++) { 193 if (colorTransforms.get(j).getColorTransform() == info.colorTransform) { 194 existingMode = true; 195 break; 196 } 197 } 198 if (existingMode) { 199 continue; 200 } 201 Display.ColorTransform colorTransform = findColorTransform(info); 202 if (colorTransform == null) { 203 colorTransform = createColorTransform(info.colorTransform); 204 colorTransformsAdded = true; 205 } 206 colorTransforms.add(colorTransform); 207 if (i == activeDisplayInfo) { 208 activeColorTransform = colorTransform; 209 } 210 } 211 212 // Build an updated list of all existing modes. 213 ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>(); 214 boolean modesAdded = false; 215 for (int i = 0; i < physicalDisplayInfos.length; i++) { 216 SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i]; 217 // First, check to see if we've already added a matching mode. Since not all 218 // configuration options are exposed via Display.Mode, it's possible that we have 219 // multiple PhysicalDisplayInfos that would generate the same Display.Mode. 220 boolean existingMode = false; 221 for (int j = 0; j < records.size(); j++) { 222 if (records.get(j).hasMatchingMode(info)) { 223 existingMode = true; 224 break; 225 } 226 } 227 if (existingMode) { 228 continue; 229 } 230 // If we haven't already added a mode for this configuration to the new set of 231 // supported modes then check to see if we have one in the prior set of supported 232 // modes to reuse. 233 DisplayModeRecord record = findDisplayModeRecord(info); 234 if (record == null) { 235 record = new DisplayModeRecord(info); 236 modesAdded = true; 237 } 238 records.add(record); 239 } 240 241 // Get the currently active mode 242 DisplayModeRecord activeRecord = null; 243 for (int i = 0; i < records.size(); i++) { 244 DisplayModeRecord record = records.get(i); 245 if (record.hasMatchingMode(physicalDisplayInfos[activeDisplayInfo])){ 246 activeRecord = record; 247 break; 248 } 249 } 250 // Check whether surface flinger spontaneously changed modes out from under us. Schedule 251 // traversals to ensure that the correct state is reapplied if necessary. 252 if (mActiveModeId != 0 253 && mActiveModeId != activeRecord.mMode.getModeId()) { 254 mActiveModeInvalid = true; 255 sendTraversalRequestLocked(); 256 } 257 // Check whether surface flinger spontaneously changed color transforms out from under 258 // us. 259 if (mActiveColorTransformId != 0 260 && mActiveColorTransformId != activeColorTransform.getId()) { 261 mActiveColorTransformInvalid = true; 262 sendTraversalRequestLocked(); 263 } 264 265 boolean colorTransformsChanged = 266 colorTransforms.size() != mSupportedColorTransforms.size() 267 || colorTransformsAdded; 268 boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded; 269 // If neither the records nor the supported color transforms have changed then we're 270 // done here. 271 if (!recordsChanged && !colorTransformsChanged) { 272 return false; 273 } 274 // Update the index of modes. 275 mHavePendingChanges = true; 276 277 mSupportedModes.clear(); 278 for (DisplayModeRecord record : records) { 279 mSupportedModes.put(record.mMode.getModeId(), record); 280 } 281 mSupportedColorTransforms.clear(); 282 for (Display.ColorTransform colorTransform : colorTransforms) { 283 mSupportedColorTransforms.put(colorTransform.getId(), colorTransform); 284 } 285 286 // Update the default mode and color transform if needed. This needs to be done in 287 // tandem so we always have a default state to fall back to. 288 if (findDisplayInfoIndexLocked(mDefaultColorTransformId, mDefaultModeId) < 0) { 289 if (mDefaultModeId != 0) { 290 Slog.w(TAG, "Default display mode no longer available, using currently" 291 + " active mode as default."); 292 } 293 mDefaultModeId = activeRecord.mMode.getModeId(); 294 if (mDefaultColorTransformId != 0) { 295 Slog.w(TAG, "Default color transform no longer available, using currently" 296 + " active color transform as default"); 297 } 298 mDefaultColorTransformId = activeColorTransform.getId(); 299 } 300 // Determine whether the active mode is still there. 301 if (mSupportedModes.indexOfKey(mActiveModeId) < 0) { 302 if (mActiveModeId != 0) { 303 Slog.w(TAG, "Active display mode no longer available, reverting to default" 304 + " mode."); 305 } 306 mActiveModeId = mDefaultModeId; 307 mActiveModeInvalid = true; 308 } 309 310 // Determine whether the active color transform is still there. 311 if (mSupportedColorTransforms.indexOfKey(mActiveColorTransformId) < 0) { 312 if (mActiveColorTransformId != 0) { 313 Slog.w(TAG, "Active color transform no longer available, reverting" 314 + " to default transform."); 315 } 316 mActiveColorTransformId = mDefaultColorTransformId; 317 mActiveColorTransformInvalid = true; 318 } 319 // Schedule traversals so that we apply pending changes. 320 sendTraversalRequestLocked(); 321 return true; 322 } 323 324 private DisplayModeRecord findDisplayModeRecord(SurfaceControl.PhysicalDisplayInfo info) { 325 for (int i = 0; i < mSupportedModes.size(); i++) { 326 DisplayModeRecord record = mSupportedModes.valueAt(i); 327 if (record.hasMatchingMode(info)) { 328 return record; 329 } 330 } 331 return null; 332 } 333 334 private Display.ColorTransform findColorTransform(SurfaceControl.PhysicalDisplayInfo info) { 335 for (int i = 0; i < mSupportedColorTransforms.size(); i++) { 336 Display.ColorTransform transform = mSupportedColorTransforms.valueAt(i); 337 if (transform.getColorTransform() == info.colorTransform) { 338 return transform; 339 } 340 } 341 return null; 342 } 343 344 @Override 345 public void applyPendingDisplayDeviceInfoChangesLocked() { 346 if (mHavePendingChanges) { 347 mInfo = null; 348 mHavePendingChanges = false; 349 } 350 } 351 352 @Override 353 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 354 if (mInfo == null) { 355 SurfaceControl.PhysicalDisplayInfo phys = mDisplayInfos[mActivePhysIndex]; 356 mInfo = new DisplayDeviceInfo(); 357 mInfo.width = phys.width; 358 mInfo.height = phys.height; 359 mInfo.modeId = mActiveModeId; 360 mInfo.defaultModeId = mDefaultModeId; 361 mInfo.supportedModes = new Display.Mode[mSupportedModes.size()]; 362 for (int i = 0; i < mSupportedModes.size(); i++) { 363 DisplayModeRecord record = mSupportedModes.valueAt(i); 364 mInfo.supportedModes[i] = record.mMode; 365 } 366 mInfo.colorTransformId = mActiveColorTransformId; 367 mInfo.defaultColorTransformId = mDefaultColorTransformId; 368 mInfo.supportedColorTransforms = 369 new Display.ColorTransform[mSupportedColorTransforms.size()]; 370 for (int i = 0; i < mSupportedColorTransforms.size(); i++) { 371 mInfo.supportedColorTransforms[i] = mSupportedColorTransforms.valueAt(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 // Apply brightness changes given that we are in a non-suspended state. 475 if (brightnessChanged) { 476 setDisplayBrightness(brightness); 477 } 478 479 // Enter the final desired state, possibly suspended. 480 if (state != currentState) { 481 setDisplayState(state); 482 } 483 } 484 485 private void setDisplayState(int state) { 486 if (DEBUG) { 487 Slog.d(TAG, "setDisplayState(" 488 + "id=" + displayId 489 + ", state=" + Display.stateToString(state) + ")"); 490 } 491 492 Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState(" 493 + "id=" + displayId 494 + ", state=" + Display.stateToString(state) + ")"); 495 try { 496 final int mode = getPowerModeForState(state); 497 SurfaceControl.setDisplayPowerMode(token, mode); 498 } finally { 499 Trace.traceEnd(Trace.TRACE_TAG_POWER); 500 } 501 } 502 503 private void setDisplayBrightness(int brightness) { 504 if (DEBUG) { 505 Slog.d(TAG, "setDisplayBrightness(" 506 + "id=" + displayId + ", brightness=" + brightness + ")"); 507 } 508 509 Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness(" 510 + "id=" + displayId + ", brightness=" + brightness + ")"); 511 try { 512 mBacklight.setBrightness(brightness); 513 } finally { 514 Trace.traceEnd(Trace.TRACE_TAG_POWER); 515 } 516 } 517 }; 518 } 519 return null; 520 } 521 522 @Override 523 public void requestColorTransformAndModeInTransactionLocked( 524 int colorTransformId, int modeId) { 525 if (modeId == 0) { 526 modeId = mDefaultModeId; 527 } else if (mSupportedModes.indexOfKey(modeId) < 0) { 528 Slog.w(TAG, "Requested mode " + modeId + " is not supported by this display," 529 + " reverting to default display mode."); 530 modeId = mDefaultModeId; 531 } 532 533 if (colorTransformId == 0) { 534 colorTransformId = mDefaultColorTransformId; 535 } else if (mSupportedColorTransforms.indexOfKey(colorTransformId) < 0) { 536 Slog.w(TAG, "Requested color transform " + colorTransformId + " is not supported" 537 + " by this display, reverting to the default color transform"); 538 colorTransformId = mDefaultColorTransformId; 539 } 540 int physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId); 541 if (physIndex < 0) { 542 Slog.w(TAG, "Requested color transform, mode ID pair (" + colorTransformId + ", " 543 + modeId + ") not available, trying color transform with default mode ID"); 544 modeId = mDefaultModeId; 545 physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId); 546 if (physIndex < 0) { 547 Slog.w(TAG, "Requested color transform with default mode ID still not" 548 + " available, falling back to default color transform with default" 549 + " mode."); 550 colorTransformId = mDefaultColorTransformId; 551 physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId); 552 } 553 } 554 if (mActivePhysIndex == physIndex) { 555 return; 556 } 557 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex); 558 mActivePhysIndex = physIndex; 559 mActiveModeId = modeId; 560 mActiveModeInvalid = false; 561 mActiveColorTransformId = colorTransformId; 562 mActiveColorTransformInvalid = false; 563 updateDeviceInfoLocked(); 564 } 565 566 @Override 567 public void dumpLocked(PrintWriter pw) { 568 super.dumpLocked(pw); 569 pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); 570 pw.println("mActivePhysIndex=" + mActivePhysIndex); 571 pw.println("mActiveModeId=" + mActiveModeId); 572 pw.println("mActiveColorTransformId=" + mActiveColorTransformId); 573 pw.println("mState=" + Display.stateToString(mState)); 574 pw.println("mBrightness=" + mBrightness); 575 pw.println("mBacklight=" + mBacklight); 576 pw.println("mDisplayInfos="); 577 for (int i = 0; i < mDisplayInfos.length; i++) { 578 pw.println(" " + mDisplayInfos[i]); 579 } 580 pw.println("mSupportedModes="); 581 for (int i = 0; i < mSupportedModes.size(); i++) { 582 pw.println(" " + mSupportedModes.valueAt(i)); 583 } 584 pw.println("mSupportedColorTransforms=["); 585 for (int i = 0; i < mSupportedColorTransforms.size(); i++) { 586 if (i != 0) { 587 pw.print(", "); 588 } 589 pw.print(mSupportedColorTransforms.valueAt(i)); 590 } 591 pw.println("]"); 592 } 593 594 private int findDisplayInfoIndexLocked(int colorTransformId, int modeId) { 595 DisplayModeRecord record = mSupportedModes.get(modeId); 596 Display.ColorTransform transform = mSupportedColorTransforms.get(colorTransformId); 597 if (record != null && transform != null) { 598 for (int i = 0; i < mDisplayInfos.length; i++) { 599 SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[i]; 600 if (info.colorTransform == transform.getColorTransform() 601 && record.hasMatchingMode(info)){ 602 return i; 603 } 604 } 605 } 606 return -1; 607 } 608 609 private void updateDeviceInfoLocked() { 610 mInfo = null; 611 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 612 } 613 } 614 615 /** 616 * Keeps track of a display configuration. 617 */ 618 private static final class DisplayModeRecord { 619 public final Display.Mode mMode; 620 621 public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys) { 622 mMode = createMode(phys.width, phys.height, phys.refreshRate); 623 } 624 625 /** 626 * Returns whether the mode generated by the given PhysicalDisplayInfo matches the mode 627 * contained by the record modulo mode ID. 628 * 629 * Note that this doesn't necessarily mean the the PhysicalDisplayInfos are identical, just 630 * that they generate identical modes. 631 */ 632 public boolean hasMatchingMode(SurfaceControl.PhysicalDisplayInfo info) { 633 int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate()); 634 int displayInfoRefreshRate = Float.floatToIntBits(info.refreshRate); 635 return mMode.getPhysicalWidth() == info.width 636 && mMode.getPhysicalHeight() == info.height 637 && modeRefreshRate == displayInfoRefreshRate; 638 } 639 640 public String toString() { 641 return "DisplayModeRecord{mMode=" + mMode + "}"; 642 } 643 } 644 645 private final class HotplugDisplayEventReceiver extends DisplayEventReceiver { 646 public HotplugDisplayEventReceiver(Looper looper) { 647 super(looper); 648 } 649 650 @Override 651 public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { 652 synchronized (getSyncRoot()) { 653 if (connected) { 654 tryConnectDisplayLocked(builtInDisplayId); 655 } else { 656 tryDisconnectDisplayLocked(builtInDisplayId); 657 } 658 } 659 } 660 } 661} 662