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