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 android.os.Build;
21 import com.android.server.LocalServices;
22 import com.android.server.lights.Light;
23 import com.android.server.lights.LightsManager;
25 import android.content.Context;
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.util.SparseBooleanArray;
35 import android.view.Display;
36 import android.view.DisplayEventReceiver;
37 import android.view.Surface;
38 import android.view.SurfaceControl;
40 import java.io.PrintWriter;
41 import java.util.ArrayList;
42 import java.util.Arrays;
45 * A display adapter for the local displays managed by Surface Flinger.
47 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
50 final class LocalDisplayAdapter extends DisplayAdapter {
51 private static final String TAG = "LocalDisplayAdapter";
52 private static final boolean DEBUG = false;
54 private static final String UNIQUE_ID_PREFIX = "local:";
56 private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
58 private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
59 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
60 SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
63 private final SparseArray<LocalDisplayDevice> mDevices =
64 new SparseArray<LocalDisplayDevice>();
65 @SuppressWarnings("unused") // Becomes active at instantiation time.
66 private HotplugDisplayEventReceiver mHotplugReceiver;
68 // Called with SyncRoot lock held.
69 public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
70 Context context, Handler handler, Listener listener) {
71 super(syncRoot, context, handler, listener, TAG);
75 public void registerLocked() {
76 super.registerLocked();
78 mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
80 for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
81 tryConnectDisplayLocked(builtInDisplayId);
85 private void tryConnectDisplayLocked(int builtInDisplayId) {
86 IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
87 if (displayToken != null) {
88 SurfaceControl.PhysicalDisplayInfo[] configs =
89 SurfaceControl.getDisplayConfigs(displayToken);
90 if (configs == null) {
91 // There are no valid configs for this device, so we can't use it
92 Slog.w(TAG, "No valid configs found for display device " +
96 int activeConfig = SurfaceControl.getActiveConfig(displayToken);
97 if (activeConfig < 0) {
98 // There is no active config, and for now we don't have the
100 Slog.w(TAG, "No active config found for display device " +
104 LocalDisplayDevice device = mDevices.get(builtInDisplayId);
105 if (device == null) {
106 // Display was added.
107 device = new LocalDisplayDevice(displayToken, builtInDisplayId,
108 configs, activeConfig);
109 mDevices.put(builtInDisplayId, device);
110 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
111 } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig)) {
112 // Display properties changed.
113 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
116 // The display is no longer available. Ignore the attempt to add it.
117 // If it was connected but has already been disconnected, we'll get a
118 // disconnect event that will remove it from mDevices.
122 private void tryDisconnectDisplayLocked(int builtInDisplayId) {
123 LocalDisplayDevice device = mDevices.get(builtInDisplayId);
124 if (device != null) {
125 // Display was removed.
126 mDevices.remove(builtInDisplayId);
127 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
131 static int getPowerModeForState(int state) {
133 case Display.STATE_OFF:
134 return SurfaceControl.POWER_MODE_OFF;
135 case Display.STATE_DOZE:
136 return SurfaceControl.POWER_MODE_DOZE;
137 case Display.STATE_DOZE_SUSPEND:
138 return SurfaceControl.POWER_MODE_DOZE_SUSPEND;
140 return SurfaceControl.POWER_MODE_NORMAL;
144 private final class LocalDisplayDevice extends DisplayDevice {
145 private final int mBuiltInDisplayId;
146 private final Light mBacklight;
147 private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
148 private final SparseArray<Display.ColorTransform> mSupportedColorTransforms =
151 private DisplayDeviceInfo mInfo;
152 private boolean mHavePendingChanges;
153 private int mState = Display.STATE_UNKNOWN;
154 private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT;
155 private int mActivePhysIndex;
156 private int mDefaultModeId;
157 private int mActiveModeId;
158 private boolean mActiveModeInvalid;
159 private int mDefaultColorTransformId;
160 private int mActiveColorTransformId;
161 private boolean mActiveColorTransformInvalid;
163 private SurfaceControl.PhysicalDisplayInfo mDisplayInfos[];
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);
178 public boolean updatePhysicalDisplayInfoLocked(
179 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
180 mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length);
181 mActivePhysIndex = activeDisplayInfo;
182 ArrayList<Display.ColorTransform> colorTransforms = new ArrayList<>();
184 // Build an updated list of all existing color transforms.
185 boolean colorTransformsAdded = false;
186 Display.ColorTransform activeColorTransform = null;
187 for (int i = 0; i < physicalDisplayInfos.length; i++) {
188 SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i];
189 // First check to see if we've already added this color transform
190 boolean existingMode = false;
191 for (int j = 0; j < colorTransforms.size(); j++) {
192 if (colorTransforms.get(j).getColorTransform() == info.colorTransform) {
200 Display.ColorTransform colorTransform = findColorTransform(info);
201 if (colorTransform == null) {
202 colorTransform = createColorTransform(info.colorTransform);
203 colorTransformsAdded = true;
205 colorTransforms.add(colorTransform);
206 if (i == activeDisplayInfo) {
207 activeColorTransform = colorTransform;
211 // Build an updated list of all existing modes.
212 ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
213 boolean modesAdded = false;
214 for (int i = 0; i < physicalDisplayInfos.length; i++) {
215 SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i];
216 // First, check to see if we've already added a matching mode. Since not all
217 // configuration options are exposed via Display.Mode, it's possible that we have
218 // multiple PhysicalDisplayInfos that would generate the same Display.Mode.
219 boolean existingMode = false;
220 for (int j = 0; j < records.size(); j++) {
221 if (records.get(j).hasMatchingMode(info)) {
229 // If we haven't already added a mode for this configuration to the new set of
230 // supported modes then check to see if we have one in the prior set of supported
232 DisplayModeRecord record = findDisplayModeRecord(info);
233 if (record == null) {
234 record = new DisplayModeRecord(info);
240 // Get the currently active mode
241 DisplayModeRecord activeRecord = null;
242 for (int i = 0; i < records.size(); i++) {
243 DisplayModeRecord record = records.get(i);
244 if (record.hasMatchingMode(physicalDisplayInfos[activeDisplayInfo])){
245 activeRecord = record;
249 // Check whether surface flinger spontaneously changed modes out from under us. Schedule
250 // traversals to ensure that the correct state is reapplied if necessary.
251 if (mActiveModeId != 0
252 && mActiveModeId != activeRecord.mMode.getModeId()) {
253 mActiveModeInvalid = true;
254 sendTraversalRequestLocked();
256 // Check whether surface flinger spontaneously changed color transforms out from under
258 if (mActiveColorTransformId != 0
259 && mActiveColorTransformId != activeColorTransform.getId()) {
260 mActiveColorTransformInvalid = true;
261 sendTraversalRequestLocked();
264 boolean colorTransformsChanged =
265 colorTransforms.size() != mSupportedColorTransforms.size()
266 || colorTransformsAdded;
267 boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded;
268 // If neither the records nor the supported color transforms have changed then we're
270 if (!recordsChanged && !colorTransformsChanged) {
273 // Update the index of modes.
274 mHavePendingChanges = true;
276 mSupportedModes.clear();
277 for (DisplayModeRecord record : records) {
278 mSupportedModes.put(record.mMode.getModeId(), record);
280 mSupportedColorTransforms.clear();
281 for (Display.ColorTransform colorTransform : colorTransforms) {
282 mSupportedColorTransforms.put(colorTransform.getId(), colorTransform);
285 // Update the default mode and color transform if needed. This needs to be done in
286 // tandem so we always have a default state to fall back to.
287 if (findDisplayInfoIndexLocked(mDefaultColorTransformId, mDefaultModeId) < 0) {
288 if (mDefaultModeId != 0) {
289 Slog.w(TAG, "Default display mode no longer available, using currently"
290 + " active mode as default.");
292 mDefaultModeId = activeRecord.mMode.getModeId();
293 if (mDefaultColorTransformId != 0) {
294 Slog.w(TAG, "Default color transform no longer available, using currently"
295 + " active color transform as default");
297 mDefaultColorTransformId = activeColorTransform.getId();
299 // Determine whether the active mode is still there.
300 if (mSupportedModes.indexOfKey(mActiveModeId) < 0) {
301 if (mActiveModeId != 0) {
302 Slog.w(TAG, "Active display mode no longer available, reverting to default"
305 mActiveModeId = mDefaultModeId;
306 mActiveModeInvalid = true;
309 // Determine whether the active color transform is still there.
310 if (mSupportedColorTransforms.indexOfKey(mActiveColorTransformId) < 0) {
311 if (mActiveColorTransformId != 0) {
312 Slog.w(TAG, "Active color transform no longer available, reverting"
313 + " to default transform.");
315 mActiveColorTransformId = mDefaultColorTransformId;
316 mActiveColorTransformInvalid = true;
318 // Schedule traversals so that we apply pending changes.
319 sendTraversalRequestLocked();
323 private DisplayModeRecord findDisplayModeRecord(SurfaceControl.PhysicalDisplayInfo info) {
324 for (int i = 0; i < mSupportedModes.size(); i++) {
325 DisplayModeRecord record = mSupportedModes.valueAt(i);
326 if (record.hasMatchingMode(info)) {
333 private Display.ColorTransform findColorTransform(SurfaceControl.PhysicalDisplayInfo info) {
334 for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
335 Display.ColorTransform transform = mSupportedColorTransforms.valueAt(i);
336 if (transform.getColorTransform() == info.colorTransform) {
344 public void applyPendingDisplayDeviceInfoChangesLocked() {
345 if (mHavePendingChanges) {
347 mHavePendingChanges = false;
352 public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
354 SurfaceControl.PhysicalDisplayInfo phys = mDisplayInfos[mActivePhysIndex];
355 mInfo = new DisplayDeviceInfo();
356 mInfo.width = phys.width;
357 mInfo.height = phys.height;
358 mInfo.modeId = mActiveModeId;
359 mInfo.defaultModeId = mDefaultModeId;
360 mInfo.supportedModes = new Display.Mode[mSupportedModes.size()];
361 for (int i = 0; i < mSupportedModes.size(); i++) {
362 DisplayModeRecord record = mSupportedModes.valueAt(i);
363 mInfo.supportedModes[i] = record.mMode;
365 mInfo.colorTransformId = mActiveColorTransformId;
366 mInfo.defaultColorTransformId = mDefaultColorTransformId;
367 mInfo.supportedColorTransforms =
368 new Display.ColorTransform[mSupportedColorTransforms.size()];
369 for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
370 mInfo.supportedColorTransforms[i] = mSupportedColorTransforms.valueAt(i);
372 mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos;
373 mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos;
374 mInfo.state = mState;
375 mInfo.uniqueId = getUniqueId();
377 // Assume that all built-in displays that have secure output (eg. HDCP) also
378 // support compositing from gralloc protected buffers.
380 mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
381 | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
384 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
385 final Resources res = getContext().getResources();
386 mInfo.name = res.getString(
387 com.android.internal.R.string.display_manager_built_in_display_name);
388 mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
389 | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
390 if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)
391 || (Build.HARDWARE.contains("goldfish")
392 && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
393 mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
395 mInfo.type = Display.TYPE_BUILT_IN;
396 mInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
397 mInfo.xDpi = phys.xDpi;
398 mInfo.yDpi = phys.yDpi;
399 mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
401 mInfo.type = Display.TYPE_HDMI;
402 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
403 mInfo.name = getContext().getResources().getString(
404 com.android.internal.R.string.display_manager_hdmi_display_name);
405 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
406 mInfo.setAssumedDensityForExternalDisplay(phys.width, phys.height);
408 // For demonstration purposes, allow rotation of the external display.
409 // In the future we might allow the user to configure this directly.
410 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
411 mInfo.rotation = Surface.ROTATION_270;
414 // For demonstration purposes, allow rotation of the external display
415 // to follow the built-in display.
416 if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
417 mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
425 public Runnable requestDisplayStateLocked(final int state, final int brightness) {
426 // Assume that the brightness is off if the display is being turned off.
427 assert state != Display.STATE_OFF || brightness == PowerManager.BRIGHTNESS_OFF;
429 final boolean stateChanged = (mState != state);
430 final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null;
431 if (stateChanged || brightnessChanged) {
432 final int displayId = mBuiltInDisplayId;
433 final IBinder token = getDisplayTokenLocked();
434 final int oldState = mState;
438 updateDeviceInfoLocked();
441 if (brightnessChanged) {
442 mBrightness = brightness;
445 // Defer actually setting the display state until after we have exited
446 // the critical section since it can take hundreds of milliseconds
448 return new Runnable() {
451 // Exit a suspended state before making any changes.
452 int currentState = oldState;
453 if (Display.isSuspendedState(oldState)
454 || oldState == Display.STATE_UNKNOWN) {
455 if (!Display.isSuspendedState(state)) {
456 setDisplayState(state);
457 currentState = state;
458 } else if (state == Display.STATE_DOZE_SUSPEND
459 || oldState == Display.STATE_DOZE_SUSPEND) {
460 setDisplayState(Display.STATE_DOZE);
461 currentState = Display.STATE_DOZE;
463 return; // old state and new state is off
467 // Apply brightness changes given that we are in a non-suspended state.
468 if (brightnessChanged) {
469 setDisplayBrightness(brightness);
472 // Enter the final desired state, possibly suspended.
473 if (state != currentState) {
474 setDisplayState(state);
478 private void setDisplayState(int state) {
480 Slog.d(TAG, "setDisplayState("
482 + ", state=" + Display.stateToString(state) + ")");
485 Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState("
487 + ", state=" + Display.stateToString(state) + ")");
489 final int mode = getPowerModeForState(state);
490 SurfaceControl.setDisplayPowerMode(token, mode);
492 Trace.traceEnd(Trace.TRACE_TAG_POWER);
496 private void setDisplayBrightness(int brightness) {
498 Slog.d(TAG, "setDisplayBrightness("
499 + "id=" + displayId + ", brightness=" + brightness + ")");
502 Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
503 + "id=" + displayId + ", brightness=" + brightness + ")");
505 mBacklight.setBrightness(brightness);
507 Trace.traceEnd(Trace.TRACE_TAG_POWER);
516 public void requestColorTransformAndModeInTransactionLocked(
517 int colorTransformId, int modeId) {
519 modeId = mDefaultModeId;
520 } else if (mSupportedModes.indexOfKey(modeId) < 0) {
521 Slog.w(TAG, "Requested mode " + modeId + " is not supported by this display,"
522 + " reverting to default display mode.");
523 modeId = mDefaultModeId;
526 if (colorTransformId == 0) {
527 colorTransformId = mDefaultColorTransformId;
528 } else if (mSupportedColorTransforms.indexOfKey(colorTransformId) < 0) {
529 Slog.w(TAG, "Requested color transform " + colorTransformId + " is not supported"
530 + " by this display, reverting to the default color transform");
531 colorTransformId = mDefaultColorTransformId;
533 int physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
535 Slog.w(TAG, "Requested color transform, mode ID pair (" + colorTransformId + ", "
536 + modeId + ") not available, trying color transform with default mode ID");
537 modeId = mDefaultModeId;
538 physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
540 Slog.w(TAG, "Requested color transform with default mode ID still not"
541 + " available, falling back to default color transform with default"
543 colorTransformId = mDefaultColorTransformId;
544 physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
547 if (mActivePhysIndex == physIndex) {
550 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex);
551 mActivePhysIndex = physIndex;
552 mActiveModeId = modeId;
553 mActiveModeInvalid = false;
554 mActiveColorTransformId = colorTransformId;
555 mActiveColorTransformInvalid = false;
556 updateDeviceInfoLocked();
560 public void dumpLocked(PrintWriter pw) {
561 super.dumpLocked(pw);
562 pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
563 pw.println("mActivePhysIndex=" + mActivePhysIndex);
564 pw.println("mActiveModeId=" + mActiveModeId);
565 pw.println("mActiveColorTransformId=" + mActiveColorTransformId);
566 pw.println("mState=" + Display.stateToString(mState));
567 pw.println("mBrightness=" + mBrightness);
568 pw.println("mBacklight=" + mBacklight);
569 pw.println("mDisplayInfos=");
570 for (int i = 0; i < mDisplayInfos.length; i++) {
571 pw.println(" " + mDisplayInfos[i]);
573 pw.println("mSupportedModes=");
574 for (int i = 0; i < mSupportedModes.size(); i++) {
575 pw.println(" " + mSupportedModes.valueAt(i));
577 pw.println("mSupportedColorTransforms=[");
578 for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
582 pw.print(mSupportedColorTransforms.valueAt(i));
587 private int findDisplayInfoIndexLocked(int colorTransformId, int modeId) {
588 DisplayModeRecord record = mSupportedModes.get(modeId);
589 Display.ColorTransform transform = mSupportedColorTransforms.get(colorTransformId);
590 if (record != null && transform != null) {
591 for (int i = 0; i < mDisplayInfos.length; i++) {
592 SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[i];
593 if (info.colorTransform == transform.getColorTransform()
594 && record.hasMatchingMode(info)){
602 private void updateDeviceInfoLocked() {
604 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
609 * Keeps track of a display configuration.
611 private static final class DisplayModeRecord {
612 public final Display.Mode mMode;
614 public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys) {
615 mMode = createMode(phys.width, phys.height, phys.refreshRate);
619 * Returns whether the mode generated by the given PhysicalDisplayInfo matches the mode
620 * contained by the record modulo mode ID.
622 * Note that this doesn't necessarily mean the the PhysicalDisplayInfos are identical, just
623 * that they generate identical modes.
625 public boolean hasMatchingMode(SurfaceControl.PhysicalDisplayInfo info) {
626 int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate());
627 int displayInfoRefreshRate = Float.floatToIntBits(info.refreshRate);
628 return mMode.getPhysicalWidth() == info.width
629 && mMode.getPhysicalHeight() == info.height
630 && modeRefreshRate == displayInfoRefreshRate;
633 public String toString() {
634 return "DisplayModeRecord{mMode=" + mMode + "}";
638 private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
639 public HotplugDisplayEventReceiver(Looper looper) {
644 public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
645 synchronized (getSyncRoot()) {
647 tryConnectDisplayLocked(builtInDisplayId);
649 tryDisconnectDisplayLocked(builtInDisplayId);