2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.display;
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;
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;
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;
46 * A display adapter for the local displays managed by Surface Flinger.
48 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
51 final class LocalDisplayAdapter extends DisplayAdapter {
52 private static final String TAG = "LocalDisplayAdapter";
53 private static final boolean DEBUG = false;
55 private static final String UNIQUE_ID_PREFIX = "local:";
57 private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
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,
64 private final SparseArray<LocalDisplayDevice> mDevices =
65 new SparseArray<LocalDisplayDevice>();
66 @SuppressWarnings("unused") // Becomes active at instantiation time.
67 private HotplugDisplayEventReceiver mHotplugReceiver;
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);
76 public void registerLocked() {
77 super.registerLocked();
79 mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
81 for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
82 tryConnectDisplayLocked(builtInDisplayId);
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 " +
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 " +
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 " +
112 activeColorMode = Display.COLOR_MODE_INVALID;
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);
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.
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);
143 static int getPowerModeForState(int 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;
152 return SurfaceControl.POWER_MODE_NORMAL;
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<>();
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;
174 private SurfaceControl.PhysicalDisplayInfo mDisplayInfos[];
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);
190 mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken);
194 public boolean hasStableUniqueId() {
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)) {
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
224 DisplayModeRecord record = findDisplayModeRecord(info);
225 if (record == null) {
226 record = new DisplayModeRecord(info);
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;
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();
249 boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded;
250 // If the records haven't changed then we're done here.
251 if (!recordsChanged) {
254 // Update the index of modes.
255 mHavePendingChanges = true;
257 mSupportedModes.clear();
258 for (DisplayModeRecord record : records) {
259 mSupportedModes.put(record.mMode.getModeId(), record);
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.");
267 mDefaultModeId = activeRecord.mMode.getModeId();
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"
275 mActiveModeId = mDefaultModeId;
276 mActiveModeInvalid = true;
279 // Schedule traversals so that we apply pending changes.
280 sendTraversalRequestLocked();
284 private boolean updateColorModesLocked(int[] colorModes,
285 int activeColorMode) {
286 List<Integer> pendingColorModes = new ArrayList<>();
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;
294 pendingColorModes.add(colorMode);
297 boolean colorModesChanged =
298 pendingColorModes.size() != mSupportedColorModes.size()
301 // If the supported color modes haven't changed then we're done here.
302 if (!colorModesChanged) {
306 mHavePendingChanges = true;
308 mSupportedColorModes.clear();
309 mSupportedColorModes.addAll(pendingColorModes);
310 Collections.sort(mSupportedColorModes);
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;
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;
327 // This should really never happen.
328 Slog.e(TAG, "No color modes available!");
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)) {
346 public void applyPendingDisplayDeviceInfoChangesLocked() {
347 if (mHavePendingChanges) {
349 mHavePendingChanges = false;
354 public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
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;
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);
373 mInfo.hdrCapabilities = mHdrCapabilities;
374 mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos;
375 mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos;
376 mInfo.state = mState;
377 mInfo.uniqueId = getUniqueId();
379 // Assume that all built-in displays that have secure output (eg. HDCP) also
380 // support compositing from gralloc protected buffers.
382 mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
383 | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
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;
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;
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);
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;
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;
423 com.android.internal.R.bool.config_localDisplaysMirrorContent)) {
424 mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
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;
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;
445 updateDeviceInfoLocked();
448 if (brightnessChanged) {
449 mBrightness = brightness;
452 // Defer actually setting the display state until after we have exited
453 // the critical section since it can take hundreds of milliseconds
455 return new Runnable() {
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;
470 return; // old state and new state is off
474 // Apply brightness changes given that we are in a non-suspended state.
475 if (brightnessChanged) {
476 setDisplayBrightness(brightness);
479 // Enter the final desired state, possibly suspended.
480 if (state != currentState) {
481 setDisplayState(state);
485 private void setDisplayState(int state) {
487 Slog.d(TAG, "setDisplayState("
489 + ", state=" + Display.stateToString(state) + ")");
492 Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState("
494 + ", state=" + Display.stateToString(state) + ")");
496 final int mode = getPowerModeForState(state);
497 SurfaceControl.setDisplayPowerMode(token, mode);
499 Trace.traceEnd(Trace.TRACE_TAG_POWER);
503 private void setDisplayBrightness(int brightness) {
505 Slog.d(TAG, "setDisplayBrightness("
506 + "id=" + displayId + ", brightness=" + brightness + ")");
509 Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
510 + "id=" + displayId + ", brightness=" + brightness + ")");
512 mBacklight.setBrightness(brightness);
514 Trace.traceEnd(Trace.TRACE_TAG_POWER);
523 public void requestDisplayModesInTransactionLocked(
524 int colorMode, int modeId) {
525 if (requestModeInTransactionLocked(modeId) ||
526 requestColorModeInTransactionLocked(colorMode)) {
527 updateDeviceInfoLocked();
531 public boolean requestModeInTransactionLocked(int modeId) {
533 modeId = mDefaultModeId;
534 } else if (mSupportedModes.indexOfKey(modeId) < 0) {
535 Slog.w(TAG, "Requested mode " + modeId + " is not supported by this display,"
536 + " reverting to default display mode.");
537 modeId = mDefaultModeId;
540 int physIndex = findDisplayInfoIndexLocked(modeId);
542 Slog.w(TAG, "Requested mode ID " + modeId + " not available,"
543 + " trying with default mode ID");
544 modeId = mDefaultModeId;
545 physIndex = findDisplayInfoIndexLocked(modeId);
547 if (mActivePhysIndex == physIndex) {
550 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex);
551 mActivePhysIndex = physIndex;
552 mActiveModeId = modeId;
553 mActiveModeInvalid = false;
557 public boolean requestColorModeInTransactionLocked(int colorMode) {
558 if (mActiveColorMode == colorMode) {
561 if (!mSupportedColorModes.contains(colorMode)) {
562 Slog.w(TAG, "Unable to find color mode " + colorMode
563 + ", ignoring request.");
566 SurfaceControl.setActiveColorMode(getDisplayTokenLocked(), colorMode);
567 mActiveColorMode = colorMode;
568 mActiveColorModeInvalid = false;
573 public void dumpLocked(PrintWriter pw) {
574 super.dumpLocked(pw);
575 pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
576 pw.println("mActivePhysIndex=" + mActivePhysIndex);
577 pw.println("mActiveModeId=" + mActiveModeId);
578 pw.println("mActiveColorMode=" + mActiveColorMode);
579 pw.println("mState=" + Display.stateToString(mState));
580 pw.println("mBrightness=" + mBrightness);
581 pw.println("mBacklight=" + mBacklight);
582 pw.println("mDisplayInfos=");
583 for (int i = 0; i < mDisplayInfos.length; i++) {
584 pw.println(" " + mDisplayInfos[i]);
586 pw.println("mSupportedModes=");
587 for (int i = 0; i < mSupportedModes.size(); i++) {
588 pw.println(" " + mSupportedModes.valueAt(i));
590 pw.print("mSupportedColorModes=[");
591 for (int i = 0; i < mSupportedColorModes.size(); i++) {
595 pw.print(mSupportedColorModes.get(i));
600 private int findDisplayInfoIndexLocked(int modeId) {
601 DisplayModeRecord record = mSupportedModes.get(modeId);
602 if (record != null) {
603 for (int i = 0; i < mDisplayInfos.length; i++) {
604 SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[i];
605 if (record.hasMatchingMode(info)){
613 private void updateDeviceInfoLocked() {
615 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
620 * Keeps track of a display configuration.
622 private static final class DisplayModeRecord {
623 public final Display.Mode mMode;
625 public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys) {
626 mMode = createMode(phys.width, phys.height, phys.refreshRate);
630 * Returns whether the mode generated by the given PhysicalDisplayInfo matches the mode
631 * contained by the record modulo mode ID.
633 * Note that this doesn't necessarily mean the the PhysicalDisplayInfos are identical, just
634 * that they generate identical modes.
636 public boolean hasMatchingMode(SurfaceControl.PhysicalDisplayInfo info) {
637 int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate());
638 int displayInfoRefreshRate = Float.floatToIntBits(info.refreshRate);
639 return mMode.getPhysicalWidth() == info.width
640 && mMode.getPhysicalHeight() == info.height
641 && modeRefreshRate == displayInfoRefreshRate;
644 public String toString() {
645 return "DisplayModeRecord{mMode=" + mMode + "}";
649 private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
650 public HotplugDisplayEventReceiver(Looper looper) {
655 public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
656 synchronized (getSyncRoot()) {
658 tryConnectDisplayLocked(builtInDisplayId);
660 tryDisconnectDisplayLocked(builtInDisplayId);